From 85f172460501f0c7cc519f106cfcf942d53141a1 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Wed, 9 Aug 2023 10:07:07 +0200 Subject: [PATCH 001/101] Tests for temporal types --- pymeos/pymeos/boxes/stbox.py | 57 +- pymeos/pymeos/boxes/tbox.py | 196 +++--- pymeos/pymeos/main/tfloat.py | 110 ++-- pymeos/pymeos/main/tint.py | 69 ++- pymeos/pymeos/main/tnumber.py | 130 ++-- pymeos/pymeos/main/tpoint.py | 12 + pymeos/pymeos/temporal/temporal.py | 58 +- pymeos/tests/boxes/tbox_test.py | 363 +++++------ pymeos/tests/main/tbool_test.py | 95 ++- pymeos/tests/main/tfloat_test.py | 579 ++++++++++++------ pymeos/tests/main/tgeogpoint_test.py | 400 +++++++++++- pymeos/tests/main/tgeompoint_test.py | 228 ++++--- pymeos/tests/main/tint_test.py | 437 ++++++++----- pymeos/tests/main/ttext_test.py | 119 +++- pymeos_cffi/pymeos_cffi/__init__.py | 47 +- .../pymeos_cffi/builder/build_helpers.py | 2 - pymeos_cffi/pymeos_cffi/builder/meos.h | 52 +- pymeos_cffi/pymeos_cffi/functions.py | 166 +++-- 18 files changed, 2089 insertions(+), 1031 deletions(-) diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index 92c4ac31..e8898e77 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -63,10 +63,14 @@ def _get_box(self, other: Union[Geometry, STBox, Temporal, Time], allow_space_on # ------------------------- Constructors ---------------------------------- def __init__(self, string: Optional[str] = None, *, - xmin: Optional[Union[str, float]] = None, xmax: Optional[Union[str, float]] = None, - ymin: Optional[Union[str, float]] = None, ymax: Optional[Union[str, float]] = None, - zmin: Optional[Union[str, float]] = None, zmax: Optional[Union[str, float]] = None, - tmin: Optional[Union[str, datetime]] = None, tmax: Optional[Union[str, datetime]] = None, + xmin: Optional[Union[str, float]] = None, + xmax: Optional[Union[str, float]] = None, + ymin: Optional[Union[str, float]] = None, + ymax: Optional[Union[str, float]] = None, + zmin: Optional[Union[str, float]] = None, + zmax: Optional[Union[str, float]] = None, + tmin: Optional[Union[str, datetime]] = None, + tmax: Optional[Union[str, datetime]] = None, tmin_inc: bool = True, tmax_inc: bool = True, geodetic: bool = False, srid: Optional[int] = None, _inner=None): @@ -226,15 +230,16 @@ def from_tpoint(temporal: TPoint) -> STBox: return STBox(_inner=tpoint_to_stbox(temporal._inner)) @staticmethod - def from_expanding_bounding_box(value: Union[Geometry, TPoint, STBox], expansion: float, - geodetic: Optional[bool] = False) -> STBox: + def from_expanding_bounding_box(value: Union[Geometry, TPoint, STBox], + expansion: float, geodetic: Optional[bool] = False) -> STBox: """ Returns a `STBox` from a `Geometry`, `TPoint` or `STBox` instance, expanding its bounding box by the given amount. Args: value: A `Geometry`, `TPoint` or `STBox` instance. expansion: The amount to expand the bounding box. - geodetic: Whether to create a geodetic or geometric `STBox`. Only used when value is a `Geometry` instance. + geodetic: Whether to create a geodetic or geometric `STBox`. + Only used when value is a `Geometry` instance. Returns: A new :class:`STBox` instance. @@ -596,7 +601,8 @@ def tscale(self, duration: timedelta) -> STBox: """ return self.shift_tscale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> STBox: + def shift_tscale(self, shift: Optional[timedelta] = None, + duration: Optional[timedelta] = None) -> STBox: """ Returns a new `STBox` with the time dimension shifted by `shift` and with duration `duration`. @@ -641,16 +647,15 @@ def round(self, maxdd : int = 0) -> STBox: return STBox(_inner=new_inner) # ------------------------- Set Operations -------------------------------- - def union(self, other: STBox, strict: bool = True) -> STBox: + def union(self, other: STBox, strict: Optional[bool] = True) -> STBox: """ - Returns the smallest spatio-temporal box that contains both ``self`` and `other`. + Returns the union of `self` with `other`. Fails if the union is not contiguous. Args: - other: The other :class:`STBox` to union with ``self``. - strict: If ``True``, the union will fail if the boxes are not overlapping. + other: spatiotemporal box to merge with Returns: - A new :class:`STBox` instance. + A :class:`STBox` instance. MEOS Functions: union_stbox_stbox @@ -659,32 +664,29 @@ def union(self, other: STBox, strict: bool = True) -> STBox: def __add__(self, other): """ - Returns the non-strict union of ``self`` and `other`. + Returns the union of `self` with `other`. Fails if the union is not contiguous. Args: - other: The spatiotemporal object to union with ``self``. + other: spatiotemporal box to merge with Returns: - An :class:`STBox` with the union of ``self`` and ``other``. + A :class:`STBox` instance. MEOS Functions: union_stbox_stbox - - See Also: - :meth:`STBox.union` """ - return self.union(other, strict=False) + return self.union(other) # TODO: Check returning None for empty intersection is the desired behaviour def intersection(self, other: STBox) -> Optional[STBox]: """ - Returns the intersection of ``self`` and `other`. + Returns the intersection of `self` with `other`. Args: - other: The other :class:`STBox` to intersect with ``self``. + other: temporal object to merge with Returns: - A new :class:`STBox` instance or ``None`` if the intersection is empty. + A :class:`STBox` instance if the instersection is not empty, `None` otherwise. MEOS Functions: intersection_stbox_stbox @@ -694,19 +696,16 @@ def intersection(self, other: STBox) -> Optional[STBox]: def __mul__(self, other): """ - Returns the intersection of ``self`` and `other`. + Returns the intersection of `self` with `other`. Args: - other: The spatiotemporal object to intersect with ``self``. + other: temporal object to merge with Returns: - An :class:`STBox` with the intersection of ``self`` and ``other``. + A :class:`STBox` instance if the instersection is not empty, `None` otherwise. MEOS Functions: intersection_stbox_stbox - - See Also: - :meth:`STBox.intersection` """ return self.intersection(other) diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index 783923f6..49f9eb74 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -15,11 +15,14 @@ class TBox: ``TBox`` objects can be created with a single argument of type string as in MobilityDB. - >>> TBox('TBox XT([0, 10),[2020-06-01, 2020-06-05])') + >>> TBox('TBOXINT XT([0, 10),[2020-06-01, 2020-06-05])') + >>> TBox('TBOXFLOAT XT([0, 10),[2020-06-01, 2020-06-05])') + >>> TBox('TBOX T([2020-06-01, 2020-06-05])') - Another possibility is to provide the ``xmin``/``xmax`` (of type str or float) and ``tmin``/``tmax`` (of type str or - datetime) named parameters, and optionally indicate whether the bounds are inclusive or exclusive (by default, - lower bounds are inclusive and upper bounds are exclusive): + Another possibility is to provide the ``xmin``/``xmax`` (of type str or + int/float) and ``tmin``/``tmax`` (of type str or datetime) named parameters, + and optionally indicate whether the bounds are inclusive or exclusive (by + default, lower bounds are inclusive and upper bounds are exclusive): >>> TBox(xmin=0, xmax=10, tmin='2020-06-01', tmax='2020-06-0') >>> TBox(xmin=0, xmax=10, tmin='2020-06-01', tmax='2020-06-0', xmax_inc=True, tmax_inc=True) @@ -41,8 +44,8 @@ def _inner_span(self): # ------------------------- Constructors ---------------------------------- def __init__(self, string: Optional[str] = None, *, - xmin: Optional[Union[str, float]] = None, - xmax: Optional[Union[str, float]] = None, + xmin: Optional[Union[str, int, float]] = None, + xmax: Optional[Union[str, int, float]] = None, tmin: Optional[Union[str, datetime]] = None, tmax: Optional[Union[str, datetime]] = None, xmin_inc: Optional[bool] = True, @@ -61,10 +64,13 @@ def __init__(self, string: Optional[str] = None, *, span = None period = None if xmin is not None and xmax is not None: - span = floatspan_make(float(xmin), float(xmax), xmin_inc, xmax_inc) + if isinstance(xmin, int) and isinstance(xmax, int): + span = intspan_make(xmin, xmax, xmin_inc, xmax_inc) + else: + span = floatspan_make(float(xmin), float(xmax), xmin_inc, xmax_inc) if tmin is not None and tmax is not None: period = Period(lower=tmin, upper=tmax, lower_inc=tmin_inc, upper_inc=tmax_inc)._inner - self._inner = tbox_make(period, span) + self._inner = tbox_make(span, period) def __copy__(self) -> TBox: """ @@ -222,21 +228,6 @@ def from_tnumber(temporal: TNumber) -> TBox: return TBox(_inner=tnumber_to_tbox(temporal._inner)) # ------------------------- Output ---------------------------------------- - def to_str(self, max_decimals=15) -> str: - """ - Returns a string representation of `self` with a maximum number of decimals. - - Args: - max_decimals: The maximum number of decimals. - - Returns: - A string representation of `self`. - - MEOS Functions: - tbox_out - """ - return tbox_out(self._inner, max_decimals) - def __str__(self, max_decimals: int = 15): """ Returns a string representation of ``self``. @@ -501,14 +492,15 @@ def tscale(self, duration: timedelta) -> TBox: """ return self.shift_tscale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TBox: + def shift_tscale(self, shift: Optional[timedelta] = None, + duration: Optional[timedelta] = None) -> TBox: """ Returns a new TBox with the temporal span shifted by `shift` and duration `duration`. Examples: - >>> tbox = TBox('TBox XT([0, 10),[2020-06-01, 2020-06-05])') + >>> tbox = TBox('TBoxInt XT([0, 10),[2020-06-01, 2020-06-05])') >>> tbox.shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) - >>> 'TBOX XT([0, 10),[2020-06-03 00:00:00+02, 2020-06-07 00:00:00+02])' + >>> 'TBOXINT XT([0, 10),[2020-06-03 00:00:00+02, 2020-06-07 00:00:00+02])' Args: shift: :class:`datetime.timedelta` instance to shift the start of the temporal span @@ -550,6 +542,69 @@ def round(self, maxdd : int = 0) -> STBox: tbox_round(new_inner, maxdd) return TBox(_inner=new_inner) + # ------------------------- Set Operations -------------------------------- + def union(self, other: TBox, strict: Optional[bool] = True) -> TBox: + """ + Returns the union of `self` with `other`. Fails if the union is not contiguous. + + Args: + other: temporal object to merge with + + Returns: + A :class:`TBox` instance. + + MEOS Functions: + union_tbox_tbox + """ + return TBox(_inner=union_tbox_tbox(self._inner, other._inner, strict)) + + def __add__(self, other): + """ + Returns the union of `self` with `other`. Fails if the union is not contiguous. + + Args: + other: temporal object to merge with + + Returns: + A :class:`TBox` instance. + + MEOS Functions: + union_tbox_tbox + """ + return self.union(other) + + # TODO: Check returning None for empty intersection is the desired behaviour + def intersection(self, other: TBox) -> Optional[TBox]: + """ + Returns the intersection of `self` with `other`. + + Args: + other: temporal object to merge with + + Returns: + A :class:`TBox` instance if the instersection is not empty, `None` otherwise. + + MEOS Functions: + intersection_tbox_tbox + """ + result = intersection_tbox_tbox(self._inner, other._inner) + return TBox(_inner=result) if result else None + + def __mul__(self, other): + """ + Returns the intersection of `self` with `other`. + + Args: + other: temporal object to merge with + + Returns: + A :class:`TBox` instance if the instersection is not empty, `None` otherwise. + + MEOS Functions: + intersection_tbox_tbox + """ + return self.intersection(other) + # ------------------------- Topological Operations ------------------------ def is_adjacent(self, other: Union[int, float, intrange, floatrange, TBox, TNumber]) -> bool: """ @@ -557,11 +612,11 @@ def is_adjacent(self, other: Union[int, float, intrange, floatrange, TBox, TNumb and only one of them contains it. Examples: - >>> TBox('TBox XT([0, 1], [2012-01-01, 2012-01-02))').is_adjacent(TBox('TBox XT([0, 1], [2012-01-02, 2012-01-03])')) + >>> TBox('TBoxInt XT([0, 1], [2012-01-01, 2012-01-02))').is_adjacent(TBox('TBoxInt XT([0, 1], [2012-01-02, 2012-01-03])')) >>> True - >>> TBox('TBox XT([0, 1], [2012-01-01, 2012-01-02])').is_adjacent(TBox('TBox XT([0, 1], [2012-01-02, 2012-01-03])')) + >>> TBox('TBoxInt XT([0, 1], [2012-01-01, 2012-01-02])').is_adjacent(TBox('TBoxInt XT([0, 1], [2012-01-02, 2012-01-03])')) >>> False # Both contain bound - >>> TBox('TBox XT([0, 1), [2012-01-01, 2012-01-02))').is_adjacent(TBox('TBox XT([1, 2], [2012-01-02, 2012-01-03])') + >>> TBox('TBoxInt XT([0, 1), [2012-01-01, 2012-01-02))').is_adjacent(TBox('TBoxInt XT([1, 2], [2012-01-02, 2012-01-03])') >>> False # Adjacent in both bounds Args: @@ -594,11 +649,11 @@ def is_contained_in(self, container: Union[TBox, TNumber]) -> bool: Returns whether ``self`` is contained in ``container``. Examples: - >>> TBox('TBox XT([1, 2], [2012-01-02, 2012-01-03])').is_contained_in(TBox('TBox XT([1, 4], [2012-01-01, 2012-01-04])')) + >>> TBox('TBoxInt XT([1, 2], [2012-01-02, 2012-01-03])').is_contained_in(TBox('TBoxInt XT([1, 4], [2012-01-01, 2012-01-04])')) >>> True - >>> TBox('TBox XT((1, 2), (2012-01-01, 2012-01-02))').is_contained_in(TBox('TBox XT([1, 4], [2012-01-01, 2012-01-02])')) + >>> TBox('TBoxFloat XT((1, 2), (2012-01-01, 2012-01-02))').is_contained_in(TBox('TBoxFloat XT([1, 4], [2012-01-01, 2012-01-02])')) >>> True - >>> TBox('TBox XT([1, 2], [2012-01-01, 2012-01-02])').is_contained_in(TBox('TBox XT((1, 2), (2012-01-01, 2012-01-02))')) + >>> TBox('TBoxFloat XT([1, 2], [2012-01-01, 2012-01-02])').is_contained_in(TBox('TBoxFloat XT((1, 2), (2012-01-01, 2012-01-02))')) >>> False Args: @@ -622,11 +677,11 @@ def contains(self, content: Union[TBox, TNumber]) -> bool: Returns whether ``self`` temporally contains ``content``. Examples: - >>> TBox('TBox XT([1, 4], [2012-01-01, 2012-01-04]').contains(TBox('TBox XT([2, 3], [2012-01-02, 2012-01-03]')) + >>> TBox('TBoxInt XT([1, 4], [2012-01-01, 2012-01-04]').contains(TBox('TBoxInt XT([2, 3], [2012-01-02, 2012-01-03]')) >>> True - >>> TBox('TBox XT([1, 2], [2012-01-01, 2012-01-02]').contains(TBox('TBox XT((1, 2), (2012-01-01, 2012-01-02)')) + >>> TBox('TBoxFloat XT([1, 2], [2012-01-01, 2012-01-02]').contains(TBox('TBoxFloat XT((1, 2), (2012-01-01, 2012-01-02)')) >>> True - >>> TBox('TBox XT((1, 2), (2012-01-01, 2012-01-02)').contains(TBox('TBox XT([1, 2], [2012-01-01, 2012-01-02]')) + >>> TBox('TBoxFloat XT((1, 2), (2012-01-01, 2012-01-02)').contains(TBox('TBoxFloat XT([1, 2], [2012-01-01, 2012-01-02]')) >>> False Args: @@ -650,11 +705,11 @@ def __contains__(self, item): Returns whether ``self`` temporally contains ``item``. Examples: - >>> TBox('TBox XT([2, 3], [2012-01-02, 2012-01-03]') in TBox('TBox XT([1, 4], [2012-01-01, 2012-01-04]') + >>> TBox('TBoxInt XT([2, 3], [2012-01-02, 2012-01-03]') in TBox('TBoxInt XT([1, 4], [2012-01-01, 2012-01-04]') >>> True - >>> TBox('TBox XT((1, 2), (2012-01-01, 2012-01-02)') in TBox('TBox XT([1, 2], [2012-01-01, 2012-01-02]') + >>> TBox('TBoxFloat XT((1, 2), (2012-01-01, 2012-01-02)') in TBox('TBoxFloat XT([1, 2], [2012-01-01, 2012-01-02]') >>> True - >>> TBox('TBox XT([1, 2], [2012-01-01, 2012-01-02]') in TBox('TBox XT((1, 2), (2012-01-01, 2012-01-02)') + >>> TBox('TBoxFloat XT([1, 2], [2012-01-01, 2012-01-02]') in TBox('TBoxFloat XT((1, 2), (2012-01-01, 2012-01-02)') >>> False Args: @@ -688,7 +743,6 @@ def overlaps(self, other: Union[TBox, TNumber]) -> bool: else: raise TypeError(f'Operation not supported with type {other.__class__}') - # ------------------------- Position Operations --------------------------- def is_same(self, other: Union[TBox, TNumber]) -> bool: """ Returns whether ``self`` is the same as ``other``. @@ -710,6 +764,7 @@ def is_same(self, other: Union[TBox, TNumber]) -> bool: else: raise TypeError(f'Operation not supported with type {other.__class__}') + # ------------------------- Position Operations --------------------------- def is_left(self, other: Union[TBox, TNumber]) -> bool: """ Returns whether ``self`` is strictly to the left of ``other``. @@ -874,69 +929,6 @@ def is_over_or_after(self, other: Union[TBox, TNumber]) -> bool: else: raise TypeError(f'Operation not supported with type {other.__class__}') - # ------------------------- Set Operations -------------------------------- - def union(self, other: TBox) -> TBox: - """ - Returns the union of `self` with `other`. Fails if the union is not contiguous. - - Args: - other: temporal object to merge with - - Returns: - A :class:`TBox` instance. - - MEOS Functions: - union_tbox_tbox - """ - return TBox(_inner=union_tbox_tbox(self._inner, other._inner)) - - def __add__(self, other): - """ - Returns the union of `self` with `other`. Fails if the union is not contiguous. - - Args: - other: temporal object to merge with - - Returns: - A :class:`TBox` instance. - - MEOS Functions: - union_tbox_tbox - """ - return self.union(other) - - # TODO: Check returning None for empty intersection is the desired behaviour - def intersection(self, other: TBox) -> Optional[TBox]: - """ - Returns the intersection of `self` with `other`. - - Args: - other: temporal object to merge with - - Returns: - A :class:`TBox` instance if the instersection is not empty, `None` otherwise. - - MEOS Functions: - intersection_tbox_tbox - """ - result = intersection_tbox_tbox(self._inner, other._inner) - return TBox(_inner=result) if result else None - - def __mul__(self, other): - """ - Returns the intersection of `self` with `other`. - - Args: - other: temporal object to merge with - - Returns: - A :class:`TBox` instance if the instersection is not empty, `None` otherwise. - - MEOS Functions: - intersection_tbox_tbox - """ - return self.intersection(other) - # ------------------------- Distance Operations --------------------------- def nearest_approach_distance(self, other: Union[TBox, TNumber]) -> float: """ diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index 2d009f44..0a74e9ff 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -94,35 +94,52 @@ def __str__(self, max_decimals=15): """ return tfloat_out(self._inner, max_decimals) - def to_str(self, max_decimals=15) -> str: + def as_wkt(self, precision: int = 15) -> str: """ - Returns a string representation of `self` with a maximum number of decimals. + Returns a WKT representation of `self`. Args: - max_decimals: The maximum number of decimals. + precision: The number of decimals to use. Returns: - A string representation of `self`. + A WKT representation of `self`. MEOS Functions: tfloat_out """ - return tfloat_out(self._inner, max_decimals) + return tfloat_out(self._inner, precision) - def as_wkt(self, precision: int = 15) -> str: + # ------------------------- Conversions ---------------------------------- + def to_tint(self) -> TInt: """ - Returns a WKT representation of `self`. + Returns a new temporal integer with the values of `self` floored. + This operation can only be performed when the interpolation is stepwise or discrete. - Args: - precision: The number of decimals to use. + Returns: + A new temporal integer. + + MEOS Functions: + tfloat_to_tint + + Raises: + ValueError: If the interpolation is linear. + """ + from ..factory import _TemporalFactory + if self.interpolation() == TInterpolation.LINEAR: + raise ValueError("Cannot convert a temporal float with linear interpolation to a temporal integer") + return _TemporalFactory.create_temporal(tfloat_to_tint(self._inner)) + + def to_floatrange(self) -> floatrange: + """ + Returns value span of `self`. Returns: - A WKT representation of `self`. + An :class:`floatrange` with the value span of `self`. MEOS Functions: - tfloat_out + tnumber_to_span """ - return tfloat_out(self._inner, precision) + return floatspan_to_floatrange(tnumber_to_span(self._inner)) # ------------------------- Accessors ------------------------------------- def value_range(self) -> floatrange: @@ -147,8 +164,9 @@ def value_ranges(self) -> List[floatrange]: MEOS Functions: tfloat_spanset """ - spanset = tnumber_values(self._inner) - spans, count = spanset_spans(spanset) + spanset = tnumber_valuespans(self._inner) + spans = spanset_spans(spanset) + count = spanset_num_spans(spanset) return [floatspan_to_floatrange(spans[i]) for i in range(count)] def start_value(self) -> float: @@ -601,31 +619,37 @@ def temporal_greater(self, other: Union[int, float, Temporal]) -> Temporal: # ------------------------- Restrictions ---------------------------------- def at(self, other: Union[int, float, List[int], List[float], - intrange, floatrange, List[intrange], List[floatrange], TBox, Time]) -> TFloat: + floatrange, List[floatrange], TBox, Time]) -> TFloat: """ - Returns a new temporal float with the values of `self` restricted to the time or value `other`. + Returns a new temporal float with the values of `self` restricted to the value or time `other`. Args: - other: Time or value to restrict to. + other: Value or time to restrict to. Returns: A new temporal float. MEOS Functions: - tfloat_at_value, temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, temporal_at_periodset + tfloat_at_value, tfloat_at_values, tfloat_at_span, tfloat_at_spanset, + temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, + temporal_at_periodset """ if isinstance(other, int) or isinstance(other, float): result = tfloat_at_value(self._inner, float(other)) - elif isinstance(other, list) and (isinstance(other[0], float) or isinstance(other[0], int)): - # result = tfloat_at_values(self._inner, [float(x) for x in other]) - results = [tfloat_at_value(self._inner, float(value)) for value in other if other is not None] + elif isinstance(other, floatrange): + result = tnumber_at_span(self._inner, floatrange_to_floatspan(other)) + elif isinstance(other, list) and (isinstance(other[0], int) or isinstance(other[0], float)): + results = [tfloat_at_value(self._inner, float(other)) for value in other if other is not None] + result = temporal_merge_array(results, len(results)) + elif isinstance(other, list) and (isinstance(other[0], floatrange) or isinstance(other[0], intrange)): + results = [tnumber_at_span(self._inner, value) for value in other if other is not None] result = temporal_merge_array(results, len(results)) else: return super().at(other) return Temporal._factory(result) - def minus(self, other: Union[ - int, float, List[float], intrange, floatrange, List[intrange], List[floatrange], TBox, Time]) -> Temporal: + def minus(self, other: Union[int, float, List[int], List[float], + floatrange, List[floatrange], TBox, Time]) -> Temporal: """ Returns a new temporal float with the values of `self` restricted to the complement of the time or value `other`. @@ -637,11 +661,14 @@ def minus(self, other: Union[ A new temporal float. MEOS Functions: - tfloat_minus_value, temporal_minus_timestamp, temporal_minus_timestampset, temporal_minus_period, - temporal_minus_periodset + tfloat_minus_value, tnumber_minus_span, + temporal_minus_timestamp, temporal_minus_timestampset, + temporal_minus_period, temporal_minus_periodset """ if isinstance(other, int) or isinstance(other, float): result = tfloat_minus_value(self._inner, float(other)) + elif isinstance(other, floatrange): + result = tnumber_minus_span(self._inner, floatrange_to_floatspan(other)) elif isinstance(other, list) and isinstance(other[0], float): result = reduce(tfloat_minus_value, other, self._inner) else: @@ -676,38 +703,7 @@ def derivative(self) -> TFloat: from ..factory import _TemporalFactory return _TemporalFactory.create_temporal(tfloat_derivative(self._inner)) - # ------------------------- Conversions ---------------------------------- - def to_tint(self) -> TInt: - """ - Returns a new temporal integer with the values of `self` floored. - This operation can only be performed when the interpolation is stepwise or discrete. - - Returns: - A new temporal integer. - - MEOS Functions: - tfloat_to_tint - - Raises: - ValueError: If the interpolation is linear. - """ - from ..factory import _TemporalFactory - if self.interpolation() == TInterpolation.LINEAR: - raise ValueError("Cannot convert a temporal float with linear interpolation to a temporal integer") - return _TemporalFactory.create_temporal(tfloat_to_tint(self._inner)) - - def to_floatrange(self) -> floatrange: - """ - Returns value span of `self`. - - Returns: - An :class:`floatrange` with the value span of `self`. - - MEOS Functions: - tnumber_to_span - """ - return floatspan_to_floatrange(tnumber_to_span(self._inner)) - + # ------------------------- Transformations ---------------------------------- def to_degrees(self, normalize: bool = True) -> TFloat: """ Returns a copy of `self` converted from radians to degrees. diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index 6b027ed1..ff8a7b0c 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -105,6 +105,32 @@ def as_wkt(self): """ return tint_out(self._inner) + # ------------------------- Conversions ---------------------------------- + def to_tfloat(self) -> TFloat: + """ + Returns a new temporal float with the values of `self`. + + Returns: + A new temporal float. + + MEOS Functions: + tint_to_tfloat + """ + from ..factory import _TemporalFactory + return _TemporalFactory.create_temporal(tint_to_tfloat(self._inner)) + + def to_intrange(self) -> intrange: + """ + Returns value span of `self`. + + Returns: + An :class:`intrange` with the value span of `self`. + + MEOS Functions: + tnumber_to_span + """ + return intspan_to_intrange(tnumber_to_span(self._inner)) + # ------------------------- Accessors ------------------------------------- def value_range(self) -> intrange: """ @@ -118,6 +144,21 @@ def value_range(self) -> intrange: """ return self.to_intrange() + def value_ranges(self) -> List[intrange]: + """ + Returns the value spans of `self` taking into account gaps. + + Returns: + A list of :class:`intrange` with the value spans of `self`. + + MEOS Functions: + tint_spanset + """ + spanset = tnumber_valuespans(self._inner) + spans = spanset_spans(spanset) + count = spanset_num_spans(spanset) + return [intspan_to_intrange(spans[i]) for i in range(count)] + def start_value(self) -> int: """ Returns the start value of `self`. @@ -448,7 +489,7 @@ def never_greater_or_equal(self, value: int) -> bool: MEOS Functions: tint_always_lt """ - return (self._inner, value) + return tint_always_lt(self._inner, value) def never_greater(self, value: int) -> bool: """ @@ -658,32 +699,6 @@ def nearest_approach_distance(self, other: Union[int, float, TNumber, TBox]) -> else: return super().nearest_approach_distance(other) - # ------------------------- Conversions ---------------------------------- - def to_tfloat(self) -> TFloat: - """ - Returns a new temporal float with the values of `self`. - - Returns: - A new temporal float. - - MEOS Functions: - tint_to_tfloat - """ - from ..factory import _TemporalFactory - return _TemporalFactory.create_temporal(tint_to_tfloat(self._inner)) - - def to_intrange(self) -> intrange: - """ - Returns value span of `self`. - - Returns: - An :class:`intrange` with the value span of `self`. - - MEOS Functions: - tnumber_to_span - """ - return intspan_to_intrange(tnumber_to_span(self._inner)) - # ------------------------- Split Operations ------------------------------ def value_split(self, size: int, start: Optional[int] = 0) -> List[TInt]: """ diff --git a/pymeos/pymeos/main/tnumber.py b/pymeos/pymeos/main/tnumber.py index 87e640b6..3464f5f0 100644 --- a/pymeos/pymeos/main/tnumber.py +++ b/pymeos/pymeos/main/tnumber.py @@ -11,6 +11,7 @@ if TYPE_CHECKING: from ..boxes import TBox from ..time import Time + from .tint import TInt from .tfloat import TFloat TBase = TypeVar('TBase', int, float) @@ -36,6 +37,30 @@ def bounding_box(self) -> TBox: from ..boxes import TBox return TBox(_inner=tnumber_to_tbox(self._inner)) + def integral(self) -> float: + """ + Returns the integral of `self`. + + Returns: + The integral of `self`. + + MEOS Function: + tnumber_integral + """ + return tnumber_integral(self._inner) + + def time_weighted_average(self) -> float: + """ + Returns the time weighted average of `self`. + + Returns: + The time weighted average of `self`. + + MEOS Function: + tnumber_twavg + """ + return tnumber_twavg(self._inner) + # ------------------------- Restrictions ---------------------------------- def at(self, other: Union[intrange, floatrange, List[intrange], List[floatrange], TBox, Time]) -> TG: """ @@ -176,10 +201,12 @@ def add(self, other: Union[int, float, TNumber]) -> TNumber: Returns: A new temporal object of the same subtype as `self`. """ - if isinstance(other, int): - result = add_tnumber_int(self._inner, other) - elif isinstance(other, float): - result = add_tnumber_float(self._inner, other) + from .tint import TInt + from .tfloat import TFloat + if isinstance(self, TInt) and isinstance(other, int): + result = add_tint_int(self._inner, other) + elif isinstance(self, TFloat) and isinstance(other, (int, float)): + result = add_tfloat_float(self._inner, float(other)) elif isinstance(other, TNumber): result = add_tnumber_tnumber(self._inner, other._inner) else: @@ -199,10 +226,12 @@ def radd(self, other: Union[int, float]) -> TNumber: MEOS Functions: add_int_tint, add_float_tfloat """ - if isinstance(other, int): - result = add_int_tnumber(other, self._inner) - elif isinstance(other, float): - result = add_float_tnumber(other, self._inner) + from .tint import TInt + from .tfloat import TFloat + if isinstance(self, TInt) and isinstance(other, int): + result = add_int_tint(other, self._inner) + elif isinstance(self, TFloat) and isinstance(other, (int, float)): + result = add_float_tfloat(float(other), self._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) @@ -220,10 +249,12 @@ def sub(self, other: Union[int, float, TNumber]) -> TNumber: MEOS Functions: sub_tint_int, sub_tfloat_float, sub_tnumber_tnumber """ - if isinstance(other, int): - result = sub_tnumber_int(self._inner, other) - elif isinstance(other, float): - result = sub_tnumber_float(self._inner, other) + from .tint import TInt + from .tfloat import TFloat + if isinstance(self, TInt) and isinstance(other, int): + result = sub_tint_int(self._inner, other) + elif isinstance(self, TFloat) and isinstance(other, (int, float)): + result = sub_tfloat_float(self._inner, float(other)) elif isinstance(other, TNumber): result = sub_tnumber_tnumber(self._inner, other._inner) else: @@ -243,10 +274,12 @@ def rsub(self, other: Union[int, float]) -> TNumber: MEOS Functions: sub_int_tint, sub_float_tfloat """ - if isinstance(other, int): - result = sub_int_tnumber(other, self._inner) - elif isinstance(other, float): - result = sub_float_tnumber(other, self._inner) + from .tint import TInt + from .tfloat import TFloat + if isinstance(self, TInt) and isinstance(other, int): + result = sub_int_tint(other, self._inner) + elif isinstance(self, TFloat) and isinstance(other, (int, float)): + result = sub_float_tfloat(float(other), self._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) @@ -264,10 +297,12 @@ def mul(self, other: Union[int, float, TNumber]) -> TNumber: MEOS Functions: mult_tint_int, mult_tfloat_float, mult_tnumber_tnumber """ - if isinstance(other, int): - result = mult_tnumber_int(self._inner, other) - elif isinstance(other, float): - result = mult_tnumber_float(self._inner, other) + from .tint import TInt + from .tfloat import TFloat + if isinstance(self, TInt) and isinstance(other, int): + result = mult_tint_int(self._inner, other) + elif isinstance(self, TFloat) and isinstance(other, (int, float)): + result = mult_tfloat_float(self._inner, float(other)) elif isinstance(other, TNumber): result = mult_tnumber_tnumber(self._inner, other._inner) else: @@ -287,10 +322,12 @@ def rmul(self, other: Union[int, float]) -> TNumber: MEOS Functions: mult_int_tint, mult_float_tfloat """ - if isinstance(other, int): - result = mult_int_tnumber(other, self._inner) - elif isinstance(other, float): - result = mult_float_tnumber(other, self._inner) + from .tint import TInt + from .tfloat import TFloat + if isinstance(self, TInt) and isinstance(other, int): + result = mult_int_tint(other, self._inner) + elif isinstance(self, TFloat) and isinstance(other, (int, float)): + result = mult_float_tfloat(float(other), self._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) @@ -308,10 +345,12 @@ def div(self, other: Union[int, float, TNumber]) -> TNumber: MEOS Functions: div_tint_int, div_tfloat_float, div_tnumber_tnumber """ - if isinstance(other, int): - result = div_tnumber_int(self._inner, other) - elif isinstance(other, float): - result = div_tnumber_float(self._inner, other) + from .tint import TInt + from .tfloat import TFloat + if isinstance(self, TInt) and isinstance(other, int): + result = div_tint_int(self._inner, other) + elif isinstance(self, TFloat) and isinstance(other, (int, float)): + result = div_tfloat_float(self._inner, float(other)) elif isinstance(other, TNumber): result = div_tnumber_tnumber(self._inner, other._inner) else: @@ -331,10 +370,12 @@ def rdiv(self, other: Union[int, float]) -> TNumber: MEOS Functions: div_int_tint, div_float_tfloat """ - if isinstance(other, int): - result = div_int_tnumber(other, self._inner) - elif isinstance(other, float): - result = div_float_tnumber(other, self._inner) + from .tint import TInt + from .tfloat import TFloat + if isinstance(self, TInt) and isinstance(other, int): + result = div_int_tint(other, self._inner) + elif isinstance(self, TFloat) and isinstance(other, (int, float)): + result = div_float_tfloat(float(other), self._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) @@ -530,28 +571,3 @@ def nearest_approach_distance(self, other: Union[int, float, TNumber, TBox]) -> else: raise TypeError(f'Operation not supported with type {other.__class__}') - # ------------------------- Aggregate Operations -------------------------- - def integral(self) -> float: - """ - Returns the integral of `self`. - - Returns: - The integral of `self`. - - MEOS Function: - tnumber_integral - """ - return tnumber_integral(self._inner) - - def time_weighted_average(self) -> float: - """ - Returns the time weighted average of `self`. - - Returns: - The time weighted average of `self`. - - MEOS Function: - tnumber_twavg - """ - return tnumber_twavg(self._inner) - diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index d34bcda1..0d0084a0 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -318,6 +318,18 @@ def bearing(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint]) -> TFloa raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) + def direction(self) -> float: + """ + Returns the azimuth of the temporal point between the start and end locations. + + Returns: + A new :class:`TFloatSeqSet` indicating the direction of the temporal point. + + MEOS Functions: + tpoint_direction + """ + return tpoint_direction(self._inner) + def azimuth(self) -> TFloatSeqSet: """ Returns the temporal azimuth of the temporal point. diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index dd8fbad0..42e0b9a0 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -178,7 +178,8 @@ def as_wkt(self) -> str: """ pass - def as_mfjson(self, with_bbox: bool = True, flags: int = 3, precision: int = 6, srs: Optional[str] = None) -> str: + def as_mfjson(self, with_bbox: bool = True, flags: int = 3, + precision: int = 6, srs: Optional[str] = None) -> str: """ Returns the temporal object as a MF-JSON string. @@ -528,6 +529,58 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim ) return Temporal._factory(scaled) + def temporal_sample(self, duration: Union[str, timedelta], + start: Optional[Union[str, datetime]] = None) -> TG: + """ + Returns a new :class:`Temporal` downsampled with respect to ``duration``. + + Args: + duration: A :class:`str` or :class:`timedelta` with the duration of the temporal tiles. + start: A :class:`str` or :class:`datetime` with the start time of the temporal tiles. If None, the start + time of `self` is used. + + MEOS Functions: + temporal_tsample + """ + if start is None: + st = temporal_start_timestamp(self._inner) + elif isinstance(start, datetime): + st = datetime_to_timestamptz(start) + else: + st = pg_timestamptz_in(start, -1) + if isinstance(duration, timedelta): + dt = timedelta_to_interval(duration) + else: + pg_interval_in(duration, -1) + result = temporal_tsample(self._inner, dt, st) + return Temporal._factory(result) + + def temporal_precision(self, duration: Union[str, timedelta], + start: Optional[Union[str, datetime]] = None) -> TG: + """ + Returns a new :class:`Temporal` with precision reduced to ``duration``. + + Args: + duration: A :class:`str` or :class:`timedelta` with the duration of the temporal tiles. + start: A :class:`str` or :class:`datetime` with the start time of the temporal tiles. If None, the start + time of `self` is used. + + MEOS Functions: + temporal_tprecision + """ + if start is None: + st = temporal_start_timestamp(self._inner) + elif isinstance(start, datetime): + st = datetime_to_timestamptz(start) + else: + st = pg_timestamptz_in(start, -1) + if isinstance(duration, timedelta): + dt = timedelta_to_interval(duration) + else: + pg_interval_in(duration, -1) + result = temporal_tprecision(self._inner, dt, st) + return Temporal._factory(result) + def to_instant(self) -> TI: """ Returns `self` as a :class:`TInstant`. @@ -1099,7 +1152,8 @@ def time_split_n(self, n: int) -> List[TG]: from ..factory import _TemporalFactory return [_TemporalFactory.create_temporal(tiles[i]) for i in range(new_count)] - def stops(self, max_distance: float, min_duration: timedelta) -> TSS: + def stops(self, max_distance: Optional[float] = 0.0, + min_duration: Optional[timedelta] = timedelta()) -> TSS: """ Return the subsequences where the objects stay within an area with a given maximum size for at least the specified duration. diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index c611950e..7c12bc1e 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -39,21 +39,22 @@ def assert_tbox_equality(tbox: TBox, if tmax_inc is not None: assert tbox.tmax_inc() == tmax_inc + class TestTBoxConstructors(TestTBox): - tbx = TBox('TBOX X([1,2])') + tbfx = TBox('TBOXFLOAT X([1,2])') tbt = TBox('TBOX T([2019-09-01,2019-09-02])') - tbxt = TBox('TBOX XT([1,2],[2019-09-01,2019-09-02])') + tbfxt = TBox('TBOXFLOAT XT([1,2],[2019-09-01,2019-09-02])') @pytest.mark.parametrize( 'source, type, expected', [ - ('TBox X([1,2])', TBox, 'TBOX X([1, 2])'), - ('TBox T([2019-09-01,2019-09-02])', TBox, + ('TBOXFLOAT X([1,2])', TBox, 'TBOXFLOAT X([1, 2])'), + ('TBOX T([2019-09-01,2019-09-02])', TBox, 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - ('TBox XT([1,2],[2019-09-01,2019-09-02])', TBox, - 'TBOX XT([1, 2],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])') + ('TBOXFLOAT XT([1,2],[2019-09-01,2019-09-02])', TBox, + 'TBOXFLOAT XT([1, 2],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])') ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_string_constructor(self, source, type, expected): tb = type(source) @@ -71,10 +72,10 @@ def test_hexwkb_constructor(self): @pytest.mark.parametrize( 'value, expected', [ - (1, 'TBOX X([1, 1])'), - (1.5, 'TBOX X([1.5, 1.5])'), - (intrange(1, 2, True, True), 'TBOX X([1, 2])'), - (floatrange(1.5, 2.5, True, True), 'TBOX X([1.5, 2.5])'), + (1, 'TBOXINT X([1, 2))'), + (1.5, 'TBOXFLOAT X([1.5, 1.5])'), + (intrange(1, 2, True, True), 'TBOXINT X([1, 3))'), + (floatrange(1.5, 2.5, True, True), 'TBOXFLOAT X([1.5, 2.5])'), ], ids=['int', 'float', 'intrange', 'floatrange'] ) @@ -106,21 +107,21 @@ def test_from_value_constructor(self, value, expected): # 'value, time, expected', # [ # (1, datetime(2019, 9, 1), - # 'TBOX XT([1, 1],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + # 'TBOXINT XT([1, 2),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), # (1.5, datetime(2019, 9, 1), - # 'TBOX XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + # 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), # (intrange(1, 2, True, True), datetime(2019, 9, 1), - # 'TBOX XT([1, 2],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + # 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), # (floatrange(1.5, 2.5, True, True), datetime(2019, 9, 1), - # 'TBOX XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + # 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), # (1, Period('[2019-09-01, 2019-09-02]'), - # 'TBOX XT([1, 1],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + # 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), # (1.5, Period('[2019-09-01, 2019-09-02]'), - # 'TBOX XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + # 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), # (intrange(1, 2, True, True), Period('[2019-09-01, 2019-09-02]'), - # 'TBOX XT([1, 2],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + # 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), # (floatrange(1.5, 2.5, True, True), Period('[2019-09-01, 2019-09-02]'), - # 'TBOX XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + # 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), # ], # ids=['int-Timestamp', 'float-Timestamp', 'intrange-Timestamp', 'floatrange-Timestamp', # 'int-Period', 'float-Period', 'intrange-Period', 'floatrange-Period',] @@ -133,19 +134,20 @@ def test_from_value_constructor(self, value, expected): @pytest.mark.parametrize( 'tnumber, expected', [ - (TIntInst('1@2019-09-01'), 'TBOX XT([1, 1],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - (TFloatInst('1.5@2019-09-01'), 'TBOX XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - (TIntSeq('{1@2019-09-01,2@2019-09-02}'), 'TBOX XT([1, 2],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - (TFloatSeq('{1.5@2019-09-01,2.5@2019-09-02}'), 'TBOX XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - (TIntSeq('(1@2019-09-01,2@2019-09-02]'), 'TBOX XT([1, 2],(2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - (TFloatSeq('[1.5@2019-09-01,2.5@2019-09-02)'), 'TBOX XT([1.5, 2.5),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00))'), + (TIntInst('1@2019-09-01'), 'TBOXINT XT([1, 2),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + (TIntSeq('{1@2019-09-01,2@2019-09-02}'), 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (TIntSeq('(1@2019-09-01,2@2019-09-02]'), 'TBOXINT XT([1, 3),(2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), (TIntSeqSet('{(1@2019-09-01,2@2019-09-02],(1@2019-09-03,2@2019-09-05]}'), - 'TBOX XT([1, 2],(2019-09-01 00:00:00+00, 2019-09-05 00:00:00+00])'), + 'TBOXINT XT([1, 3),(2019-09-01 00:00:00+00, 2019-09-05 00:00:00+00])'), + + (TFloatInst('1.5@2019-09-01'), 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + (TFloatSeq('{1.5@2019-09-01,2.5@2019-09-02}'), 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (TFloatSeq('[1.5@2019-09-01,2.5@2019-09-02)'), 'TBOXFLOAT XT([1.5, 2.5),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00))'), (TFloatSeqSet('{[1.5@2019-09-01,2.5@2019-09-02),[1.5@2019-09-03,1.5@2019-09-05)}'), - 'TBOX XT([1.5, 2.5),[2019-09-01 00:00:00+00, 2019-09-05 00:00:00+00))'), + 'TBOXFLOAT XT([1.5, 2.5),[2019-09-01 00:00:00+00, 2019-09-05 00:00:00+00))'), ], - ids=['TInt Instant', 'TFloat Instant', 'TInt Discrete Sequence', 'TFloat Discrete Sequence', - 'TInt Sequence', 'TFloat Sequence', 'TInt Sequence Set', 'TFloat Sequence Set'] + ids=['TInt Instant', 'TInt Discrete Sequence', 'TInt Sequence', 'TInt Sequence Set', + 'TFloat Instant', 'TFloat Discrete Sequence', 'TFloat Sequence', 'TFloat Sequence Set'] ) def test_from_tnumber_constructor(self, tnumber, expected): tb = TBox.from_tnumber(tnumber) @@ -154,8 +156,8 @@ def test_from_tnumber_constructor(self, tnumber, expected): @pytest.mark.parametrize( 'tbox', - [tbx, tbt, tbxt], - ids=['TBox X', 'TBox T', 'TBox XT'] + [tbfx, tbt, tbfxt], + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_from_as_constructor(self, tbox): assert tbox == tbox.from_wkb(tbox.as_wkb()) @@ -163,8 +165,8 @@ def test_from_as_constructor(self, tbox): @pytest.mark.parametrize( 'tbox', - [tbx, tbt, tbxt], - ids=['TBox X', 'TBox T', 'TBox XT'] + [tbfx, tbt, tbfxt], + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_copy_constructor(self, tbox): other = copy(tbox) @@ -173,18 +175,18 @@ def test_copy_constructor(self, tbox): class TestTBoxOutputs(TestTBox): - tbx = TBox('TBOX X([1,2])') + tbfx = TBox('TBOXFLOAT X([1,2])') tbt = TBox('TBOX T([2019-09-01,2019-09-02])') - tbxt = TBox('TBOX XT([1,2],[2019-09-01,2019-09-02])') + tbfxt = TBox('TBOXFLOAT XT([1,2],[2019-09-01,2019-09-02])') @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, 'TBOX X([1, 2])'), + (tbfx, 'TBOXFLOAT X([1, 2])'), (tbt, 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - (tbxt, 'TBOX XT([1, 2],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (tbfxt, 'TBOXFLOAT XT([1, 2],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_str(self, tbox, expected): assert str(tbox) == expected @@ -192,11 +194,11 @@ def test_str(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, 'TBox(TBOX X([1, 2]))'), + (tbfx, 'TBox(TBOXFLOAT X([1, 2]))'), (tbt, 'TBox(TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00]))'), - (tbxt, 'TBox(TBOX XT([1, 2],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00]))'), + (tbfxt, 'TBox(TBOXFLOAT XT([1, 2],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00]))'), ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_repr(self, tbox, expected): assert repr(tbox) == expected @@ -204,11 +206,11 @@ def test_repr(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, '0101070003000000000000F03F0000000000000040'), + (tbfx, '0101070003000000000000F03F0000000000000040'), (tbt, '010221000300A01E4E713402000000F66B85340200'), - (tbxt, '010321000300A01E4E713402000000F66B85340200070003000000000000F03F0000000000000040'), + (tbfxt, '010321000300A01E4E713402000000F66B85340200070003000000000000F03F0000000000000040'), ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_as_hexwkb(self, tbox, expected): assert tbox.as_hexwkb() == expected @@ -216,10 +218,10 @@ def test_as_hexwkb(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, floatrange(1.0, 2.0, True, True)), - (tbxt, floatrange(1.0, 2.0, True, True)), + (tbfx, floatrange(1.0, 2.0, True, True)), + (tbfxt, floatrange(1.0, 2.0, True, True)), ], - ids=['TBox X', 'TBox XT'] + ids=['TBoxFloat X', 'TBoxFloat XT'] ) def test_to_floatrange(self, tbox, expected): tb = tbox.to_floatrange() @@ -230,9 +232,9 @@ def test_to_floatrange(self, tbox, expected): 'tbox, expected', [ (tbt, Period('[2019-09-01, 2019-09-02]')), - (tbxt, Period('[2019-09-01, 2019-09-02]')), + (tbfxt, Period('[2019-09-01, 2019-09-02]')), ], - ids=['TBox X', 'TBox XT'] + ids=['TBoxFloat X', 'TBoxFloat XT'] ) def test_to_period(self, tbox, expected): tb = tbox.to_period() @@ -241,18 +243,18 @@ def test_to_period(self, tbox, expected): class TestTBoxAccessors(TestTBox): - tbx = TBox('TBox X([1,2])') - tbt = TBox('TBox T([2019-09-01,2019-09-02])') - tbxt = TBox('TBox XT([1,2],[2019-09-01,2019-09-02])') + tbfx = TBox('TBOXFLOAT X([1,2])') + tbt = TBox('TBOX T([2019-09-01,2019-09-02])') + tbfxt = TBox('TBOXFLOAT XT([1,2],[2019-09-01,2019-09-02])') @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, True), + (tbfx, True), (tbt, False), - (tbxt, True) + (tbfxt, True) ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_has_x(self, tbox, expected): assert tbox.has_x() == expected @@ -260,11 +262,11 @@ def test_has_x(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, False), + (tbfx, False), (tbt, True), - (tbxt, True) + (tbfxt, True) ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_has_t(self, tbox, expected): assert tbox.has_t() == expected @@ -272,11 +274,11 @@ def test_has_t(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, 1), + (tbfx, 1), (tbt, None), - (tbxt, 1) + (tbfxt, 1) ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_xmin(self, tbox, expected): assert tbox.xmin() == expected @@ -284,11 +286,11 @@ def test_xmin(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, True), + (tbfx, True), (tbt, None), - (tbxt, True) + (tbfxt, True) ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_xmin_xmax_inc(self, tbox, expected): assert tbox.xmin_inc() == expected @@ -297,11 +299,11 @@ def test_xmin_xmax_inc(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, 2), + (tbfx, 2), (tbt, None), - (tbxt, 2) + (tbfxt, 2) ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_xmax(self, tbox, expected): assert tbox.xmax() == expected @@ -309,11 +311,11 @@ def test_xmax(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, None), + (tbfx, None), (tbt, datetime(year=2019, month=9, day=1, tzinfo=timezone.utc)), - (tbxt, datetime(year=2019, month=9, day=1, tzinfo=timezone.utc)) + (tbfxt, datetime(year=2019, month=9, day=1, tzinfo=timezone.utc)) ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_tmin(self, tbox, expected): assert tbox.tmin() == expected @@ -321,11 +323,11 @@ def test_tmin(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, None), + (tbfx, None), (tbt, True), - (tbxt, True) + (tbfxt, True) ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_tmin_tmax_inc(self, tbox, expected): assert tbox.tmin_inc() == expected @@ -334,27 +336,28 @@ def test_tmin_tmax_inc(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, None), + (tbfx, None), (tbt, datetime(year=2019, month=9, day=2, tzinfo=timezone.utc)), - (tbxt, datetime(year=2019, month=9, day=2, tzinfo=timezone.utc)) + (tbfxt, datetime(year=2019, month=9, day=2, tzinfo=timezone.utc)) ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_tmax(self, tbox, expected): assert tbox.tmax() == expected + class TestTBoxTransformations(TestTBox): - tbx = TBox('TBox X([1,2])') - tbt = TBox('TBox T([2019-09-01,2019-09-02])') - tbxt = TBox('TBox XT([1,2],[2019-09-01,2019-09-02])') + tbfx = TBox('TBOXFLOAT X([1,2])') + tbt = TBox('TBOX T([2019-09-01,2019-09-02])') + tbfxt = TBox('TBOXFLOAT XT([1,2],[2019-09-01,2019-09-02])') @pytest.mark.parametrize( 'tbox, expected', [ - (tbx, TBox('TBOX X([0, 3])')), - (tbxt, TBox('TBOX XT([0,3],[2019-09-01, 2019-09-02])')), + (tbfx, TBox('TBOXFLOAT X([0, 3])')), + (tbfxt, TBox('TBOXFLOAT XT([0,3],[2019-09-01, 2019-09-02])')), ], - ids=['TBox X', 'TBox XT'] + ids=['TBoxFloat X', 'TBoxFloat XT'] ) def test_expand_float(self, tbox, expected): tb = tbox.expand(1) @@ -365,18 +368,18 @@ def test_expand_float(self, tbox, expected): 'tbox, expected', [ (tbt, TBox('TBOX T([2019-08-31, 2019-09-03])')), - (tbxt, TBox('TBOX XT([1,2],[2019-08-31, 2019-09-03])')), + (tbfxt, TBox('TBOXFLOAT XT([1,2],[2019-08-31, 2019-09-03])')), ], - ids=['TBox T', 'TBox XT'] + ids=['TBox T', 'TBoxFloat XT'] ) def test_expand_time(self, tbox, expected): tb = tbox.expand(timedelta(days=1)) assert isinstance(tb, TBox) assert tb == expected - ###################################### - # THIS TEST DOES NOT WORK CORRECTLY - ###################################### + ##################################### + ## THIS TEST DOES NOT WORK CORRECTLY + ##################################### @pytest.mark.parametrize( 'tbox, delta, expected', [(tbt, timedelta(days=4), @@ -393,9 +396,9 @@ def test_expand_time(self, tbox, expected): def test_shift(self, tbox, delta, expected): assert tbox.shift(delta) == expected - ###################################### - # THIS TEST DOES NOT WORK CORRECTLY - ###################################### + ##################################### + ## THIS TEST DOES NOT WORK CORRECTLY + ##################################### @pytest.mark.parametrize( 'tbox, delta, expected', [(tbt, timedelta(days=4), @@ -408,9 +411,9 @@ def test_shift(self, tbox, delta, expected): def test_tscale(self, tbox, delta, expected): assert tbox.tscale(delta) == expected - ###################################### - # THIS TEST DOES NOT WORK CORRECTLY - ###################################### + ##################################### + ## THIS TEST DOES NOT WORK CORRECTLY + ##################################### def test_shift_tscale(self): assert self.tbt.shift_tscale(timedelta(days=4), timedelta(hours=4)) == \ TBox('TBOX T([2019-09-01,2019-09-02])') @@ -418,34 +421,34 @@ def test_shift_tscale(self): @pytest.mark.parametrize( 'tbox, expected', [ - (TBox('TBOX X([1.123456789,2.123456789])'), - TBox('TBOX X([1.12,2.12])')), - (TBox('TBOX XT([1.123456789,2.123456789],[2019-09-01, 2019-09-02])'), - TBox('TBOX XT([1.12,2.12],[2019-09-01, 2019-09-03])')), + (TBox('TBOXFLOAT X([1.123456789,2.123456789])'), + TBox('TBOXFLOAT X([1.12,2.12])')), + (TBox('TBOXFLOAT XT([1.123456789,2.123456789],[2019-09-01, 2019-09-02])'), + TBox('TBOXFLOAT XT([1.12,2.12],[2019-09-01, 2019-09-03])')), ], - ids=['TBox X', 'TBox XT'] + ids=['TBoxFloat X', 'TBoxFloat XT'] ) def test_round(self, tbox, expected): assert tbox.round(maxdd=2) class TestTBoxTopologicalFunctions(TestTBox): - tbx = TBox('TBOX X([1,2])') + tbfx = TBox('TBOXFLOAT X([1,2])') tbt = TBox('TBOX T([2019-09-01,2019-09-02])') - tbxt = TBox('TBOX XT([1,2],[2019-09-01,2019-09-02])') + tbfxt = TBox('TBOXFLOAT XT([1,2],[2019-09-01,2019-09-02])') @pytest.mark.parametrize( 'tbox, argument, expected', [ - (tbx, TBox('TBOX X([1,3])'), False), - (tbx, TBox('TBOX X((2,3])'), True), + (tbfx, TBox('TBOXFLOAT X([1,3])'), False), + (tbfx, TBox('TBOXFLOAT X((2,3])'), True), (tbt, TBox('TBOX T([2019-09-01,2019-09-03])'), False), (tbt, TBox('TBOX T((2019-09-02,2019-09-03])'), True), - (tbxt, TBox('TBOX XT([1,3],[2019-09-01,2019-09-03])'), False), - (tbxt, TBox('TBOX XT((2,3],[2019-09-02,2019-09-03])'), True), + (tbfxt, TBox('TBOXFLOAT XT([1,3],[2019-09-01,2019-09-03])'), False), + (tbfxt, TBox('TBOXFLOAT XT((2,3],[2019-09-02,2019-09-03])'), True), ], - ids=['TBox X False', 'TBox X True', 'TBox T False', 'TBox T True', - 'TBox XT False', 'TBox XT True'] + ids=['TBoxFloat X False', 'TBoxFloat X True', 'TBox T False', 'TBox T True', + 'TBoxFloat XT False', 'TBoxFloat XT True'] ) def test_is_adjacent(self, tbox, argument, expected): assert tbox.is_adjacent(argument) == expected @@ -453,15 +456,15 @@ def test_is_adjacent(self, tbox, argument, expected): @pytest.mark.parametrize( 'tbox, argument, expected', [ - (tbx, TBox('TBOX X([2,3])'), False), - (tbx, TBox('TBOX X([1,3])'), True), + (tbfx, TBox('TBOXFLOAT X([2,3])'), False), + (tbfx, TBox('TBOXFLOAT X([1,3])'), True), (tbt, TBox('TBOX T([2019-09-02,2019-09-03])'), False), (tbt, TBox('TBOX T([2019-09-01,2019-09-03])'), True), - (tbxt, TBox('TBOX XT([2,3],[2019-09-02,2019-09-03])'), False), - (tbxt, TBox('TBOX XT([1,3],[2019-09-01,2019-09-03])'), True), + (tbfxt, TBox('TBOXFLOAT XT([2,3],[2019-09-02,2019-09-03])'), False), + (tbfxt, TBox('TBOXFLOAT XT([1,3],[2019-09-01,2019-09-03])'), True), ], - ids=['TBox X False', 'TBox X True', 'TBox T False', 'TBox T True', - 'TBox XT False', 'TBox XT True'] + ids=['TBoxFloat X False', 'TBoxFloat X True', 'TBox T False', 'TBox T True', + 'TBoxFloat XT False', 'TBoxFloat XT True'] ) def test_is_contained_in_contains(self, tbox, argument, expected): assert tbox.is_contained_in(argument) == expected @@ -470,15 +473,15 @@ def test_is_contained_in_contains(self, tbox, argument, expected): @pytest.mark.parametrize( 'tbox, argument, expected', [ - (tbx, TBox('TBOX X([3,3])'), False), - (tbx, TBox('TBOX X([1,3])'), True), + (tbfx, TBox('TBOXFLOAT X([3,3])'), False), + (tbfx, TBox('TBOXFLOAT X([1,3])'), True), (tbt, TBox('TBOX T([2019-09-03,2019-09-03])'), False), (tbt, TBox('TBOX T([2019-09-01,2019-09-03])'), True), - (tbxt, TBox('TBOX XT([3,3],[2019-09-02,2019-09-03])'), False), - (tbxt, TBox('TBOX XT([1,3],[2019-09-01,2019-09-03])'), True), + (tbfxt, TBox('TBOXFLOAT XT([3,3],[2019-09-02,2019-09-03])'), False), + (tbfxt, TBox('TBOXFLOAT XT([1,3],[2019-09-01,2019-09-03])'), True), ], - ids=['TBox X False', 'TBox X True', 'TBox T False', 'TBox T True', - 'TBox XT False', 'TBox XT True'] + ids=['TBoxFloat X False', 'TBoxFloat X True', 'TBox T False', 'TBox T True', + 'TBoxFloat XT False', 'TBoxFloat XT True'] ) def test_overlaps(self, tbox, argument, expected): assert tbox.overlaps(argument) == expected @@ -486,34 +489,34 @@ def test_overlaps(self, tbox, argument, expected): @pytest.mark.parametrize( 'tbox, argument, expected', [ - (tbx, TBox('TBOX X([3,3])'), False), - (tbx, TBox('TBOX X([1,2])'), True), + (tbfx, TBox('TBOXFLOAT X([3,3])'), False), + (tbfx, TBox('TBOXFLOAT X([1,2])'), True), (tbt, TBox('TBOX T([2019-09-03,2019-09-03])'), False), (tbt, TBox('TBOX T([2019-09-01,2019-09-02])'), True), - (tbxt, TBox('TBOX X([3,3])'), False), - (tbxt, TBox('TBOX X([1,2])'), True), + (tbfxt, TBox('TBOXFLOAT X([3,3])'), False), + (tbfxt, TBox('TBOXFLOAT X([1,2])'), True), ], - ids=['TBox X False', 'TBox X True', 'TBox T False', 'TBox T True', - 'TBox XT False', 'TBox XT True'] + ids=['TBoxFloat X False', 'TBoxFloat X True', 'TBox T False', 'TBox T True', + 'TBoxFloat XT False', 'TBoxFloat XT True'] ) def test_is_same(self, tbox, argument, expected): assert tbox.is_same(argument) == expected class TestTBoxPositionFunctions(TestTBox): - tbx = TBox('TBOX X([1,2])') + tbfx = TBox('TBOXFLOAT X([1,2])') tbt = TBox('TBOX T([2019-09-01,2019-09-02])') - tbxt = TBox('TBOX XT([1,2],[2019-09-01,2019-09-02])') + tbfxt = TBox('TBOXFLOAT XT([1,2],[2019-09-01,2019-09-02])') @pytest.mark.parametrize( 'tbox, argument, expected', [ - (tbx, TBox('TBOX X([1,3])'), False), - (tbx, TBox('TBOX X([3,4])'), True), - (tbxt, TBox('TBOX XT([1,3],[2019-09-01,2019-09-03])'), False), - (tbxt, TBox('TBOX XT([3,4],[2019-09-02,2019-09-03])'), True), + (tbfx, TBox('TBOXFLOAT X([1,3])'), False), + (tbfx, TBox('TBOXFLOAT X([3,4])'), True), + (tbfxt, TBox('TBOXFLOAT XT([1,3],[2019-09-01,2019-09-03])'), False), + (tbfxt, TBox('TBOXFLOAT XT([3,4],[2019-09-02,2019-09-03])'), True), ], - ids=['TBox X False', 'TBox X True', 'TBox XT False', 'TBox XT True'] + ids=['TBoxFloat X False', 'TBoxFloat X True', 'TBoxFloat XT False', 'TBoxFloat XT True'] ) def test_is_left_right(self, tbox, argument, expected): assert tbox.is_left(argument) == expected @@ -522,12 +525,12 @@ def test_is_left_right(self, tbox, argument, expected): @pytest.mark.parametrize( 'tbox, argument, expected', [ - (tbx, TBox('TBOX X([1,1])'), False), - (tbx, TBox('TBOX X([3,4])'), True), - (tbxt, TBox('TBOX XT([1,1],[2019-09-01,2019-09-03])'), False), - (tbxt, TBox('TBOX XT([3,4],[2019-09-02,2019-09-03])'), True), + (tbfx, TBox('TBOXFLOAT X([1,1])'), False), + (tbfx, TBox('TBOXFLOAT X([3,4])'), True), + (tbfxt, TBox('TBOXFLOAT XT([1,1],[2019-09-01,2019-09-03])'), False), + (tbfxt, TBox('TBOXFLOAT XT([3,4],[2019-09-02,2019-09-03])'), True), ], - ids=['TBox X False', 'TBox X True', 'TBox XT False', 'TBox XT True'] + ids=['TBoxFloat X False', 'TBoxFloat X True', 'TBoxFloat XT False', 'TBoxFloat XT True'] ) def test_is_over_or_left(self, tbox, argument, expected): assert tbox.is_over_or_left(argument) == expected @@ -535,12 +538,12 @@ def test_is_over_or_left(self, tbox, argument, expected): @pytest.mark.parametrize( 'tbox, argument, expected', [ - (tbx, TBox('TBOX X([0,1])'), False), - (tbx, TBox('TBOX X([3,4])'), True), - (tbxt, TBox('TBOX XT([0,1],[2019-09-01,2019-09-03])'), False), - (tbxt, TBox('TBOX XT([3,4],[2019-09-02,2019-09-03])'), True), + (tbfx, TBox('TBOXFLOAT X([0,1])'), False), + (tbfx, TBox('TBOXFLOAT X([3,4])'), True), + (tbfxt, TBox('TBOXFLOAT XT([0,1],[2019-09-01,2019-09-03])'), False), + (tbfxt, TBox('TBOXFLOAT XT([3,4],[2019-09-02,2019-09-03])'), True), ], - ids=['TBox X False', 'TBox X True', 'TBox XT False', 'TBox XT True'] + ids=['TBoxFloat X False', 'TBoxFloat X True', 'TBoxFloat XT False', 'TBoxFloat XT True'] ) def test_is_over_or_right(self, tbox, argument, expected): assert argument.is_over_or_right(tbox) == expected @@ -550,10 +553,10 @@ def test_is_over_or_right(self, tbox, argument, expected): [ (tbt, TBox('TBOX T([2019-09-01,2019-09-03])'), False), (tbt, TBox('TBOX T([2019-09-03,2019-09-03])'), True), - (tbxt, TBox('TBOX XT([1,3],[2019-09-01,2019-09-03])'), False), - (tbxt, TBox('TBOX XT([3,4],[2019-09-03,2019-09-03])'), True), + (tbfxt, TBox('TBOXFLOAT XT([1,3],[2019-09-01,2019-09-03])'), False), + (tbfxt, TBox('TBOXFLOAT XT([3,4],[2019-09-03,2019-09-03])'), True), ], - ids=['TBox T False', 'TBox T True', 'TBox XT False', 'TBox XT True'] + ids=['TBox T False', 'TBox T True', 'TBoxFloat XT False', 'TBoxFloat XT True'] ) def test_is_before_after(self, tbox, argument, expected): assert tbox.is_before(argument) == expected @@ -564,10 +567,10 @@ def test_is_before_after(self, tbox, argument, expected): [ (tbt, TBox('TBOX T([2019-09-01,2019-09-01])'), False), (tbt, TBox('TBOX T([2019-09-03,2019-09-03])'), True), - (tbxt, TBox('TBOX XT([1,3],[2019-09-01,2019-09-01])'), False), - (tbxt, TBox('TBOX XT([3,4],[2019-09-03,2019-09-03])'), True), + (tbfxt, TBox('TBOXFLOAT XT([1,3],[2019-09-01,2019-09-01])'), False), + (tbfxt, TBox('TBOXFLOAT XT([3,4],[2019-09-03,2019-09-03])'), True), ], - ids=['TBox T False', 'TBox T True', 'TBox XT False', 'TBox XT True'] + ids=['TBox T False', 'TBox T True', 'TBoxFloat XT False', 'TBoxFloat XT True'] ) def test_is_over_or_before(self, tbox, argument, expected): assert tbox.is_over_or_before(argument) == expected @@ -577,31 +580,31 @@ def test_is_over_or_before(self, tbox, argument, expected): [ (tbt, TBox('TBOX T([2019-09-03,2019-09-03])'), False), (tbt, TBox('TBOX T([2019-09-01,2019-09-03])'), True), - (tbxt, TBox('TBOX XT([1,3],[2019-09-02,2019-09-03])'), False), - (tbxt, TBox('TBOX XT([3,4],[2019-09-01,2019-09-03])'), True), + (tbfxt, TBox('TBOXFLOAT XT([1,3],[2019-09-02,2019-09-03])'), False), + (tbfxt, TBox('TBOXFLOAT XT([3,4],[2019-09-01,2019-09-03])'), True), ], - ids=['TBox T False', 'TBox T True', 'TBox XT False', 'TBox XT True'] + ids=['TBox T False', 'TBox T True', 'TBoxFloat XT False', 'TBoxFloat XT True'] ) def test_is_over_or_after(self, tbox, argument, expected): assert tbox.is_over_or_after(argument) == expected class TestTBoxSetFunctions(TestTBox): - tbx1 = TBox('TBox X([1,2])') - tbt1 = TBox('TBox T([2019-09-01,2019-09-02])') - tbxt1 = TBox('TBox XT([1,2],[2019-09-01,2019-09-02])') - tbx2 = TBox('TBox X([2,3])') - tbt2 = TBox('TBox T([2019-09-02,2019-09-03])') - tbxt2 = TBox('TBox XT([2,3],[2019-09-02,2019-09-03])') + tbx1 = TBox('TBOXFLOAT X([1,2])') + tbt1 = TBox('TBOX T([2019-09-01,2019-09-02])') + tbxt1 = TBox('TBOXFLOAT XT([1,2],[2019-09-01,2019-09-02])') + tbx2 = TBox('TBOXFLOAT X([2,3])') + tbt2 = TBox('TBOX T([2019-09-02,2019-09-03])') + tbxt2 = TBox('TBOXFLOAT XT([2,3],[2019-09-02,2019-09-03])') @pytest.mark.parametrize( 'tbox1, tbox2, expected', [ - (tbx1, tbx2, TBox('TBox X([1,3])')), - (tbt1, tbt2, TBox('TBox T([2019-09-01,2019-09-03])')), - (tbxt1, tbxt2, TBox('TBox XT([1,3],[2019-09-01,2019-09-03])')) + (tbx1, tbx2, TBox('TBOXFLOAT X([1,3])')), + (tbt1, tbt2, TBox('TBOXFLOAT T([2019-09-01,2019-09-03])')), + (tbxt1, tbxt2, TBox('TBOXFLOAT XT([1,3],[2019-09-01,2019-09-03])')) ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_union(self, tbox1, tbox2, expected): assert tbox1.union(tbox2) == expected @@ -610,11 +613,11 @@ def test_union(self, tbox1, tbox2, expected): @pytest.mark.parametrize( 'tbox1, tbox2, expected', [ - (tbx1, tbx2, TBox('TBox X([2,2])')), - (tbt1, tbt2, TBox('TBox T([2019-09-02,2019-09-02])')), - (tbxt1, tbxt2, TBox('TBox XT([2,2],[2019-09-02,2019-09-02])')) + (tbx1, tbx2, TBox('TBOXFLOAT X([2,2])')), + (tbt1, tbt2, TBox('TBOX T([2019-09-02,2019-09-02])')), + (tbxt1, tbxt2, TBox('TBOXFLOAT XT([2,2],[2019-09-02,2019-09-02])')) ], - ids=['TBox X', 'TBox T', 'TBox XT'] + ids=['TBoxFloat X', 'TBpx T', 'TBoxFloat XT'] ) def test_intersection(self, tbox1, tbox2, expected): assert tbox1.intersection(tbox2) == expected @@ -622,43 +625,43 @@ def test_intersection(self, tbox1, tbox2, expected): class TestTBoxDistanceFunctions(TestTBox): - tbx = TBox('TBOX X([1,2])') + tbfx = TBox('TBOXFLOAT X([1,2])') tbt = TBox('TBOX T([2019-09-01,2019-09-02])') - tbxt = TBox('TBOX XT([1,2],[2019-09-01,2019-09-02])') + tbfxt = TBox('TBOXFLOAT XT([1,2],[2019-09-01,2019-09-02])') @pytest.mark.parametrize( 'tbox, argument, expected', [ - (tbx, TBox('TBOX X([1,3])'), 0), - (tbx, TBox('TBOX X([3,4])'), 1), - (tbxt, TBox('TBOX XT([1,3],[2019-09-01,2019-09-03])'), 0), - (tbxt, TBox('TBOX XT([3,4],[2019-09-01,2019-09-03])'), 1), + (tbfx, TBox('TBOXFLOAT X([1,3])'), 0), + (tbfx, TBox('TBOXFLOAT X([3,4])'), 1), + (tbfxt, TBox('TBOXFLOAT XT([1,3],[2019-09-01,2019-09-03])'), 0), + (tbfxt, TBox('TBOXFLOAT XT([3,4],[2019-09-01,2019-09-03])'), 1), ], - ids=['TBox X Intersection', 'TBox X Distance', - 'TBox XT Intersection', 'TBox XT Distance'] + ids=['TBoxFloat X Intersection', 'TBoxFloat X Distance', + 'TBoxFloat XT Intersection', 'TBoxFloat XT Distance'] ) def test_nearest_approach_distance(self, tbox, argument, expected): assert tbox.nearest_approach_distance(argument) == expected class TestTBoxComparisons(TestTBox): - tbxt = TBox('TBOX XT([1,2],[2019-09-01,2019-09-02])') - other = TBox('TBOX XT([3,4],[2019-09-03,2019-09-04])') + tbfxt = TBox('TBOXFLOAT XT([1,2],[2019-09-01,2019-09-02])') + other = TBox('TBOXFLOAT XT([3,4],[2019-09-03,2019-09-04])') def test_eq(self): - _ = self.tbxt == self.other + _ = self.tbfxt == self.other def test_ne(self): - _ = self.tbxt != self.other + _ = self.tbfxt != self.other def test_lt(self): - _ = self.tbxt < self.other + _ = self.tbfxt < self.other def test_le(self): - _ = self.tbxt <= self.other + _ = self.tbfxt <= self.other def test_gt(self): - _ = self.tbxt > self.other + _ = self.tbfxt > self.other def test_ge(self): - _ = self.tbxt >= self.other + _ = self.tbfxt >= self.other diff --git a/pymeos/tests/main/tbool_test.py b/pymeos/tests/main/tbool_test.py index cb01946a..dbff67da 100644 --- a/pymeos/tests/main/tbool_test.py +++ b/pymeos/tests/main/tbool_test.py @@ -330,6 +330,19 @@ class TestTBoolAccessors(TestTBool): tbs = TBoolSeq('[True@2019-09-01, False@2019-09-02]') tbss = TBoolSeqSet('{[True@2019-09-01, False@2019-09-02],[True@2019-09-03, True@2019-09-05]}') + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tbi, Period('[2019-09-01, 2019-09-01]')), + (tbds, Period('[2019-09-01, 2019-09-02]')), + (tbs, Period('[2019-09-01, 2019-09-02]')), + (tbss, Period('[2019-09-01, 2019-09-05]')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_bounding_box(self, temporal, expected): + assert temporal.bounding_box() == expected + @pytest.mark.parametrize( 'temporal, expected', [ @@ -542,27 +555,27 @@ def test_end_instant(self, temporal, expected): 'temporal, expected', [ (tbi, tbi), - (tbds, tbi), - (tbs, tbi), - (tbss, tbi), + (tbds, TBoolInst('False@2019-09-02')), + (tbs, TBoolInst('False@2019-09-02')), + (tbss, TBoolInst('False@2019-09-02')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_max_instant(self, temporal, expected): - assert temporal.max_instant() == expected + def test_min_instant(self, temporal, expected): + assert temporal.min_instant() == expected @pytest.mark.parametrize( 'temporal, expected', [ (tbi, tbi), - (tbds, TBoolInst('False@2019-09-02')), - (tbs, TBoolInst('False@2019-09-02')), - (tbss, TBoolInst('False@2019-09-02')), + (tbds, tbi), + (tbs, tbi), + (tbss, tbi), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_min_instant(self, temporal, expected): - assert temporal.min_instant() == expected + def test_max_instant(self, temporal, expected): + assert temporal.max_instant() == expected @pytest.mark.parametrize( 'temporal, n, expected', @@ -676,18 +689,6 @@ def test_timestamps(self, temporal, expected): def test_segments(self, temporal, expected): assert temporal.segments() == expected - @pytest.mark.parametrize( - 'temporal, expected', - [ - (tbds, True), - (tbs, True), - ], - ids=['Discrete Sequence', 'Sequence'] - ) - def test_lower_upper_inc(self, temporal, expected): - assert temporal.lower_inc() == expected - assert temporal.upper_inc() == expected - @pytest.mark.parametrize( 'temporal, expected', [ @@ -701,6 +702,33 @@ def test_lower_upper_inc(self, temporal, expected): def test_hash(self, temporal, expected): assert hash(temporal) == expected + def test_value_timestamp(self): + assert self.tbi.value() == True + assert self.tbi.timestamp() == datetime(year=2019, month=9, day=1, tzinfo=timezone.utc) + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tbds, True), + (tbs, True), + ], + ids=['Discrete Sequence', 'Sequence'] + ) + def test_lower_upper_inc(self, temporal, expected): + assert temporal.lower_inc() == expected + assert temporal.upper_inc() == expected + + def test_sequenceset_sequence_functions(self): + tbss1 =TBoolSeqSet('{[True@2019-09-01, False@2019-09-02],' + '[True@2019-09-03, True@2019-09-05], [True@2019-09-06]}') + assert tbss1.num_sequences() == 3 + assert tbss1.start_sequence() == TBoolSeq('[True@2019-09-01, False@2019-09-02]') + assert tbss1.end_sequence() == TBoolSeq('[True@2019-09-06]') + assert tbss1.sequence_n(1) == TBoolSeq('[True@2019-09-03, True@2019-09-05]') + assert tbss1.sequences() == [TBoolSeq('[True@2019-09-01, False@2019-09-02]'), + TBoolSeq('[True@2019-09-03, True@2019-09-05]'), + TBoolSeq('[True@2019-09-06]')] + class TestTBoolTransformations(TestTBool): tbi = TBoolInst('True@2019-09-01') @@ -825,6 +853,29 @@ def test_shift_tscale(self): TBoolSeqSet('{[True@2019-09-05 00:00:00, False@2019-09-05 00:30:00],' '[True@2019-09-05 01:00:00, True@2019-09-05 02:00:00]}') + @pytest.mark.parametrize( + 'tint, delta, expected', + [(tbi, timedelta(days=4), TBoolInst('True@2019-09-01')), + (tbi, timedelta(hours=12), TBoolInst('True@2019-09-01')), + (tbds, timedelta(days=4), TBoolSeq('{True@2019-09-01}')), + (tbds, timedelta(hours=12), TBoolSeq('{True@2019-09-01, False@2019-09-02}')), + (tbs, timedelta(days=4), TBoolSeq('{True@2019-09-01}')), + (tbs, timedelta(hours=12), TBoolSeq('{True@2019-09-01, True@2019-09-01 12:00:00, False@2019-09-02}')), + (tbss, timedelta(days=4), + TBoolSeq('{True@2019-09-01,True@2019-09-05}')), + (tbss, timedelta(hours=12), + TBoolSeq('{True@2019-09-01, True@2019-09-01 12:00:00, False@2019-09-02,' + 'True@2019-09-03, True@2019-09-03 12:00:00, True@2019-09-04, ' + 'True@2019-09-04 12:00:00, True@2019-09-05}')), + ], + ids=['Instant days', 'Instant hours', + 'Discrete Sequence days', 'Discrete Sequence hours', + 'Sequence days', 'Sequence hours', + 'Sequence Set days', 'Sequence Set hours'] + ) + def test_temporal_sample(self, tint, delta, expected): + assert tint.temporal_sample(delta) == expected + class TestTBoolModifications(TestTBool): tbi = TBoolInst('True@2019-09-01') diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index 258f3653..b1417bb3 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -30,24 +30,8 @@ class TestTFloatConstructors(TestTFloat): [ (TIntInst('1@2019-09-01'), TFloatInst, TInterpolation.NONE), (TIntSeq('{1@2019-09-01, 2@2019-09-02}'), TFloatSeq, TInterpolation.DISCRETE), - (TIntSeq('[1@2019-09-01, 2@2019-09-02]'), TFloatSeq, TInterpolation.STEPWISE), + (TIntSeq('[1@2019-09-01, 2@2019-09-02]'), TFloatSeq, TInterpolation.LINEAR), (TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}'), - TFloatSeqSet, TInterpolation.STEPWISE) - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - ) - def test_from_base_constructor(self, source, type, interpolation): - tf = TFloat.from_base_temporal(1.5, source) - assert isinstance(tf, type) - assert tf.interpolation() == interpolation - - @pytest.mark.parametrize( - 'source, type, interpolation', - [ - (TFloatInst('1@2019-09-01'), TFloatInst, TInterpolation.NONE), - (TFloatSeq('{1@2019-09-01, 2@2019-09-02}'), TFloatSeq, TInterpolation.DISCRETE), - (TFloatSeq('[1@2019-09-01, 2@2019-09-02]'), TFloatSeq, TInterpolation.LINEAR), - (TFloatSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}'), TFloatSeqSet, TInterpolation.LINEAR) ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] @@ -369,6 +353,30 @@ def test_as_mfjson(self, temporal, expected): assert temporal.as_mfjson() == expected +class TestTFloatConversions(TestTFloat): + tfi = TFloatInst('1.5@2019-09-01') + tfds = TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}') + tfs = TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]') + tfss = TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}') + tfsts = TFloatSeq('Interp=Step;[1.5@2019-09-01, 2.5@2019-09-02]') + tfstss = TFloatSeqSet('Interp=Step;{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}') + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tfi, TIntInst('1@2019-09-01')), + (tfds, TIntSeq('{1@2019-09-01,2@2019-09-02}')), + (tfsts, TIntSeq('[1@2019-09-01,2@2019-09-02]')), + (tfstss, TIntSeq('{[1@2019-09-01,2@2019-09-02],[1@2019-09-03,1@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Step Sequence', 'Step Sequence Set'] + ) + def test_to_tint(self, temporal, expected): + temp = temporal.to_tint() + assert isinstance(temp, TInt) + assert temp == expected + + class TestTFloatAccessors(TestTFloat): tfi = TFloatInst('1.5@2019-09-01') tfds = TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}') @@ -377,6 +385,19 @@ class TestTFloatAccessors(TestTFloat): tfsts = TFloatSeq('Interp=Step;[1.5@2019-09-01, 2.5@2019-09-02]') tfstss = TFloatSeqSet('Interp=Step;{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}') + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tfi, TBox('TBOXFLOAT XT([1.5,1.5],[2019-09-01, 2019-09-01])')), + (tfds, TBox('TBOXFLOAT XT([1.5,2.5],[2019-09-01, 2019-09-02])')), + (tfs, TBox('TBOXFLOAT XT([1.5,2.5],[2019-09-01, 2019-09-02])')), + (tfss, TBox('TBOXFLOAT XT([1.5,2.5],[2019-09-01, 2019-09-05])')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_bounding_box(self, temporal, expected): + assert temporal.bounding_box() == expected + @pytest.mark.parametrize( 'temporal, expected', [ @@ -416,6 +437,32 @@ def test_value_set(self, temporal, expected): def test_values(self, temporal, expected): assert temporal.values() == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tfi, floatrange(1.5, 1.5, True, True)), + (tfds, floatrange(1.5, 2.5, True, True)), + (tfs, floatrange(1.5, 2.5, True, True)), + (tfss, floatrange(1.5, 2.5, True, True)), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_value_range(self, temporal, expected): + assert temporal.value_range() == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tfi, [floatrange(1.5, 1.5, True, True)]), + (tfds, [floatrange(1.5, 1.5, True, True),floatrange(2.5, 2.5, True, True)]), + (tfs, [floatrange(1.5, 2.5, True, True)]), + (tfss, [floatrange(1.5, 2.5, True, True)]), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_value_ranges(self, temporal, expected): + assert temporal.value_ranges() == expected + @pytest.mark.parametrize( 'temporal, expected', [ @@ -589,27 +636,27 @@ def test_end_instant(self, temporal, expected): 'temporal, expected', [ (tfi, tfi), - (tfds, TFloatInst('2.5@2019-09-02')), - (tfs, TFloatInst('2.5@2019-09-02')), - (tfss, TFloatInst('2.5@2019-09-02')), + (tfds, tfi), + (tfs, tfi), + (tfss, tfi), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_max_instant(self, temporal, expected): - assert temporal.max_instant() == expected + def test_min_instant(self, temporal, expected): + assert temporal.min_instant() == expected @pytest.mark.parametrize( 'temporal, expected', [ (tfi, tfi), - (tfds, tfi), - (tfs, tfi), - (tfss, tfi), + (tfds, TFloatInst('2.5@2019-09-02')), + (tfs, TFloatInst('2.5@2019-09-02')), + (tfss, TFloatInst('2.5@2019-09-02')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_min_instant(self, temporal, expected): - assert temporal.min_instant() == expected + def test_max_instant(self, temporal, expected): + assert temporal.max_instant() == expected @pytest.mark.parametrize( 'temporal, n, expected', @@ -725,6 +772,26 @@ def test_timestamps(self, temporal, expected): def test_segments(self, temporal, expected): assert temporal.segments() == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tfi, 1307112078), + (tfds, 1935376725), + (tfs, 1935376725), + (tfss, 4247071962), + (tfs, 1935376725), + (tfss, 4247071962) + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet', + 'Stepwise Sequence', 'Stepwise SequenceSet'] + ) + def test_hash(self, temporal, expected): + assert hash(temporal) == expected + + def test_value_timestamp(self): + assert self.tfi.value() == 1.5 + assert self.tfi.timestamp() == datetime(year=2019, month=9, day=1, tzinfo=timezone.utc) + @pytest.mark.parametrize( 'temporal, expected', [ @@ -732,40 +799,48 @@ def test_segments(self, temporal, expected): (tfs, True), (tfsts, True), ], - ids=['Discrete Sequence', 'Sequence', 'Stepwise Sequence'] + ids=['Discrete Sequence', 'Sequence', 'Step Sequence'] ) def test_lower_upper_inc(self, temporal, expected): assert temporal.lower_inc() == expected assert temporal.upper_inc() == expected + def test_sequenceset_sequence_functions(self): + tfss1 =TFloatSeqSet('{[1@2019-09-01, 2@2019-09-02],' + '[1@2019-09-03, 1@2019-09-05], [3@2019-09-06]}') + assert tfss1.num_sequences() == 3 + assert tfss1.start_sequence() == TFloatSeq('[1@2019-09-01, 2@2019-09-02]') + assert tfss1.end_sequence() == TFloatSeq('[3@2019-09-06]') + assert tfss1.sequence_n(1) == TFloatSeq('[1@2019-09-03, 1@2019-09-05]') + assert tfss1.sequences() == [TFloatSeq('[1@2019-09-01, 2@2019-09-02]'), + TFloatSeq('[1@2019-09-03, 1@2019-09-05]'), + TFloatSeq('[3@2019-09-06]')] + @pytest.mark.parametrize( 'temporal, expected', [ - (tfi, 1307112078), - (tfds, 1935376725), - (tfs, 1935376725), - (tfss, 4247071962), - (tfs, 1935376725), - (tfss, 4247071962) + (tfi, 0), + (tfds, 0), + (tfs, 172800000000), + (tfss, 432000000000), ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet', - 'Stepwise Sequence', 'Stepwise SequenceSet'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_hash(self, temporal, expected): - assert hash(temporal) == expected + def test_integral(self, temporal, expected): + assert temporal.integral() == expected @pytest.mark.parametrize( 'temporal, expected', [ - (tfi, TBox('TBOX XT([1.5,1.5],[2019-09-01, 2019-09-01])')), - (tfds, TBox('TBOX XT([1.5,2.5],[2019-09-01, 2019-09-02])')), - (tfs, TBox('TBOX XT([1.5,2.5],[2019-09-01, 2019-09-02])')), - (tfss, TBox('TBOX XT([1.5,2.5],[2019-09-01, 2019-09-05])')), + (tfi, 1.5), + (tfds, 2), + (tfs, 2), + (tfss, 1.6667), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_bounding_box(self, temporal, expected): - assert temporal.bounding_box() == expected + def test_time_weighted_average(self, temporal, expected): + assert round(temporal.time_weighted_average(), 4) == expected class TestTFloatTransformations(TestTFloat): @@ -776,6 +851,15 @@ class TestTFloatTransformations(TestTFloat): tfss = TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}') tfstss = TFloatSeqSet('Interp=Step;{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}') + tfs_d = TFloatSeq('[1.5@2019-09-01]') + tfss_d = TFloatSeqSet('{[1.5@2019-09-01],[2.5@2019-09-03]}') + tfs_s = TFloatSeq('[1.5@2019-09-01, 1.5@2019-09-02]') + tfss_s = TFloatSeqSet('{[1.5@2019-09-01, 1.5@2019-09-02],' + '[2.5@2019-09-03, 2.5@2019-09-05]}') + tfs_l = TFloatSeq('Interp=Step;[1.5@2019-09-01, 2.5@2019-09-02]') + tfss_l = TFloatSeqSet('Interp=Step;{[1.5@2019-09-01, 2.5@2019-09-02],' + '[1.5@2019-09-03, 1.5@2019-09-05]}') + @pytest.mark.parametrize( 'temporal, expected', [ @@ -829,6 +913,44 @@ def test_to_sequenceset(self, temporal, expected): assert isinstance(temp, TFloatSeqSet) assert temp == expected + @pytest.mark.parametrize( + 'temporal, interpolation, expected', + [ + (tfi, TInterpolation.DISCRETE, + TFloatSeq('{1.5@2019-09-01}')), + (tfds, TInterpolation.DISCRETE, tfds), + (tfs_d, TInterpolation.DISCRETE, + TFloatSeq('{1.5@2019-09-01}')), + (tfss_d, TInterpolation.DISCRETE, + TFloatSeq('{1.5@2019-09-01,2.5@2019-09-03}')), + + (tfi, TInterpolation.STEPWISE, + TFloatSeq('Interp=Step;[1.5@2019-09-01]')), + (tfds, TInterpolation.STEPWISE, + TFloatSeqSet('Interp=Step;{[1.5@2019-09-01], [2.5@2019-09-02]}')), + (tfs_s, TInterpolation.STEPWISE, + TFloatSeq('Interp=Step;[1.5@2019-09-01, 1.5@2019-09-02]')), + (tfss_s, TInterpolation.STEPWISE, + TFloatSeqSet('Interp=Step;{[1.5@2019-09-01, 1.5@2019-09-02],' + '[2.5@2019-09-03, 2.5@2019-09-05]}')), + + (tfi, TInterpolation.LINEAR, + TFloatSeq('[1.5@2019-09-01]')), + (tfds, TInterpolation.LINEAR, + TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02]}')), + (tfs_l, TInterpolation.LINEAR, + TFloatSeqSet('{[1.5@2019-09-01, 1.5@2019-09-02), [2.5@2019-09-02]}')), + (tfss_l, TInterpolation.LINEAR, + TFloatSeqSet('{[1.5@2019-09-01, 1.5@2019-09-02),' + '[2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), + ], + ids=['Instant to discrete', 'Discrete Sequence to discrete', 'Sequence to discrete', 'SequenceSet to discrete', + 'Instant to step', 'Discrete Sequence to step', 'Sequence to step', 'SequenceSet to step', + 'Instant to linear', 'Discrete Sequence to linear', 'Sequence to linear', 'SequenceSet to linear'] + ) + def test_set_interpolation(self, temporal, interpolation, expected): + assert temporal.set_interpolation(interpolation) == expected + @pytest.mark.parametrize( 'tfloat, delta, expected', [(tfi, timedelta(days=4), TFloatInst('1.5@2019-09-05')), @@ -894,32 +1016,85 @@ def test_shift_tscale(self): '[1.5@2019-09-05 01:00:00, 1.5@2019-09-05 02:00:00]}') @pytest.mark.parametrize( - 'temporal, expected', - [ - (tfi, TIntInst('1@2019-09-01')), - (tfds, TIntSeq('{1@2019-09-01,2@2019-09-02}')), - (tfsts, TIntSeq('[1@2019-09-01,2@2019-09-02]')), - (tfstss, TIntSeq('{[1@2019-09-01,2@2019-09-02],[1@2019-09-03,1@2019-09-05]}')), - ], - ids=['Instant', 'Discrete Sequence', 'Step Sequence', 'Step Sequence Set'] + 'tfloat, delta, expected', + [(tfi, timedelta(days=4), TFloatInst('1.5@2019-09-01')), + (tfi, timedelta(hours=12), TFloatInst('1.5@2019-09-01')), + (tfds, timedelta(days=4), TFloatSeq('{1.5@2019-09-01}')), + (tfds, timedelta(hours=12), TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}')), + (tfs, timedelta(days=4), TFloatSeq('{1.5@2019-09-01}')), + (tfs, timedelta(hours=12), TFloatSeq('{1.5@2019-09-01, 2@2019-09-01 12:00:00, 2.5@2019-09-02}')), + (tfss, timedelta(days=4), + TFloatSeq('{1.5@2019-09-01,1.5@2019-09-05}')), + (tfss, timedelta(hours=12), + TFloatSeq('{1.5@2019-09-01, 2@2019-09-01 12:00:00, 2.5@2019-09-02,' + '1.5@2019-09-03, 1.5@2019-09-03 12:00:00, 1.5@2019-09-04, ' + '1.5@2019-09-04 12:00:00, 1.5@2019-09-05}')), + ], + ids=['Instant days', 'Instant hours', + 'Discrete Sequence days', 'Discrete Sequence hours', + 'Sequence days', 'Sequence hours', + 'Sequence Set days', 'Sequence Set hours'] + ) + def test_temporal_sample(self, tfloat, delta, expected): + assert tfloat.temporal_sample(delta) == expected + + # This function should be corrected in MEOS + # @pytest.mark.parametrize( + # 'tfloat, delta, expected', + # [(tfi, timedelta(days=4), TFloatInst('1.5@2019-09-01')), + # (tfi, timedelta(hours=12), TFloatInst('1.5@2019-09-01')), + # (tfds, timedelta(days=4), TFloatSeq('{2@2019-09-01}')), + # (tfds, timedelta(hours=12), TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}')), + # (tfs, timedelta(days=4), TFloatSeq('{2@2019-09-01}')), + # (tfs, timedelta(hours=12), TFloatSeq('{1.5@2019-09-01, 2@2019-09-01 12:00:00, 2.5@2019-09-02}')), + # (tfss, timedelta(days=4), + # TFloatSeq('{1.5@2019-09-01,1.5@2019-09-05}')), + # (tfss, timedelta(hours=12), + # TFloatSeq('{1.5@2019-09-01, 2@2019-09-01 12:00:00, 2.5@2019-09-02,' + # '1.5@2019-09-03, 1.5@2019-09-03 12:00:00, 1.5@2019-09-04, ' + # '1.5@2019-09-04 12:00:00, 1.5@2019-09-05}')), + # ], + # ids=['Instant days', 'Instant hours', + # 'Discrete Sequence days', 'Discrete Sequence hours', + # 'Sequence days', 'Sequence hours', + # 'Sequence Set days', 'Sequence Set hours'] + # ) + # def test_temporal_precision(self, tfloat, delta, expected): + # assert tfloat.temporal_precision(delta) == expected + + @pytest.mark.parametrize( + 'tfloat, delta, expected', + [(tfs, timedelta(days=4), None), + (tfs, timedelta(hours=12), None), + (tfss, timedelta(days=4), None), + (tfss, timedelta(hours=12), + TFloatSeq('[1.5@2019-09-03, 1.5@2019-09-05]')), + ], + ids=['Sequence days', 'Sequence hours', + 'Sequence Set days', 'Sequence Set hours'] ) - def test_to_tint(self, temporal, expected): - temp = temporal.to_tint() - assert isinstance(temp, TInt) - assert temp == expected + def test_stops(self, tfloat, delta, expected): + assert tfloat.stops(0.0, delta) == expected @pytest.mark.parametrize( 'temporal, expected', [ - (tfi, floatrange(1.5, 1.5, True, True)), - (tfds, floatrange(1.5, 2.5, True, True)), - (tfs, floatrange(1.5, 2.5, True, True)), - (tfss, floatrange(1.5, 2.5, True, True)), + (TFloatInst('1.123456789@2019-09-01'), + TFloatInst('1.12@2019-09-01')), + (TFloatSeq('{1.123456789@2019-09-01,' + '2.123456789@2019-09-02}'), + TFloatSeq('{1.12@2019-09-01, 2.12@2019-09-02}')), + (TFloatSeq('[1.123456789@2019-09-01, 2.123456789@2019-09-02]'), + TFloatSeq('[1.12@2019-09-01, 2.12@2019-09-02]')), + (TFloatSeqSet('{[1.123456789@2019-09-01, 2.123456789@2019-09-02],' + '[1.123456789@2019-09-03, 1.123456789@2019-09-05]}'), + TFloatSeq('{[1.12@2019-09-01, 2.12@2019-09-02],' + '[1.12@2019-09-03, 1.12@2019-09-05]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_floatrange(self, temporal, expected): - assert temporal.to_floatrange() == expected + def test_round(self, temporal, expected): + assert temporal.round(maxdd=2) class TestTFloatModifications(TestTFloat): @@ -1006,23 +1181,17 @@ class TestTFloatMathematicalOperations(TestTFloat): tfds = TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}') tfs = TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]') tfss = TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}') - tintarg = TIntSeq('[2@2019-09-01, 1@2019-09-02, 1@2019-09-03]') tfloatarg = TFloatSeq('[2.5@2019-09-01, 1.5@2019-09-02, 1.5@2019-09-03]') @pytest.mark.parametrize( 'temporal, argument, expected', [ - (tfi, tintarg, TFloatInst('3.5@2019-09-01')), - (tfds, tintarg, TFloatSeq('{3.5@2019-09-01, 3.5@2019-09-02}')), - (tfs, tintarg, TFloatSeqSet('{[3.5@2019-09-01, 4.5@2019-09-02),[3.5@2019-09-02]}')), - (tfss, tintarg, TFloatSeqSet('{[3.5@2019-09-01, 4.5@2019-09-02),[3.5@2019-09-02],[2.5@2019-09-03]}')), (tfi, tfloatarg, TFloatInst('4@2019-09-01')), (tfds, tfloatarg, TFloatSeq('{4@2019-09-01, 4@2019-09-02}')), (tfs, tfloatarg, TFloatSeqSet('{[4@2019-09-01, 4@2019-09-02]}')), (tfss, tfloatarg, TFloatSeqSet('{[4@2019-09-01, 4@2019-09-02],[3@2019-09-03]}')), ], - ids=['Instant TInt', 'Discrete Sequence TInt', 'Sequence TInt', 'SequenceSet TInt', - 'Instant TFloat', 'Discrete Sequence TFloat', 'Sequence TFloat', 'SequenceSet TFloat'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_temporal_add_temporal(self, temporal, argument, expected): assert temporal.add(argument) == expected @@ -1040,28 +1209,19 @@ def test_temporal_add_temporal(self, temporal, argument, expected): ) def test_temporal_add_int_float(self, temporal, argument, expected): assert temporal.add(argument) == expected - assert temporal.add(float(argument)) == expected assert temporal.radd(argument) == expected - assert temporal.radd(float(argument)) == expected assert (temporal + argument) == expected - assert (temporal + float(argument)) == expected assert (argument + temporal) == expected - assert (float(argument) + temporal) == expected @pytest.mark.parametrize( 'temporal, argument, expected', [ - (tfi, tintarg, TFloatInst('-0.5@2019-09-01')), - (tfds, tintarg, TFloatSeq('{-0.5@2019-09-01, 1.5@2019-09-02}')), - (tfs, tintarg, TFloatSeqSet('{[-0.5@2019-09-01, 0.5@2019-09-02), [1.5@2019-09-02]}')), - (tfss, tintarg, TFloatSeqSet('{[-0.5@2019-09-01, 0.5@2019-09-02), [1.5@2019-09-02],[0.5@2019-09-03]}')), (tfi, tfloatarg, TFloatInst('-1@2019-09-01')), (tfds, tfloatarg, TFloatSeq('{-1@2019-09-01, 1@2019-09-02}')), (tfs, tfloatarg, TFloatSeqSet('{[-1@2019-09-01, 1@2019-09-02]}')), (tfss, tfloatarg, TFloatSeqSet('{[-1@2019-09-01, 1@2019-09-02],[0@2019-09-03]}')), ], - ids=['Instant TInt', 'Discrete Sequence TInt', 'Sequence TInt', 'SequenceSet TInt', - 'Instant TFloat', 'Discrete Sequence TFloat', 'Sequence TFloat', 'SequenceSet TFloat'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_temporal_sub_temporal(self, temporal, argument, expected): assert temporal.sub(argument) == expected @@ -1079,28 +1239,19 @@ def test_temporal_sub_temporal(self, temporal, argument, expected): ) def test_temporal_sub_int_float(self, temporal, argument, expected): assert temporal.sub(argument) == expected - assert temporal.sub(float(argument)) == expected assert temporal.rsub(argument) == -1 * expected - assert temporal.rsub(float(argument)) == -1 * expected assert (temporal - argument) == expected - assert (temporal - float(argument)) == expected assert (argument - temporal) == -1 * expected - assert (float(argument) - temporal) == -1 * expected @pytest.mark.parametrize( 'temporal, argument, expected', [ - (tfi, tintarg, TFloatInst('3@2019-09-01')), - (tfds, tintarg, TFloatSeq('{3@2019-09-01, 2.5@2019-09-02}')), - (tfs, tintarg, TFloatSeqSet('{[3@2019-09-01, 5@2019-09-02 00:00:00+00), [2.5@2019-09-02]}')), - (tfss, tintarg, TFloatSeqSet('{[3@2019-09-01, 5@2019-09-02 00:00:00+00), [2.5@2019-09-02],[1.5@2019-09-03]}')), (tfi, tfloatarg, TFloatInst('3.75@2019-09-01')), (tfds, tfloatarg, TFloatSeq('{3.75@2019-09-01, 3.75@2019-09-02}')), (tfs, tfloatarg, TFloatSeqSet('{[3.75@2019-09-01, 4@2019-09-01 12:00:00+00, 3.75@2019-09-02]}')), (tfss, tfloatarg, TFloatSeqSet('{[3.75@2019-09-01, 4@2019-09-01 12:00:00+00, 3.75@2019-09-02], [2.25@2019-09-03]}')), ], - ids=['Instant TInt', 'Discrete Sequence TInt', 'Sequence TInt', 'SequenceSet TInt', - 'Instant TFloat', 'Discrete Sequence TFloat', 'Sequence TFloat', 'SequenceSet TFloat'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_temporal_mul_temporal(self, temporal, argument, expected): assert temporal.mul(argument) == expected @@ -1133,25 +1284,16 @@ def test_temporal_mul_int_float(self, temporal, argument, expected): assert temporal.rmul(argument) == expected assert (temporal * argument) == expected assert (argument * temporal) == expected - assert temporal.mul(float(argument)) == expected - assert temporal.rmul(float(argument)) == expected - assert (temporal * float(argument)) == expected - assert (float(argument) * temporal) == expected @pytest.mark.parametrize( 'temporal, argument, expected', [ - (tfi, tintarg, TFloatInst('0.75@2019-09-01')), - (tfds, tintarg, TFloatSeq('{0.75@2019-09-01, 2.5@2019-09-02}')), - (tfs, tintarg, TFloatSeqSet('{[0.75@2019-09-01, 1.25@2019-09-02 00:00:00+00), [2.5@2019-09-02]}')), - (tfss, tintarg, TFloatSeqSet('{[0.75@2019-09-01, 1.25@2019-09-02 00:00:00+00), [2.5@2019-09-02], [1.5@2019-09-03]}')), (tfi, tfloatarg, TFloatInst('0.6@2019-09-01')), (tfds, tfloatarg, TFloatSeq('{0.6@2019-09-01, 1.667@2019-09-02}')), (tfs, tfloatarg, TFloatSeqSet('{[0.6@2019-09-01, 1@2019-09-01 12:00:00+00, 1.667@2019-09-02]}')), (tfss, tfloatarg, TFloatSeqSet('{[0.6@2019-09-01, 1@2019-09-01 12:00:00+00, 1.667@2019-09-02], [1@2019-09-03]}')), ], - ids=['Instant TInt', 'Discrete Sequence TInt', 'Sequence TInt', 'SequenceSet TInt', - 'Instant TFloat', 'Discrete Sequence TFloat', 'Sequence TFloat', 'SequenceSet TFloat'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_temporal_div_temporal(self, temporal, argument, expected): assert temporal.div(argument).round(3) == expected @@ -1174,9 +1316,7 @@ def test_temporal_div_temporal(self, temporal, argument, expected): ) def test_temporal_div_int_float(self, temporal, argument, expected): assert temporal.div(argument) == expected - assert temporal.div(float(argument)) == expected assert (temporal / argument) == expected - assert (temporal / float(argument)) == expected @pytest.mark.parametrize( 'temporal', @@ -1190,16 +1330,18 @@ def test_abs(self, temporal): @pytest.mark.parametrize( 'temporal, expected', [ - # (tfi, TFloatInst('1@2019-09-01')), + (tfi, None), (tfds, TFloatSeq('{1@2019-09-01, 1@2019-09-02}')), (tfs, TFloatSeq('Interp=Step;[1@2019-09-01, 1@2019-09-02)')), (tfss, TFloatSeqSet('Interp=Step;{[1@2019-09-01, 1@2019-09-02),[0@2019-09-03, 0@2019-09-05)}')), ], - ids=[# 'Instant', - 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_delta_value(self, temporal, expected): - assert temporal.delta_value() == expected + if expected is None: + assert temporal.delta_value() is None + else: + assert temporal.delta_value() == expected @pytest.mark.parametrize( 'temporal, expected', @@ -1225,16 +1367,18 @@ def test_to_radians_to_degrees(self, temporal): @pytest.mark.parametrize( 'temporal, expected', [ - # (tfi, None), - # (tfds, None), + (tfi, None), + (tfds, None), (tfs, TFloatSeq('Interp=Step;[-1@2019-09-01, -1@2019-09-02]')), (tfss, TFloatSeqSet('Interp=Step;{[-1@2019-09-01, -1@2019-09-02],[0@2019-09-03, 0@2019-09-05]}')), ], - ids=[ # 'Instant', 'Discrete Sequence', - 'Sequence', 'SequenceSet'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_derivative(self, temporal, expected): - assert temporal.derivative() * 3600 * 24 == expected + if expected is None: + assert temporal.derivative() is None + else: + assert temporal.derivative() * 3600 * 24 == expected class TestTFloatRestrictors(TestTFloat): @@ -1255,51 +1399,79 @@ class TestTFloatRestrictors(TestTFloat): (tfi, timestamp_set, TFloatInst('1.5@2019-09-01')), (tfi, period, TFloatInst('1.5@2019-09-01')), (tfi, period_set, TFloatInst('1.5@2019-09-01')), - (tfi, 1.5, TFloatInst('1.5@2019-09-01')), - (tfi, 2.5, None), - # (tfi, [1.5,2.5], TFloatInst('1.5@2019-09-01')), (tfds, timestamp, TFloatSeq('{1.5@2019-09-01}')), (tfds, timestamp_set, TFloatSeq('{1.5@2019-09-01}')), (tfds, period, TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}')), (tfds, period_set, TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}')), - (tfds, 1.5, TFloatSeq('{1.5@2019-09-01}')), - (tfds, 2.5, TFloatSeq('{2.5@2019-09-02}')), - # (tfds, [1.5,2.5], TFloatSeq('{1.5@2019-09-01}')), (tfs, timestamp, TFloatSeq('[1.5@2019-09-01]')), (tfs, timestamp_set, TFloatSeq('{1.5@2019-09-01}')), (tfs, period, TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]')), (tfs, period_set, TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]')), - (tfs, 1.5, TFloatSeq('[1.5@2019-09-01]')), - (tfs, 2.5, TFloatSeq('[2.5@2019-09-02]')), - # (tfs, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), (tfss, timestamp, TFloatSeqSet('[1.5@2019-09-01]')), (tfss, timestamp_set, TFloatSeq('{1.5@2019-09-01, 1.5@2019-09-03}')), (tfss, period, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}')), (tfss, period_set, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), - (tfss, 1.5, TFloatSeqSet('{[1.5@2019-09-01],[1.5@2019-09-03, 1.5@2019-09-05]}')), - (tfss, 2.5, TFloatSeqSet('{[2.5@2019-09-02]}')) - # (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),[1.5@2019-09-03, 1.5@2019-09-05]}')) ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', 'Instant-1_5', 'Instant-2_5', - #'Instant-[1.5,2.5]', + 'Instant-PeriodSet', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', - 'Discrete Sequence-1_5', 'Discrete Sequence-2_5', - # 'Discrete Sequence-[1.5,2.5]' 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', 'Sequence-1_5', 'Sequence-2_5', - # 'Sequence-[1.5,2.5]', - 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', - 'SequenceSet-PeriodSet', 'SequenceSet-1_5', 'SequenceSet-2_5', - #'SequenceSet-[1.5,2.5]' + 'Sequence-PeriodSet', + 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', + 'SequenceSet-Period', 'SequenceSet-PeriodSet', ] ) - def test_at(self, temporal, restrictor, expected): + def test_at_time(self, temporal, restrictor, expected): + assert temporal.at(restrictor) == expected + + @pytest.mark.parametrize( + 'temporal, restrictor, expected', + [ + (tfi, 1.5, TFloatInst('1.5@2019-09-01')), + (tfi, 2.5, None), + (tfi, floatrange(1.5, 1.5, True, True), TFloatInst('1.5@2019-09-01')), + # (tfi, [1.5,2.5], TFloatInst('1.5@2019-09-01')), + # (tfi, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # TFloatInst('1.5@2019-09-01')), + + (tfds, 1.5, TFloatSeq('{1.5@2019-09-01}')), + (tfds, 2.5, TFloatSeq('{2.5@2019-09-02}')), + (tfds, floatrange(1.5, 1.5, True, True), TFloatInst('1.5@2019-09-01')), + # (tfds, [1.5,2.5], TFloatSeq('{1.5@2019-09-01}')), + # (tfds, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # TFloatInst('1.5@2019-09-01')), + + (tfs, 1.5, TFloatSeq('[1.5@2019-09-01]')), + (tfs, 2.5, TFloatSeq('[2.5@2019-09-02]')), + (tfs, floatrange(1.5, 1.5, True, True), TFloatInst('1.5@2019-09-01')), + # (tfs, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), + # (tfs, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # TFloatInst('1.5@2019-09-01')), + + (tfss, 1.5, TFloatSeqSet('{[1.5@2019-09-01],[1.5@2019-09-03, 1.5@2019-09-05]}')), + (tfss, 2.5, TFloatSeqSet('{[2.5@2019-09-02]}')), + (tfss, floatrange(1.5, 1.5, True, True), + TFloatSeqSet('{[1.5@2019-09-01],[1.5@2019-09-03, 1.5@2019-09-05]}')), + # (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),[1.5@2019-09-03, 1.5@2019-09-05]}')) + # (tfss, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # TFloatInst('1.5@2019-09-01')), + ], + ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', + # 'Instant-ValueList', 'Instant-RangeList', + 'Discrete Sequence-1.5', 'Discrete Sequence-2.5', 'Discrete Sequence-Range', + # 'Discrete Sequence-ValueList', 'Discrete Sequence-RangeList' + 'Sequence-1.5', 'Sequence-2.5', 'Sequence-Range', + # 'Sequence-ValueList', 'Sequence-RangeList', + 'SequenceSet-1.5', 'SequenceSet-2.5', 'Sequence Set-Range', + # 'SequenceSet-ValueList', 'SequenceSet-RangeList' + ] + ) + def test_at_values(self, temporal, restrictor, expected): assert temporal.at(restrictor) == expected @pytest.mark.parametrize( @@ -1335,25 +1507,16 @@ def test_at_max(self, temporal, expected): (tfi, timestamp_set, None), (tfi, period, None), (tfi, period_set, None), - (tfi, 1.5, None), - (tfi, 2.5, TFloatInst('1.5@2019-09-01')), - # (tfi, [1.5,2.5], TFloatInst('1.5@2019-09-01')), (tfds, timestamp, TFloatSeq('{2.5@2019-09-02}')), (tfds, timestamp_set, TFloatSeq('{2.5@2019-09-02}')), (tfds, period, None), (tfds, period_set, None), - (tfds, 1.5, TFloatSeq('{2.5@2019-09-02}')), - (tfds, 2.5, TFloatSeq('{1.5@2019-09-01}')), - # (tfds, [1.5,2.5], TFloatSeq('{1.5@2019-09-01}')), (tfs, timestamp, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), (tfs, timestamp_set, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), (tfs, period, None), (tfs, period_set, None), - (tfs, 1.5, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), - (tfs, 2.5, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), - # (tfs, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), (tfss, timestamp, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), @@ -1361,28 +1524,67 @@ def test_at_max(self, temporal, expected): TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02],(1.5@2019-09-03, 1.5@2019-09-05]}')), (tfss, period, TFloatSeqSet('{[1.5@2019-09-03, 1.5@2019-09-05]}')), (tfss, period_set, None), - (tfss, 1.5, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), - (tfss, 2.5, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),[1.5@2019-09-03, 1.5@2019-09-05]}')), - # (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),[1.5@2019-09-03, 1.5@2019-09-05]}')) ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', 'Instant-1_5', 'Instant-2_5', - #'Instant-[1.5,2.5]', - 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', - 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', - 'Discrete Sequence-1_5', 'Discrete Sequence-2_5', - # 'Discrete Sequence-[1.5,2.5]' - 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', 'Sequence-1_5', 'Sequence-2_5', - # 'Sequence-[1.5,2.5]', - 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', - 'SequenceSet-PeriodSet', 'SequenceSet-1_5', 'SequenceSet-2_5', - #'SequenceSet-[1.5,2.5]' + 'Instant-PeriodSet', 'Discrete Sequence-Timestamp', + 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', + 'Discrete Sequence-PeriodSet', 'Sequence-Timestamp', + 'Sequence-TimestampSet', 'Sequence-Period', 'Sequence-PeriodSet', + 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', + 'SequenceSet-Period', 'SequenceSet-PeriodSet', ] ) - def test_minus(self, temporal, restrictor, expected): + def test_minus_time(self, temporal, restrictor, expected): assert temporal.minus(restrictor) == expected + @pytest.mark.parametrize( + 'temporal, restrictor, expected', + [ + (tfi, 1.5, None), + (tfi, 2.5, TFloatInst('1.5@2019-09-01')), + (tfi, floatrange(1.5, 1.5, True, True), None), + # (tfi, [1.5,2.5], TFloatInst('1.5@2019-09-01')), + # (tfi, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # TFloatInst('1.5@2019-09-01')), + + (tfds, 1.5, TFloatSeq('{2.5@2019-09-02}')), + (tfds, 2.5, TFloatSeq('{1.5@2019-09-01}')), + (tfds, floatrange(1.5, 1.5, True, True), TFloatSeq('{2.5@2019-09-02}')), + # (tfds, [1.5,2.5], TFloatSeq('{1.5@2019-09-01}')), + # (tfds, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # TFloatInst('1.5@2019-09-01')), + + (tfs, 1.5, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), + (tfs, 2.5, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), + (tfs, floatrange(1.5, 1.5, True, True), TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), + # (tfs, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), + # (tfs, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # TFloatInst('1.5@2019-09-01')), + + (tfss, 1.5, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), + (tfss, 2.5, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),[1.5@2019-09-03, 1.5@2019-09-05]}')), + (tfs, floatrange(1.5, 1.5, True, True), TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), + # (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),[1.5@2019-09-03, 1.5@2019-09-05]}')) + # (tfss, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # TFloatInst('1.5@2019-09-01')), + ], + ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', + # 'Instant-ValueList', 'Instant-RangeList', + 'Discrete Sequence-1.5', 'Discrete Sequence-2.5', + 'Discrete Sequence-Range', + # 'Discrete Sequence-ValueList' 'Discrete Sequence-RangeList', + 'Sequence-1.5', 'Sequence-2.5', 'Sequence-Range', + # 'Sequence-ValueList', 'Sequence-RangeList', + 'SequenceSet-1.5', 'SequenceSet-2.5', 'Sequence Set-Range', + # 'SequenceSet-ValueList', 'SequenceSet-RangeList', + ] + ) + def test_minus_values(self, temporal, restrictor, expected): + if expected is None: + assert temporal.minus(restrictor) is None + else: + assert temporal.minus(restrictor) == expected + @pytest.mark.parametrize( 'temporal, expected', [ @@ -1416,47 +1618,74 @@ def test_minus_max(self, temporal, expected): (tfi, timestamp_set), (tfi, period), (tfi, period_set), - (tfi, 1.5), - (tfi, 2.5), - # (tfi, [1.5,2.5]), (tfds, timestamp), (tfds, timestamp_set), (tfds, period), (tfds, period_set), - (tfds, 1.5), - (tfds, 2.5), - # (tfds, [1.5,2.5]), (tfs, timestamp), (tfs, timestamp_set), (tfs, period), (tfs, period_set), - (tfs, 1.5), - (tfs, 2.5), - # (tfs, [1.5,2.5]), (tfss, timestamp), (tfss, timestamp_set), (tfss, period), (tfss, period_set), - (tfss, 1.5), - (tfss, 2.5), - # (tfss, [1.5,2.5]), ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', 'Instant-1_5', 'Instant-2_5', # 'Instant-[1.5,2.5]' + 'Instant-PeriodSet', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', - 'Discrete Sequence-1_5', 'Discrete Sequence-2_5', # 'Discrete Sequence-[1.5,2.5]', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', 'Sequence-1_5', 'Sequence-2_5', # 'Sequence-[1.5,2.5]' + 'Sequence-PeriodSet', 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', - 'SequenceSet-PeriodSet', 'SequenceSet-1_5', 'SequenceSet-2_5', - # 'SequenceSet-[1.5,2.5]' + 'SequenceSet-PeriodSet', + ] + ) + def test_at_minus_time(self, temporal, restrictor): + assert TFloat.merge(temporal.at(restrictor), temporal.minus(restrictor)) == temporal + + @pytest.mark.parametrize( + 'temporal, restrictor', + [ + (tfi, 1.5), + (tfi, 2.5), + (tfi, floatrange(1.5, 1.5, True, True)), + # (tfi, [1.5,2.5]), + # (tfi, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), + + (tfds, 1.5), + (tfds, 2.5), + (tfds, floatrange(1.5, 1.5, True, True)), + # (tfds, [1.5,2.5]), + # (tfds, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), + + (tfs, 1.5), + (tfs, 2.5), + (tfs, floatrange(1.5, 1.5, True, True)), + # (tfs, [1.5,2.5]), + # (tfs, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), + + (tfss, 1.5), + (tfss, 2.5), + (tfss, floatrange(1.5, 1.5, True, True)), + # (tfss, [1.5,2.5]), + # (tfss, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), + ], + ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', + # 'Instant-ValueList', 'Instant-RangeList', + 'Discrete Sequence-1.5', 'Discrete Sequence-2.5', + 'Discrete Sequence-Range', + # 'Discrete Sequence-ValueList', 'Discrete Sequence-RangeList', + 'Sequence-1.5', 'Sequence-2.5', 'Sequence-Range', + # 'Sequence-ValueList', 'Sequence-RangeList', + 'SequenceSet-1.5', 'SequenceSet-2.5', 'SequenceSet-Range', + # 'SequenceSet-ValueList', 'SequenceSet-RangeList', ] ) - def test_at_minus(self, temporal, restrictor): + def test_at_minus_values(self, temporal, restrictor): assert TFloat.merge(temporal.at(restrictor), temporal.minus(restrictor)) == temporal @pytest.mark.parametrize( diff --git a/pymeos/tests/main/tgeogpoint_test.py b/pymeos/tests/main/tgeogpoint_test.py index 3702582d..6069d5c2 100644 --- a/pymeos/tests/main/tgeogpoint_test.py +++ b/pymeos/tests/main/tgeogpoint_test.py @@ -3,11 +3,15 @@ from datetime import datetime, timezone, timedelta import pytest +import numpy as np from shapely import Point import shapely.geometry -from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, TFloat, TFloatInst, TFloatSeq, TFloatSeqSet, TGeogPoint, \ - TGeogPointInst, TGeogPointSeq, TGeogPointSeqSet, STBox, TInterpolation, TimestampSet, Period, PeriodSet +from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, \ + TFloat, TFloatInst, TFloatSeq, TFloatSeqSet, \ + TGeomPoint, TGeomPointInst, TGeomPointSeq, TGeomPointSeqSet, \ + TGeogPoint, TGeogPointInst, TGeogPointSeq, TGeogPointSeqSet, \ + STBox, TInterpolation, TimestampSet, Period, PeriodSet from tests.conftest import TestPyMEOS @@ -408,6 +412,19 @@ class TestTGeogPointAccessors(TestTGeogPoint): tps = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') tpss = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, STBox('GEODSTBOX XT(((1,1),(1,1)),[2019-09-01, 2019-09-01])')), + (tpds, STBox('GEODSTBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-02])')), + (tps, STBox('GEODSTBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-02])')), + (tpss, STBox('GEODSTBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-05])')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_bounding_box(self, temporal, expected): + assert temporal.bounding_box() == expected + @pytest.mark.parametrize( 'temporal, expected', [ @@ -594,27 +611,27 @@ def test_end_instant(self, temporal, expected): 'temporal, expected', [ (tpi, tpi), - (tpds, TGeogPointInst('Point(2 2)@2019-09-02')), - (tps, TGeogPointInst('Point(2 2)@2019-09-02')), - (tpss, TGeogPointInst('Point(2 2)@2019-09-02')), + (tpds, tpi), + (tps, tpi), + (tpss, tpi), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_max_instant(self, temporal, expected): - assert temporal.max_instant() == expected + def test_min_instant(self, temporal, expected): + assert temporal.min_instant() == expected @pytest.mark.parametrize( 'temporal, expected', [ (tpi, tpi), - (tpds, tpi), - (tps, tpi), - (tpss, tpi), + (tpds, TGeogPointInst('Point(2 2)@2019-09-02')), + (tps, TGeogPointInst('Point(2 2)@2019-09-02')), + (tpss, TGeogPointInst('Point(2 2)@2019-09-02')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_min_instant(self, temporal, expected): - assert temporal.min_instant() == expected + def test_max_instant(self, temporal, expected): + assert temporal.max_instant() == expected @pytest.mark.parametrize( 'temporal, n, expected', @@ -726,6 +743,23 @@ def test_timestamps(self, temporal, expected): def test_segments(self, temporal, expected): assert temporal.segments() == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, 1181779687), + (tpds, 1545137628), + (tps, 1545137628), + (tpss, 1008965061) + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_hash(self, temporal, expected): + assert hash(temporal) == expected + + def test_value_timestamp(self): + assert self.tpi.value() == Point(1,1) + assert self.tpi.timestamp() == datetime(year=2019, month=9, day=1, tzinfo=timezone.utc) + @pytest.mark.parametrize( 'temporal, expected', [ @@ -738,18 +772,220 @@ def test_lower_upper_inc(self, temporal, expected): assert temporal.lower_inc() == expected assert temporal.upper_inc() == expected + def test_sequenceset_sequence_functions(self): + tpss1 =TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],' + '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05], [Point(3 3)@2019-09-06]}') + assert tpss1.num_sequences() == 3 + assert tpss1.start_sequence() == TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') + assert tpss1.end_sequence() == TGeogPointSeq('[Point(3 3)@2019-09-06]') + assert tpss1.sequence_n(1) == TGeogPointSeq('[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]') + assert tpss1.sequences() == [TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]'), + TGeogPointSeq('[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]'), + TGeogPointSeq('[Point(3 3)@2019-09-06]')] + + +class TestTGeogPointTPointAccessors(TestTGeogPoint): + tpi = TGeogPointInst('Point(1 1)@2019-09-01') + tpds = TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') + tps = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') + tpss = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + tpi3d = TGeogPointInst('Point(1 1 1)@2019-09-01') + tpds3d = TGeogPointSeq('{Point(1 1 1)@2019-09-01, Point(2 2 2)@2019-09-02}') + tps3d = TGeogPointSeq('[Point(1 1 1)@2019-09-01, Point(2 2 2)@2019-09-02]') + tpss3d = TGeogPointSeqSet('{[Point(1 1 1)@2019-09-01, Point(2 2 2)@2019-09-02],' + '[Point(1 1 1)@2019-09-03, Point(1 1 1)@2019-09-05]}') + @pytest.mark.parametrize( 'temporal, expected', [ - (tpi, 1181779687), - (tpds, 1545137628), - (tps, 1545137628), - (tpss, 1008965061) + (tpi, 0), + (tpds, 0), + (tps, 156876.1494), + (tpss, 156876.1494), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_hash(self, temporal, expected): - assert hash(temporal) == expected + def test_length(self, temporal, expected): + assert round(temporal.length(),4) == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, TFloatInst('0@2019-09-01')), + (tpds, TFloatSeq('{0@2019-09-01, 0@2019-09-02}')), + (tps, TFloatSeq('[0@2019-09-01, 156876.1494@2019-09-02]')), + (tpss, TFloatSeqSet('{[0@2019-09-01, 156876.1494@2019-09-02],' + '[156876.1494@2019-09-03, 156876.1494@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_cumulative_length(self, temporal, expected): + assert temporal.cumulative_length().round(4) == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, None), + (tpds, None), + (tps, TFloatSeq('Interp=Step;[1.8157@2019-09-01, 1.8157@2019-09-02]')), + (tpss, TFloatSeqSet('Interp=Step;{[1.8157@2019-09-01, 1.8157@2019-09-02],' + '[0@2019-09-03, 0@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_speed(self, temporal, expected): + if expected is None: + assert temporal.speed() is None + else: + assert temporal.speed().round(4) == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, TFloatInst('1@2019-09-01')), + (tpds, TFloatSeq('{1@2019-09-01, 2@2019-09-02}')), + (tps, TFloatSeq('[1@2019-09-01, 2@2019-09-02]')), + (tpss, TFloatSeqSet('{[1@2019-09-01, 2@2019-09-02],' + '[1@2019-09-03, 1@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_x_y(self, temporal, expected): + assert temporal.x() == expected + assert temporal.y() == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi3d, TFloatInst('1@2019-09-01')), + (tpds3d, TFloatSeq('{1@2019-09-01, 2@2019-09-02}')), + (tps3d, TFloatSeq('[1@2019-09-01, 2@2019-09-02]')), + (tpss3d, TFloatSeqSet('{[1@2019-09-01, 2@2019-09-02],' + '[1@2019-09-03, 1@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_x_y_z(self, temporal, expected): + assert temporal.x() == expected + assert temporal.y() == expected + assert temporal.z() == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, False), + (tpds, False), + (tps, False), + (tpss, False), + (tpi3d, True), + (tpds3d, True), + (tps3d, True), + (tpss3d, True), + ], + ids=['Instant 2D', 'Discrete Sequence 2D', 'Sequence 2D', 'SequenceSet 2D', + 'Instant 3D', 'Discrete Sequence 3D', 'Sequence 3D', 'SequenceSet 3D'] + ) + def test_has_z(self, temporal, expected): + assert temporal.has_z() == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi3d, True), + (tpds3d, True), + (tps3d, True), + (tpss3d, True), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_has_z(self, temporal, expected): + assert temporal.has_z() == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, []), + (tpds, []), + (tps, [STBox('GEODSTBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-02])')]), + (tpss, [STBox('GEODSTBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-02])'), + STBox('GEODSTBOX XT(((1,1),(1,1)),[2019-09-03, 2019-09-05])')]), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_stboxes(self, temporal, expected): + assert temporal.stboxes() == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, True), + (tpds, True), + (tps, True), + (tpss, True), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_is_simple(self, temporal, expected): + assert temporal.is_simple() == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, TFloatInst('0.7846@2019-09-01')), + (tpds, TFloatSeq('{0.7846@2019-09-01,0.7846@2019-09-02}')), + (tps, TFloatSeq('[0.7846@2019-09-01,0.7846@2019-09-02]')), + (tpss, TFloatSeqSet('{[0.7846@2019-09-01,0.7846@2019-09-02],' + '[0.7846@2019-09-03,0.7846@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_bearing(self, temporal, expected): + assert temporal.bearing(shapely.set_srid(shapely.Point(3,3), 4326)).round(4) == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, None), + (tpds, 45.1705), + (tps, 45.1705), + (tpss, None), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_direction(self, temporal, expected): + res = temporal.direction() + result = round(np.rad2deg(res), 4) if res is not None else None + assert result == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, None), + (tpds, None), + (tps, TFloatSeqSet('Interp=Step;{[0.7884@2019-09-01,0.7884@2019-09-02]}')), + (tpss, TFloatSeqSet('Interp=Step;{[0.7884@2019-09-01,0.7884@2019-09-02]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_azimuth(self, temporal, expected): + res = temporal.azimuth() + result = res.round(4) if res is not None else None + assert result == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, None), + (tpds, None), + (tps, TFloatSeq('{0@2019-09-01,0@2019-09-02}')), + (tpss, TFloatSeqSet('{0@2019-09-01,0@2019-09-02}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_angular_difference(self, temporal, expected): + res = temporal.angular_difference() + result = res.to_degrees() if res is not None else None + assert result == expected @pytest.mark.parametrize( 'temporal, expected', @@ -764,18 +1000,26 @@ def test_hash(self, temporal, expected): def test_srid(self, temporal, expected): assert temporal.srid() == expected + +class TestTGeogPointConversions(TestTGeogPoint): + tpi = TGeogPointInst('Point(1 1)@2019-09-01') + tpds = TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') + tps = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') + tpss = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + @pytest.mark.parametrize( 'temporal, expected', [ - (tpi, STBox('GEODSTBOX XT(((1,1),(1,1)),[2019-09-01, 2019-09-01])')), - (tpds, STBox('GEODSTBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-02])')), - (tps, STBox('GEODSTBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-02])')), - (tpss, STBox('GEODSTBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-05])')), + (tpi, TGeomPointInst('SRID=4326;Point(1 1)@2019-09-01')), + (tpds, TGeomPointSeq('SRID=4326;{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}')), + (tps, TGeomPointSeq('SRID=4326;[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), + (tpss, TGeomPointSeqSet('SRID=4326;{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],' + '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_bounding_box(self, temporal, expected): - assert temporal.bounding_box() == expected + def test_to_geometric(self, temporal, expected): + assert temporal.to_geometric() == expected class TestTGeogPointTransformations(TestTGeogPoint): @@ -784,6 +1028,15 @@ class TestTGeogPointTransformations(TestTGeogPoint): tps = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') tpss = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + tps_d = TGeogPointSeq('[Point(1 1)@2019-09-01]') + tpss_d = TGeogPointSeqSet('{[Point(1 1)@2019-09-01],[Point(2 2)@2019-09-03]}') + tps_s = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02]') + tpss_s = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02],' + '[Point(2 2)@2019-09-03, Point(2 2)@2019-09-05]}') + tps_l = TGeogPointSeq('Interp=Step;[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') + tpss_l = TGeogPointSeqSet('Interp=Step;{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],' + '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + @pytest.mark.parametrize( 'temporal, expected', [ @@ -837,6 +1090,44 @@ def test_to_sequenceset(self, temporal, expected): assert isinstance(temp, TGeogPointSeqSet) assert temp == expected + @pytest.mark.parametrize( + 'temporal, interpolation, expected', + [ + (tpi, TInterpolation.DISCRETE, + TGeogPointSeq('{Point(1 1)@2019-09-01}')), + (tpds, TInterpolation.DISCRETE, tpds), + (tps_d, TInterpolation.DISCRETE, + TGeogPointSeq('{Point(1 1)@2019-09-01}')), + (tpss_d, TInterpolation.DISCRETE, + TGeogPointSeq('{Point(1 1)@2019-09-01,Point(2 2)@2019-09-03}')), + + (tpi, TInterpolation.STEPWISE, + TGeogPointSeq('Interp=Step;[Point(1 1)@2019-09-01]')), + (tpds, TInterpolation.STEPWISE, + TGeogPointSeqSet('Interp=Step;{[Point(1 1)@2019-09-01], [Point(2 2)@2019-09-02]}')), + (tps_s, TInterpolation.STEPWISE, + TGeogPointSeq('Interp=Step;[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02]')), + (tpss_s, TInterpolation.STEPWISE, + TGeogPointSeqSet('Interp=Step;{[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02],' + '[Point(2 2)@2019-09-03, Point(2 2)@2019-09-05]}')), + + (tpi, TInterpolation.LINEAR, + TGeogPointSeq('[Point(1 1)@2019-09-01]')), + (tpds, TInterpolation.LINEAR, + TGeogPointSeqSet('{[Point(1 1)@2019-09-01], [Point(2 2)@2019-09-02]}')), + (tps_l, TInterpolation.LINEAR, + TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02), [Point(2 2)@2019-09-02]}')), + (tpss_l, TInterpolation.LINEAR, + TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02),' + '[Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}')), + ], + ids=['Instant to discrete', 'Discrete Sequence to discrete', 'Sequence to discrete', 'SequenceSet to discrete', + 'Instant to step', 'Discrete Sequence to step', 'Sequence to step', 'SequenceSet to step', + 'Instant to linear', 'Discrete Sequence to linear', 'Sequence to linear', 'SequenceSet to linear'] + ) + def test_set_interpolation(self, temporal, interpolation, expected): + assert temporal.set_interpolation(interpolation) == expected + @pytest.mark.parametrize( 'tpoint, delta, expected', [(tpi, timedelta(days=4), TGeogPointInst('Point(1 1)@2019-09-05')), @@ -901,6 +1192,67 @@ def test_shift_tscale(self): TGeogPointSeqSet('{[Point(1 1)@2019-09-05 00:00:00, Point(2 2)@2019-09-05 00:30:00],' '[Point(1 1)@2019-09-05 01:00:00, Point(1 1)@2019-09-05 02:00:00]}') + @pytest.mark.parametrize( + 'tpoint, delta, expected', + [(tpi, timedelta(days=4), TGeogPointInst('Point(1 1)@2019-09-01')), + (tpi, timedelta(hours=12), TGeogPointInst('Point(1 1)@2019-09-01')), + (tpds, timedelta(days=4), TGeogPointSeq('{Point(1 1)@2019-09-01}')), + (tpds, timedelta(hours=12), TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}')), + (tps, timedelta(days=4), TGeogPointSeq('{Point(1 1)@2019-09-01}')), + (tps, timedelta(hours=12), TGeogPointSeq('{Point(1 1)@2019-09-01, Point(1.5 1.5)@2019-09-01 12:00:00, Point(2 2)@2019-09-02}')), + (tpss, timedelta(days=4), + TGeogPointSeq('{Point(1 1)@2019-09-01,Point(1 1)@2019-09-05}')), + (tpss, timedelta(hours=12), + TGeogPointSeq('{Point(1 1)@2019-09-01, Point(1.5 1.5)@2019-09-01 12:00:00,' + 'Point(2 2)@2019-09-02, Point(1 1)@2019-09-03, Point(1 1)@2019-09-03 12:00:00, ' + 'Point(1 1)@2019-09-04, Point(1 1)@2019-09-04 12:00:00, Point(1 1)@2019-09-05}')), + ], + ids=['Instant days', 'Instant hours', + 'Discrete Sequence days', 'Discrete Sequence hours', + 'Sequence days', 'Sequence hours', + 'Sequence Set days', 'Sequence Set hours' + ] + ) + def test_temporal_sample(self, tpoint, delta, expected): + assert tpoint.temporal_sample(delta).round(1) == expected + + @pytest.mark.parametrize( + 'tpoint, delta, expected', + [(tps, timedelta(days=4), None), + (tps, timedelta(hours=12), None), + (tpss, timedelta(days=4), None), + (tpss, timedelta(hours=12), + TGeogPointSeq('[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]')), + ], + ids=['Sequence days', 'Sequence hours', + 'Sequence Set days', 'Sequence Set hours'] + ) + def test_stops(self, tpoint, delta, expected): + assert tpoint.stops(0.0, delta) == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (TGeogPointInst('Point(1.123456789 1.123456789)@2019-09-01'), + TGeogPointInst('Point(1.12 1.12)@2019-09-01')), + (TGeogPointSeq('{Point(1.123456789 1.123456789)@2019-09-01,' + 'Point(2.123456789 2.123456789)@2019-09-02}'), + TGeogPointSeq('{Point(1.12 1.12)@2019-09-01,Point(2.12 2.12)@2019-09-02}')), + (TGeogPointSeq('[Point(1.123456789 1.123456789)@2019-09-01,' + 'Point(2.123456789 2.123456789)@2019-09-02]'), + TGeogPointSeq('[Point(1.12 1.12)@2019-09-01,Point(2.12 2.12)@2019-09-02]')), + (TGeogPointSeqSet('{[Point(1.123456789 1.123456789)@2019-09-01,' + 'Point(2.123456789 2.123456789)@2019-09-02],' + '[Point(1.123456789 1.123456789)@2019-09-03,' + 'Point(1.123456789 1.123456789)@2019-09-05]}'), + TGeogPointSeq('{[Point(1.12 1.12)@2019-09-01,Point(2.12 2.12)@2019-09-02],' + '[Point(1.12 1.12)@2019-09-03,Point(1.12 1.12)@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_round(self, temporal, expected): + assert temporal.round(maxdd=2) + class TestTGeogPointModifications(TestTGeogPoint): tpi = TGeogPointInst('Point(1 1)@2019-09-01') diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index 1be059b9..55d0a1ca 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -4,6 +4,7 @@ import pytest import math +import numpy as np from shapely import Point, LineString, Polygon, MultiPoint, GeometryCollection from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, \ @@ -433,6 +434,19 @@ class TestTGeomPointTemporalAccessors(TestTGeomPoint): tpss3d = TGeomPointSeqSet('{[Point(1 1 1)@2019-09-01, Point(2 2 2)@2019-09-02],' '[Point(1 1 1)@2019-09-03, Point(1 1 1)@2019-09-05]}') + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, STBox('STBOX XT(((1,1),(1,1)),[2019-09-01, 2019-09-01])')), + (tpds, STBox('STBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-02])')), + (tps, STBox('STBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-02])')), + (tpss, STBox('STBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-05])')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_bounding_box(self, temporal, expected): + assert temporal.bounding_box() == expected + @pytest.mark.parametrize( 'temporal, expected', [ @@ -619,27 +633,27 @@ def test_end_instant(self, temporal, expected): 'temporal, expected', [ (tpi, tpi), - (tpds, TGeomPointInst('Point(2 2)@2019-09-02')), - (tps, TGeomPointInst('Point(2 2)@2019-09-02')), - (tpss, TGeomPointInst('Point(2 2)@2019-09-02')), + (tpds, tpi), + (tps, tpi), + (tpss, tpi), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_max_instant(self, temporal, expected): - assert temporal.max_instant() == expected + def test_min_instant(self, temporal, expected): + assert temporal.min_instant() == expected @pytest.mark.parametrize( 'temporal, expected', [ (tpi, tpi), - (tpds, tpi), - (tps, tpi), - (tpss, tpi), + (tpds, TGeomPointInst('Point(2 2)@2019-09-02')), + (tps, TGeomPointInst('Point(2 2)@2019-09-02')), + (tpss, TGeomPointInst('Point(2 2)@2019-09-02')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_min_instant(self, temporal, expected): - assert temporal.min_instant() == expected + def test_max_instant(self, temporal, expected): + assert temporal.max_instant() == expected @pytest.mark.parametrize( 'temporal, n, expected', @@ -780,11 +794,7 @@ def test_lower_upper_inc(self, temporal, expected): assert temporal.lower_inc() == expected assert temporal.upper_inc() == expected - def test_instant_functions(self): - assert self.tpi.value() == Point(1,1) - assert self.tpi.timestamp() == datetime(year=2019, month=9, day=1, tzinfo=timezone.utc) - - def test_sequenceset_functions(self): + def test_sequenceset_sequence_functions(self): tpss1 =TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],' '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05], [Point(3 3)@2019-09-06]}') assert tpss1.num_sequences() == 3 @@ -807,19 +817,6 @@ class TestTGeomPointTPointAccessors(TestTGeomPoint): tpss3d = TGeomPointSeqSet('{[Point(1 1 1)@2019-09-01, Point(2 2 2)@2019-09-02],' '[Point(1 1 1)@2019-09-03, Point(1 1 1)@2019-09-05]}') - @pytest.mark.parametrize( - 'temporal, expected', - [ - (tpi, STBox('STBOX XT(((1,1),(1,1)),[2019-09-01, 2019-09-01])')), - (tpds, STBox('STBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-02])')), - (tps, STBox('STBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-02])')), - (tpss, STBox('STBOX XT(((1,1),(2,2)),[2019-09-01, 2019-09-05])')), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - ) - def test_bounding_box(self, temporal, expected): - assert temporal.bounding_box() == expected - @pytest.mark.parametrize( 'temporal, expected', [ @@ -847,19 +844,19 @@ def test_length(self, temporal, expected): def test_cumulative_length(self, temporal, expected): assert temporal.cumulative_length() == expected - @pytest.mark.parametrize( - 'temporal, expected', - [ - (tpi, None), - (tpds, None), - (tps, TFloatSeq('Interp=Step;[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02]') / 3600 / 24), - (tpss, TFloatSeqSet('Interp=Step;{[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02],' - '[0@2019-09-03, 0@2019-09-05]}') / 3600 / 24), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - ) - def test_speed(self, temporal, expected): - assert temporal.speed() == expected + # @pytest.mark.parametrize( + # 'temporal, expected', + # [ + # (tpi, None), + # (tpds, None), + # (tps, TFloatSeq('Interp=Step;[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02]') / 3600 / 24), + # (tpss, TFloatSeqSet('Interp=Step;{[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02],' + # '[0@2019-09-03, 0@2019-09-05]}') / 3600 / 24), + # ], + # ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + # ) + # def test_speed(self, temporal, expected): + # assert temporal.speed() == expected @pytest.mark.parametrize( 'temporal, expected', @@ -963,6 +960,21 @@ def test_is_simple(self, temporal, expected): def test_bearing(self, temporal, expected): assert temporal.bearing(Point(3,3)).to_degrees() == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, None), + (tpds, 45), + (tps, 45), + (tpss, None), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_direction(self, temporal, expected): + res = temporal.direction() + result = np.rad2deg(res) if res is not None else None + assert result == expected + @pytest.mark.parametrize( 'temporal, expected', [ @@ -1082,6 +1094,59 @@ class TestTGeomPointTransformations(TestTGeomPoint): tpss_l = TGeomPointSeqSet('Interp=Step;{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],' '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + @pytest.mark.parametrize( + 'temporal, expected', + [ + (TGeomPointInst('Point(1 1)@2019-09-01'), tpi), + (TGeomPointSeq('{Point(1 1)@2019-09-01}'), tpi), + (TGeomPointSeq('[Point(1 1)@2019-09-01]'), tpi), + (TGeomPointSeqSet('{[Point(1 1)@2019-09-01]}'), tpi), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_to_instant(self, temporal, expected): + temp = temporal.to_instant() + assert isinstance(temp, TGeomPointInst) + assert temp == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (TGeomPointInst('Point(1 1)@2019-09-01'), + TGeomPointSeq('[Point(1 1)@2019-09-01]')), + (TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}'), + TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}')), + (TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]'), + TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), + (TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}'), + TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_to_sequence(self, temporal, expected): + temp = temporal.to_sequence() + assert isinstance(temp, TGeomPointSeq) + assert temp == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (TGeomPointInst('Point(1 1)@2019-09-01'), + TGeomPointSeqSet('{[Point(1 1)@2019-09-01]}')), + (TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}'), + TGeomPointSeqSet('{[Point(1 1)@2019-09-01], [Point(2 2)@2019-09-02]}')), + (TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]'), + TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}')), + (TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}'), + TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_to_sequenceset(self, temporal, expected): + temp = temporal.to_sequenceset() + assert isinstance(temp, TGeomPointSeqSet) + assert temp == expected + @pytest.mark.parametrize( 'temporal, interpolation, expected', [ @@ -1111,7 +1176,7 @@ class TestTGeomPointTransformations(TestTGeomPoint): TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02), [Point(2 2)@2019-09-02]}')), (tpss_l, TInterpolation.LINEAR, TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02),' - '[Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1 )@2019-09-05]}')), + '[Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}')), ], ids=['Instant to discrete', 'Discrete Sequence to discrete', 'Sequence to discrete', 'SequenceSet to discrete', 'Instant to step', 'Discrete Sequence to step', 'Sequence to step', 'SequenceSet to step', @@ -1185,57 +1250,42 @@ def test_shift_tscale(self): '[Point(1 1)@2019-09-05 01:00:00, Point(1 1)@2019-09-05 02:00:00]}') @pytest.mark.parametrize( - 'temporal, expected', - [ - (TGeomPointInst('Point(1 1)@2019-09-01'), tpi), - (TGeomPointSeq('{Point(1 1)@2019-09-01}'), tpi), - (TGeomPointSeq('[Point(1 1)@2019-09-01]'), tpi), - (TGeomPointSeqSet('{[Point(1 1)@2019-09-01]}'), tpi), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - ) - def test_to_instant(self, temporal, expected): - temp = temporal.to_instant() - assert isinstance(temp, TGeomPointInst) - assert temp == expected - - @pytest.mark.parametrize( - 'temporal, expected', - [ - (TGeomPointInst('Point(1 1)@2019-09-01'), - TGeomPointSeq('[Point(1 1)@2019-09-01]')), - (TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}'), - TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}')), - (TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]'), - TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), - (TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}'), - TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + 'tpoint, delta, expected', + [(tpi, timedelta(days=4), TGeomPointInst('Point(1 1)@2019-09-01')), + (tpi, timedelta(hours=12), TGeomPointInst('Point(1 1)@2019-09-01')), + (tpds, timedelta(days=4), TGeomPointSeq('{Point(1 1)@2019-09-01}')), + (tpds, timedelta(hours=12), TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}')), + (tps, timedelta(days=4), TGeomPointSeq('{Point(1 1)@2019-09-01}')), + (tps, timedelta(hours=12), TGeomPointSeq('{Point(1 1)@2019-09-01, Point(1.5 1.5)@2019-09-01 12:00:00, Point(2 2)@2019-09-02}')), + (tpss, timedelta(days=4), + TGeomPointSeq('{Point(1 1)@2019-09-01,Point(1 1)@2019-09-05}')), + (tpss, timedelta(hours=12), + TGeomPointSeq('{Point(1 1)@2019-09-01, Point(1.5 1.5)@2019-09-01 12:00:00,' + 'Point(2 2)@2019-09-02, Point(1 1)@2019-09-03, Point(1 1)@2019-09-03 12:00:00, ' + 'Point(1 1)@2019-09-04, Point(1 1)@2019-09-04 12:00:00, Point(1 1)@2019-09-05}')), + ], + ids=['Instant days', 'Instant hours', + 'Discrete Sequence days', 'Discrete Sequence hours', + 'Sequence days', 'Sequence hours', + 'Sequence Set days', 'Sequence Set hours' + ] ) - def test_to_sequence(self, temporal, expected): - temp = temporal.to_sequence() - assert isinstance(temp, TGeomPointSeq) - assert temp == expected + def test_temporal_sample(self, tpoint, delta, expected): + assert tpoint.temporal_sample(delta) == expected @pytest.mark.parametrize( - 'temporal, expected', - [ - (TGeomPointInst('Point(1 1)@2019-09-01'), - TGeomPointSeqSet('{[Point(1 1)@2019-09-01]}')), - (TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}'), - TGeomPointSeqSet('{[Point(1 1)@2019-09-01], [Point(2 2)@2019-09-02]}')), - (TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]'), - TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}')), - (TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}'), - TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}')), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + 'tpoint, delta, expected', + [(tps, timedelta(days=4), None), + (tps, timedelta(hours=12), None), + (tpss, timedelta(days=4), None), + (tpss, timedelta(hours=12), + TGeomPointSeq('[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]')), + ], + ids=['Sequence days', 'Sequence hours', + 'Sequence Set days', 'Sequence Set hours'] ) - def test_to_sequenceset(self, temporal, expected): - temp = temporal.to_sequenceset() - assert isinstance(temp, TGeomPointSeqSet) - assert temp == expected + def test_stops(self, tpoint, delta, expected): + assert tpoint.stops(0.0, delta) == expected @pytest.mark.parametrize( 'temporal, expected', diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index 28a29281..7dadfdf1 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -5,8 +5,10 @@ import pytest -from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, TFloat, TFloatInst, TFloatSeq, TFloatSeqSet, TInt, \ - TIntInst, TIntSeq, TIntSeqSet, TInterpolation, TBox, TimestampSet, Period, PeriodSet +from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, \ + TFloat, TFloatInst, TFloatSeq, TFloatSeqSet, \ + TInt, TIntInst, TIntSeq, TIntSeqSet, \ + TInterpolation, TBox, TimestampSet, Period, PeriodSet from tests.conftest import TestPyMEOS @@ -340,12 +342,47 @@ def test_as_mfjson(self, temporal, expected): assert temporal.as_mfjson() == expected +class TestTIntConversions(TestTInt): + tii = TIntInst('1@2019-09-01') + tids = TIntSeq('{1@2019-09-01, 2@2019-09-02}') + tis = TIntSeq('[1@2019-09-01, 2@2019-09-02]') + tiss = TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}') + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tii, TFloatInst('1@2019-09-01')), + (tids, TFloatSeq('{1@2019-09-01,2@2019-09-02}')), + (tis, TFloatSeq('Interp=Step;[1@2019-09-01,2@2019-09-02]')), + (tiss, TFloatSeq('Interp=Step;{[1@2019-09-01,2@2019-09-02],[1@2019-09-03,1@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Step Sequence', 'Step Sequence Set'] + ) + def test_to_tfloat(self, temporal, expected): + temp = temporal.to_tfloat() + assert isinstance(temp, TFloat) + assert temp == expected + + class TestTIntAccessors(TestTInt): tii = TIntInst('1@2019-09-01') tids = TIntSeq('{1@2019-09-01, 2@2019-09-02}') tis = TIntSeq('[1@2019-09-01, 2@2019-09-02]') tiss = TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}') + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tii, TBox('TBOXINT XT([1,1],[2019-09-01, 2019-09-01])')), + (tids, TBox('TBOXINT XT([1,2],[2019-09-01, 2019-09-02])')), + (tis, TBox('TBOXINT XT([1,2],[2019-09-01, 2019-09-02])')), + (tiss, TBox('TBOXINT XT([1,2],[2019-09-01, 2019-09-05])')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_bounding_box(self, temporal, expected): + assert temporal.bounding_box() == expected + @pytest.mark.parametrize( 'temporal, expected', [ @@ -385,6 +422,32 @@ def test_value_set(self, temporal, expected): def test_values(self, temporal, expected): assert temporal.values() == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tii, intrange(1, 1, True, True)), + (tids, intrange(1, 2, True, True)), + (tis, intrange(1, 2, True, True)), + (tiss, intrange(1, 2, True, True)), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_value_range(self, temporal, expected): + assert temporal.value_range() == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tii, [intrange(1, 1, True, True)]), + (tids, [intrange(1, 2, True, True)]), + (tis, [intrange(1, 2, True, True)]), + (tiss, [intrange(1, 2, True, True)]), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_value_ranges(self, temporal, expected): + assert temporal.value_ranges() == expected + @pytest.mark.parametrize( 'temporal, expected', [ @@ -558,27 +621,27 @@ def test_end_instant(self, temporal, expected): 'temporal, expected', [ (tii, tii), - (tids, TIntInst('2@2019-09-02')), - (tis, TIntInst('2@2019-09-02')), - (tiss, TIntInst('2@2019-09-02')), + (tids, tii), + (tis, tii), + (tiss, tii), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_max_instant(self, temporal, expected): - assert temporal.max_instant() == expected + def test_min_instant(self, temporal, expected): + assert temporal.min_instant() == expected @pytest.mark.parametrize( 'temporal, expected', [ (tii, tii), - (tids, tii), - (tis, tii), - (tiss, tii), + (tids, TIntInst('2@2019-09-02')), + (tis, TIntInst('2@2019-09-02')), + (tiss, TIntInst('2@2019-09-02')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_min_instant(self, temporal, expected): - assert temporal.min_instant() == expected + def test_max_instant(self, temporal, expected): + assert temporal.max_instant() == expected @pytest.mark.parametrize( 'temporal, n, expected', @@ -692,6 +755,23 @@ def test_timestamps(self, temporal, expected): def test_segments(self, temporal, expected): assert temporal.segments() == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tii, 440045287), + (tids, 3589664982), + (tis, 3589664982), + (tiss, 205124107) + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_hash(self, temporal, expected): + assert hash(temporal) == expected + + def test_value_timestamp(self): + assert self.tii.value() == 1 + assert self.tii.timestamp() == datetime(year=2019, month=9, day=1, tzinfo=timezone.utc) + @pytest.mark.parametrize( 'temporal, expected', [ @@ -704,31 +784,42 @@ def test_lower_upper_inc(self, temporal, expected): assert temporal.lower_inc() == expected assert temporal.upper_inc() == expected + def test_sequenceset_sequence_functions(self): + tiss1 =TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],' + '[1@2019-09-03, 1@2019-09-05], [3@2019-09-06]}') + assert tiss1.num_sequences() == 3 + assert tiss1.start_sequence() == TIntSeq('[1@2019-09-01, 2@2019-09-02]') + assert tiss1.end_sequence() == TIntSeq('[3@2019-09-06]') + assert tiss1.sequence_n(1) == TIntSeq('[1@2019-09-03, 1@2019-09-05]') + assert tiss1.sequences() == [TIntSeq('[1@2019-09-01, 2@2019-09-02]'), + TIntSeq('[1@2019-09-03, 1@2019-09-05]'), + TIntSeq('[3@2019-09-06]')] + @pytest.mark.parametrize( 'temporal, expected', [ - (tii, 440045287), - (tids, 3589664982), - (tis, 3589664982), - (tiss, 205124107) + (tii, 0), + (tids, 0), + (tis, 86400000000), + (tiss, 259200000000), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_hash(self, temporal, expected): - assert hash(temporal) == expected + def test_integral(self, temporal, expected): + assert temporal.integral() == expected @pytest.mark.parametrize( 'temporal, expected', [ - (tii, TBox('TBOX XT([1,1],[2019-09-01, 2019-09-01])')), - (tids, TBox('TBOX XT([1,2],[2019-09-01, 2019-09-02])')), - (tis, TBox('TBOX XT([1,2],[2019-09-01, 2019-09-02])')), - (tiss, TBox('TBOX XT([1,2],[2019-09-01, 2019-09-05])')), + (tii, 1), + (tids, 1.5), + (tis, 1), + (tiss, 1), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_bounding_box(self, temporal, expected): - assert temporal.bounding_box() == expected + def test_time_weighted_average(self, temporal, expected): + assert temporal.time_weighted_average() == expected class TestTIntTransformations(TestTInt): @@ -737,6 +828,9 @@ class TestTIntTransformations(TestTInt): tis = TIntSeq('[1@2019-09-01, 2@2019-09-02]') tiss = TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}') + tis_d = TIntSeq('[1@2019-09-01]') + tiss_d = TIntSeqSet('{[1@2019-09-01],[2@2019-09-03]}') + @pytest.mark.parametrize( 'temporal, expected', [ @@ -790,6 +884,30 @@ def test_to_sequenceset(self, temporal, expected): assert isinstance(temp, TIntSeqSet) assert temp == expected + @pytest.mark.parametrize( + 'temporal, interpolation, expected', + [ + (tii, TInterpolation.DISCRETE, + TIntSeq('{1@2019-09-01}')), + (tids, TInterpolation.DISCRETE, tids), + (tis_d, TInterpolation.DISCRETE, + TIntSeq('{1@2019-09-01}')), + (tiss_d, TInterpolation.DISCRETE, + TIntSeq('{1@2019-09-01,2@2019-09-03}')), + + (tii, TInterpolation.STEPWISE, + TIntSeq('[1@2019-09-01]')), + (tids, TInterpolation.STEPWISE, + TIntSeqSet('{[1@2019-09-01], [2@2019-09-02]}')), + (tis, TInterpolation.STEPWISE, tis), + (tiss, TInterpolation.STEPWISE, tiss), + ], + ids=['Instant to discrete', 'Discrete Sequence to discrete', 'Sequence to discrete', 'SequenceSet to discrete', + 'Instant to step', 'Discrete Sequence to step', 'Sequence to step', 'SequenceSet to step'] + ) + def test_set_interpolation(self, temporal, interpolation, expected): + assert temporal.set_interpolation(interpolation) == expected + @pytest.mark.parametrize( 'tint, delta, expected', [(tii, timedelta(days=4), TIntInst('1@2019-09-05')), @@ -854,32 +972,42 @@ def test_shift_tscale(self): TIntSeqSet('{[1@2019-09-05 00:00:00, 2@2019-09-05 00:30:00],' '[1@2019-09-05 01:00:00, 1@2019-09-05 02:00:00]}') + @pytest.mark.parametrize( + 'tint, delta, expected', + [(tii, timedelta(days=4), TIntInst('1@2019-09-01')), + (tii, timedelta(hours=12), TIntInst('1@2019-09-01')), + (tids, timedelta(days=4), TIntSeq('{1@2019-09-01}')), + (tids, timedelta(hours=12), TIntSeq('{1@2019-09-01, 2@2019-09-02}')), + (tis, timedelta(days=4), TIntSeq('{1@2019-09-01}')), + (tis, timedelta(hours=12), TIntSeq('{1@2019-09-01, 1@2019-09-01 12:00:00, 2@2019-09-02}')), + (tiss, timedelta(days=4), + TIntSeq('{1@2019-09-01,1@2019-09-05}')), + (tiss, timedelta(hours=12), + TIntSeq('{1@2019-09-01, 1@2019-09-01 12:00:00, 2@2019-09-02,' + '1@2019-09-03, 1@2019-09-03 12:00:00, 1@2019-09-04, ' + '1@2019-09-04 12:00:00, 1@2019-09-05}')), + ], + ids=['Instant days', 'Instant hours', + 'Discrete Sequence days', 'Discrete Sequence hours', + 'Sequence days', 'Sequence hours', + 'Sequence Set days', 'Sequence Set hours'] + ) + def test_temporal_sample(self, tint, delta, expected): + assert tint.temporal_sample(delta) == expected + @pytest.mark.parametrize( 'temporal, expected', [ (tii, TFloatInst('1@2019-09-01')), (tids, TFloatSeq('{1@2019-09-01, 2@2019-09-02}')), (tis, TFloatSeq('Interp=Step;[1@2019-09-01, 2@2019-09-02]')), - # (tiss, TFloatSeqSet('Interp=Step;{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), + (tiss, TFloatSeqSet('Interp=Step;{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), ], - ids=['Instant', 'Discrete Sequence', 'Sequence'] #, 'SequenceSet'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_to_tfloat(self, temporal, expected): assert temporal.to_tfloat() == expected - @pytest.mark.parametrize( - 'temporal, expected', - [ - (tii, intrange(1, 1, True, True)), - (tids, intrange(1, 2, True, True)), - (tis, intrange(1, 2, True, True)), - (tiss, intrange(1, 2, True, True)), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - ) - def test_to_intrange(self, temporal, expected): - assert temporal.to_intrange() == expected - class TestTIntModifications(TestTInt): tii = TIntInst('1@2019-09-01') @@ -966,7 +1094,6 @@ class TestTIntMathematicalOperations(TestTInt): tis = TIntSeq('[1@2019-09-01, 2@2019-09-02]') tiss = TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}') tintarg = TIntSeq('[2@2019-09-01, 1@2019-09-02, 1@2019-09-03]') - tfloatarg = TFloatSeq('[2@2019-09-01, 1@2019-09-02, 1@2019-09-03]') @pytest.mark.parametrize( 'temporal, argument, expected', @@ -975,13 +1102,8 @@ class TestTIntMathematicalOperations(TestTInt): (tids, tintarg, TIntSeq('{3@2019-09-01, 3@2019-09-02}')), (tis, tintarg, TIntSeq('[3@2019-09-01, 3@2019-09-02]')), (tiss, tintarg, TIntSeqSet('{[3@2019-09-01, 3@2019-09-02],[2@2019-09-03]}')), - (tii, tfloatarg, TFloatInst('3@2019-09-01')), - (tids, tfloatarg, TFloatSeq('{3@2019-09-01, 3@2019-09-02}')), - (tis, tfloatarg, TFloatSeqSet('{[3@2019-09-01, 2@2019-09-02),[3@2019-09-02]}')), - (tiss, tfloatarg, TFloatSeqSet('{[3@2019-09-01, 2@2019-09-02),[3@2019-09-02],[2@2019-09-03]}')), ], - ids=['Instant TInt', 'Discrete Sequence TInt', 'Sequence TInt', 'SequenceSet TInt', - 'Instant TFloat', 'Discrete Sequence TFloat', 'Sequence TFloat', 'SequenceSet TFloat'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_temporal_add_temporal(self, temporal, argument, expected): assert temporal.add(argument) == expected @@ -999,13 +1121,9 @@ def test_temporal_add_temporal(self, temporal, argument, expected): ) def test_temporal_add_int_float(self, temporal, argument, expected): assert temporal.add(argument) == expected - assert temporal.add(float(argument)) == expected.to_tfloat() assert temporal.radd(argument) == expected - assert temporal.radd(float(argument)) == expected.to_tfloat() assert (temporal + argument) == expected - assert (temporal + float(argument)) == expected.to_tfloat() assert (argument + temporal) == expected - assert (float(argument) + temporal) == expected.to_tfloat() @pytest.mark.parametrize( 'temporal, argument, expected', @@ -1014,13 +1132,8 @@ def test_temporal_add_int_float(self, temporal, argument, expected): (tids, tintarg, TIntSeq('{-1@2019-09-01, 1@2019-09-02}')), (tis, tintarg, TIntSeq('[-1@2019-09-01, 1@2019-09-02]')), (tiss, tintarg, TIntSeqSet('{[-1@2019-09-01, 1@2019-09-02],[0@2019-09-03]}')), - (tii, tfloatarg, TFloatInst('-1@2019-09-01')), - (tids, tfloatarg, TFloatSeq('{-1@2019-09-01, 1@2019-09-02}')), - (tis, tfloatarg, TFloatSeqSet('{[-1@2019-09-01, 0@2019-09-02),[1@2019-09-02]}')), - (tiss, tfloatarg, TFloatSeqSet('{[-1@2019-09-01, 0@2019-09-02),[1@2019-09-02],[0@2019-09-03]}')) ], - ids=['Instant TInt', 'Discrete Sequence TInt', 'Sequence TInt', 'SequenceSet TInt', - 'Instant TFloat', 'Discrete Sequence TFloat', 'Sequence TFloat', 'SequenceSet TFloat'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_temporal_sub_temporal(self, temporal, argument, expected): assert temporal.sub(argument) == expected @@ -1038,13 +1151,9 @@ def test_temporal_sub_temporal(self, temporal, argument, expected): ) def test_temporal_sub_int_float(self, temporal, argument, expected): assert temporal.sub(argument) == expected - assert temporal.sub(float(argument)) == expected.to_tfloat() assert temporal.rsub(argument) == -1 * expected - assert temporal.rsub(float(argument)) == (-1 * expected).to_tfloat() assert (temporal - argument) == expected - assert (temporal - float(argument)) == expected.to_tfloat() assert (argument - temporal) == - 1 * expected - assert (float(argument) - temporal) == (- 1 * expected).to_tfloat() @pytest.mark.parametrize( 'temporal, argument, expected', @@ -1053,13 +1162,8 @@ def test_temporal_sub_int_float(self, temporal, argument, expected): (tids, tintarg, TIntSeq('{2@2019-09-01, 2@2019-09-02}')), (tis, tintarg, TIntSeq('[2@2019-09-01, 2@2019-09-02]')), (tiss, tintarg, TIntSeqSet('{[2@2019-09-01, 2@2019-09-02],[1@2019-09-03]}')), - (tii, tfloatarg, TFloatInst('2@2019-09-01')), - (tids, tfloatarg, TFloatSeq('{2@2019-09-01, 2@2019-09-02}')), - (tis, tfloatarg, TFloatSeqSet('{[2@2019-09-01, 1@2019-09-02), [2@2019-09-02]}')), - (tiss, tfloatarg, TFloatSeqSet('{[2@2019-09-01, 1@2019-09-02), [2@2019-09-02],[1@2019-09-03]}')), ], - ids=['Instant TInt', 'Discrete Sequence TInt', 'Sequence TInt', 'SequenceSet TInt', - 'Instant TFloat', 'Discrete Sequence TFloat', 'Sequence TFloat', 'SequenceSet TFloat'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_temporal_mul_temporal(self, temporal, argument, expected): assert temporal.mul(argument) == expected @@ -1089,13 +1193,9 @@ def test_temporal_mul_temporal(self, temporal, argument, expected): ) def test_temporal_mul_int_float(self, temporal, argument, expected): assert temporal.mul(argument) == expected - assert temporal.mul(float(argument)) == expected.to_tfloat() assert temporal.rmul(argument) == expected - assert temporal.rmul(float(argument)) == expected.to_tfloat() assert (temporal * argument) == expected - assert (temporal * float(argument)) == expected.to_tfloat() assert (argument * temporal) == expected - assert (float(argument) * temporal) == expected.to_tfloat() @pytest.mark.parametrize( 'temporal, argument, expected', @@ -1104,13 +1204,8 @@ def test_temporal_mul_int_float(self, temporal, argument, expected): (tids, tintarg, TIntSeq('{0@2019-09-01, 2@2019-09-02}')), (tis, tintarg, TIntSeq('[0@2019-09-01, 2@2019-09-02]')), (tiss, tintarg, TIntSeqSet('{[0@2019-09-01, 2@2019-09-02],[1@2019-09-03]}')), - (tii, tfloatarg, TFloatInst('0.5@2019-09-01')), - (tids, tfloatarg, TFloatSeq('{0.5@2019-09-01, 2@2019-09-02}')), - (tis, tfloatarg, TFloatSeqSet('{[0.5@2019-09-01, 1@2019-09-02), [2@2019-09-02]}')), - (tiss, tfloatarg, TFloatSeqSet('{[0.5@2019-09-01, 1@2019-09-02), [2@2019-09-02],[1@2019-09-03]}')) ], - ids=['Instant TInt', 'Discrete Sequence TInt', 'Sequence TInt', 'SequenceSet TInt', - 'Instant TFloat', 'Discrete Sequence TFloat', 'Sequence TFloat', 'SequenceSet TFloat'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_temporal_div_temporal(self, temporal, argument, expected): assert temporal.div(argument) == expected @@ -1123,23 +1218,13 @@ def test_temporal_div_temporal(self, temporal, argument, expected): (tids, 1, tids), (tis, 1, tis), (tiss, 1, tiss), - (tii, 1.0, TFloatInst('1@2019-09-01')), - (tids, 1.0, TFloatSeq('{1@2019-09-01, 2@2019-09-02}')), - # (tis, 1.0, TFloatSeq('[1@2019-09-01, 2@2019-09-02]')), - # (tiss, 1.0, TFloatSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), (tii, 2, TIntInst('0@2019-09-01')), (tids, 2, TIntSeq('{0@2019-09-01, 1@2019-09-02}')), (tis, 2, TIntSeq('[0@2019-09-01, 1@2019-09-02]')), (tiss, 2, TIntSeqSet('{[0@2019-09-01, 1@2019-09-02],[0@2019-09-03, 0@2019-09-05]}')), - (tii, 2.0, TFloatInst('0.5@2019-09-01')), - (tids, 2.0, TFloatSeq('{0.5@2019-09-01, 1@2019-09-02}')), - (tis, 2.0, TFloatSeq('Interp=Step;[0.5@2019-09-01, 1@2019-09-02]')), - (tiss, 2.0, TFloatSeqSet('Interp=Step;{[0.5@2019-09-01, 1@2019-09-02],[0.5@2019-09-03, 0.5@2019-09-05]}')), ], ids=['Instant 1', 'Discrete Sequence 1', 'Sequence 1', 'SequenceSet 1', - 'Instant 1.0', 'Discrete Sequence 1.0', # 'Sequence 1.0', 'SequenceSet 1.0', - 'Instant 2', 'Discrete Sequence 2', 'Sequence 2', 'SequenceSet 2', - 'Instant 2.0', 'Discrete Sequence 2.0', 'Sequence 2.0', 'SequenceSet 2.0'] + 'Instant 2', 'Discrete Sequence 2', 'Sequence 2', 'SequenceSet 2'] ) def test_temporal_div_int_float (self, temporal, argument, expected): assert temporal.div(argument) == expected @@ -1157,16 +1242,18 @@ def test_abs(self, temporal): @pytest.mark.parametrize( 'temporal, expected', [ - # (tii, TIntInst('1@2019-09-01')), + (tii, None), (tids, TIntSeq('{1@2019-09-01, 1@2019-09-02}')), (tis, TIntSeq('[1@2019-09-01, 1@2019-09-02)')), (tiss, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[0@2019-09-03, 0@2019-09-05)}')), ], - ids=[# 'Instant', - 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_delta_value(self, temporal, expected): - assert temporal.delta_value() == expected + if expected is None: + assert temporal.delta_value() is None + else: + assert temporal.delta_value() == expected class TestTIntRestrictors(TestTInt): @@ -1187,47 +1274,77 @@ class TestTIntRestrictors(TestTInt): (tii, timestamp_set, TIntInst('1@2019-09-01')), (tii, period, TIntInst('1@2019-09-01')), (tii, period_set, TIntInst('1@2019-09-01')), - (tii, 1, TIntInst('1@2019-09-01')), - (tii, 2, None), - # (tii, [1,2], TIntInst('1@2019-09-01')), (tids, timestamp, TIntSeq('{1@2019-09-01}')), (tids, timestamp_set, TIntSeq('{1@2019-09-01}')), (tids, period, TIntSeq('{1@2019-09-01, 2@2019-09-02}')), (tids, period_set, TIntSeq('{1@2019-09-01, 2@2019-09-02}')), - (tids, 1, TIntSeq('{1@2019-09-01}')), - (tids, 2, TIntSeq('{2@2019-09-02}')), - # (tids, [1,2], TIntSeq('{2@2019-09-02}')), (tis, timestamp, TIntSeq('[1@2019-09-01]')), (tis, timestamp_set, TIntSeq('{1@2019-09-01}')), (tis, period, TIntSeq('[1@2019-09-01, 2@2019-09-02]')), (tis, period_set, TIntSeq('[1@2019-09-01, 2@2019-09-02]')), - (tis, 1, TIntSeq('[1@2019-09-01, 1@2019-09-02)')), - (tis, 2, TIntSeq('[2@2019-09-02]')), - # (tis, [1,2], TIntSeq('[2@2019-09-02]')), (tiss, timestamp, TIntSeqSet('[1@2019-09-01]')), (tiss, timestamp_set, TIntSeq('{1@2019-09-01, 1@2019-09-03}')), (tiss, period, TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), (tiss, period_set, TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), - (tiss, 1, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), - (tiss, 2, TIntSeqSet('{[2@2019-09-02]}')), - # (tiss, [1,2], TIntSeqSet('{[2@2019-09-02]}')) ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', 'Instant-1', 'Instant-2', # 'Instant-[1,2]', + 'Instant-PeriodSet', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', - 'Discrete Sequence-1', 'Discrete Sequence-2', # 'Discrete Sequence-[1,2]', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', 'Sequence-1', 'Sequence-2', # 'Sequence-[1,2]', - 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', - 'SequenceSet-PeriodSet', 'SequenceSet-1', 'SequenceSet-2', # 'SequenceSet-[1,2]' + 'Sequence-PeriodSet', + 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', + 'SequenceSet-Period', 'SequenceSet-PeriodSet', ] ) - def test_at(self, temporal, restrictor, expected): + def test_at_time(self, temporal, restrictor, expected): + assert temporal.at(restrictor) == expected + + @pytest.mark.parametrize( + 'temporal, restrictor, expected', + [ + (tii, 1, TIntInst('1@2019-09-01')), + (tii, 2, None), + (tii, intrange(1, 1, True, True), TIntInst('1@2019-09-01')), + # (tii, [1,2], TIntInst('1@2019-09-01')), + # (tii, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # TIntInst('1@2019-09-01')), + + (tids, 1, TIntSeq('{1@2019-09-01}')), + (tids, 2, TIntSeq('{2@2019-09-02}')), + (tids, intrange(1, 1, True, True), TIntSeq('{1@2019-09-01}')), + # (tids, [1,2], TIntSeq('{2@2019-09-02}')), + # (tids, [intrange(1, 1, True, True), tids), + + (tis, 1, TIntSeq('[1@2019-09-01, 1@2019-09-02)')), + (tis, 2, TIntSeq('[2@2019-09-02]')), + (tis, intrange(1, 1, True, True), TIntSeq('[1@2019-09-01, 1@2019-09-02)')), + # (tis, [1,2], TIntSeq('[2@2019-09-02]')), + # (tis, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), + + (tiss, 1, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), + (tiss, 2, TIntSeqSet('{[2@2019-09-02]}')), + (tiss, intrange(1, 1, True, True), TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), + # (tiss, [1,2], TIntSeqSet('{[2@2019-09-02]}')) + # (tiss, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # tiss), + ], + ids=['Instant-1', 'Instant-2', 'Instant-Range', + # 'Instant-ValueList', 'Instant-RangeList', + 'Discrete Sequence-1', 'Discrete Sequence-2', 'Discrete Sequence-Range', + # 'Discrete Sequence-ValueList', 'Discrete Sequence-RangeList', + 'Sequence-1', 'Sequence-2', 'Sequence-Range', + # 'Sequence-ValueList', 'Sequence-RangeList', + 'SequenceSet-1', 'SequenceSet-2', 'SequenceSet-Range', + # 'SequenceSet-ValueList', 'SequenceSet-RangeList' + ] + ) + def test_at_values(self, temporal, restrictor, expected): assert temporal.at(restrictor) == expected @pytest.mark.parametrize( @@ -1264,25 +1381,16 @@ def test_at_max(self, temporal, expected): (tii, timestamp_set, None), (tii, period, None), (tii, period_set, None), - (tii, 1, None), - (tii, 2, TIntInst('1@2019-09-01')), - # (tii, [1,2], None), (tids, timestamp, TIntSeq('{2@2019-09-02}')), (tids, timestamp_set, TIntSeq('{2@2019-09-02}')), (tids, period, None), (tids, period_set, None), - (tids, 1, TIntSeq('{2@2019-09-02}')), - (tids, 2, TIntSeq('{1@2019-09-01}')), - # (tids, [1,2], tids), (tis, timestamp, TIntSeqSet('{(1@2019-09-01, 2@2019-09-02]}')), (tis, timestamp_set, TIntSeqSet('{(1@2019-09-01, 2@2019-09-02]}')), (tis, period, None), (tis, period_set, None), - (tis, 1, TIntSeqSet('{[2@2019-09-02]}')), - (tis, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02)}')), - # (tis, [1,2], tis), (tiss, timestamp, TIntSeqSet('{(1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), @@ -1290,22 +1398,62 @@ def test_at_max(self, temporal, expected): TIntSeqSet('{(1@2019-09-01, 2@2019-09-02],(1@2019-09-03, 1@2019-09-05]}')), (tiss, period, TIntSeqSet('{[1@2019-09-03, 1@2019-09-05]}')), (tiss, period_set, None), - (tiss, 1, TIntSeqSet('{[2@2019-09-02]}')), - (tiss, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), - # (tiss, [1,2], tiss) ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', 'Instant-1', 'Instant-2', # 'Instant-[1,2]', + 'Instant-PeriodSet', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', - 'Discrete Sequence-1', 'Discrete Sequence-2', # 'Discrete Sequence-[1,2]', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', 'Sequence-1', 'Sequence-2', # 'Sequence-[1,2]', + 'Sequence-PeriodSet', 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', - 'SequenceSet-PeriodSet', 'SequenceSet-1', 'SequenceSet-2', # 'SequenceSet-[1,2]' + 'SequenceSet-PeriodSet', + ] + ) + def test_minus_time(self, temporal, restrictor, expected): + assert temporal.minus(restrictor) == expected + + @pytest.mark.parametrize( + 'temporal, restrictor, expected', + [ + (tii, 1, None), + (tii, 2, TIntInst('1@2019-09-01')), + (tii, intrange(1, 1, True, True), None), + # (tii, [1,2], None), + # (tii, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # None), + + (tids, 1, TIntSeq('{2@2019-09-02}')), + (tids, 2, TIntSeq('{1@2019-09-01}')), + (tids, intrange(1, 1, True, True), TIntSeq('{2@2019-09-02}')), + # (tids, [1,2], tids), + # (tids, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # None), + + (tis, 1, TIntSeqSet('{[2@2019-09-02]}')), + (tis, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02)}')), + (tis, intrange(1, 1, True, True), TIntSeqSet('{[2@2019-09-02]}')), + # (tis, [1,2], tis), + # (tis, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # None), + + (tiss, 1, TIntSeqSet('{[2@2019-09-02]}')), + (tiss, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), + (tis, intrange(1, 1, True, True), TIntSeqSet('{[2@2019-09-02]}')), + # (tiss, [1,2], tiss) + # (tiss, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # None), + ], + ids=['Instant-1', 'Instant-2', 'Instant-Range', + # 'Instant-ValueList', 'Instant-RangeList', + 'Discrete Sequence-1', 'Discrete Sequence-2', 'Discrete Sequence-Range', + # 'Discrete Sequence-ValueList', 'Discrete Sequence-RangeList', + 'Sequence-1', 'Sequence-2', 'Sequence-Range', + # 'Sequence-ValueList', 'Sequence-RangeList', + 'SequenceSet-1', 'SequenceSet-2', 'SequenceSet-Range', + # 'SequenceSet-ValueList', 'SequenceSet-RangeList', ] ) - def test_minus(self, temporal, restrictor, expected): + def test_minus_values(self, temporal, restrictor, expected): assert temporal.minus(restrictor) == expected @pytest.mark.parametrize( @@ -1393,24 +1541,23 @@ class TestTIntTopologicalFunctions(TestTInt): tis = TIntSeq('[1@2019-09-01, 2@2019-09-02]') tiss = TIntSeqSet('{[1@2019-09-01, 2@2019-09-02], [1@2019-09-03, 1@2019-09-05]}') - # Problem in MEOS: the definition of TBox for tint should have an intspan ! - # @pytest.mark.parametrize( - # 'temporal, argument, expected', - # [ - # (tii, TIntInst('1@2019-09-02'), False), - # (tii, TIntSeq('(1@2019-09-01, 2@2019-09-02]'), True), - # (tids, TIntInst('1@2019-09-03'), False), - # (tids, TIntInst('2@2019-09-01'), True), - # (tis, TIntInst('1@2019-09-03'), False), - # (tis, TIntSeq('(2@2019-09-01, 3@2019-09-02]'), True), - # (tiss, TIntInst('1@2019-09-08'), False), - # (tiss, TIntSeq('(2@2019-09-01, 3@2019-09-02]'), True), - # ], - # ids=['Instant False', 'Instant True', 'Discrete Sequence False', 'Discrete Sequence True', - # 'Sequence False', 'Sequence True', 'Sequence Set False', 'Sequence Set True'] - # ) - # def test_is_adjacent(self, temporal, argument, expected): - # assert temporal.is_adjacent(argument) == expected + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tii, TIntInst('1@2019-09-02'), False), + (tii, TIntInst('2@2019-09-02'), True), + (tids, TIntInst('1@2019-09-03'), False), + (tids, TIntInst('3@2019-09-02'), True), + (tis, TIntInst('1@2019-09-03'), False), + (tis, TIntInst('3@2019-09-02'), True), + (tiss, TIntInst('1@2019-09-08'), False), + (tiss, TIntInst('3@2019-09-02'), True), + ], + ids=['Instant False', 'Instant True', 'Discrete Sequence False', 'Discrete Sequence True', + 'Sequence False', 'Sequence True', 'Sequence Set False', 'Sequence Set True'] + ) + def test_is_adjacent(self, temporal, argument, expected): + assert temporal.is_adjacent(argument) == expected @pytest.mark.parametrize( 'temporal, argument, expected', @@ -1773,7 +1920,7 @@ def test_ever_equal_always_not_equal(self, temporal, argument, expected): ) def test_always_less_ever_greater_or_equal(self, temporal, argument, expected): assert temporal.always_less(argument) == expected - # assert temporal.never_greater_or_equal(argument) == expected + assert temporal.never_greater_or_equal(argument) == expected assert temporal.ever_greater_or_equal(argument) == not_(expected) @pytest.mark.parametrize( diff --git a/pymeos/tests/main/ttext_test.py b/pymeos/tests/main/ttext_test.py index 22da37c7..20df2c0c 100644 --- a/pymeos/tests/main/ttext_test.py +++ b/pymeos/tests/main/ttext_test.py @@ -325,6 +325,19 @@ class TestTTextAccessors(TestTText): tts = TTextSeq('[AAA@2019-09-01, BBB@2019-09-02]') ttss = TTextSeqSet('{[AAA@2019-09-01, BBB@2019-09-02],[AAA@2019-09-03, AAA@2019-09-05]}') + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tti, Period('[2019-09-01, 2019-09-01]')), + (ttds, Period('[2019-09-01, 2019-09-02]')), + (tts, Period('[2019-09-01, 2019-09-02]')), + (ttss, Period('[2019-09-01, 2019-09-05]')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_bounding_box(self, temporal, expected): + assert temporal.bounding_box() == expected + @pytest.mark.parametrize( 'temporal, expected', [ @@ -416,18 +429,18 @@ def test_min_value(self, temporal, expected): def test_max_value(self, temporal, expected): assert temporal.max_value() == expected - # @pytest.mark.parametrize( - # 'temporal, expected', - # [ - # (tti, 'AAA'), - # (ttds, 'AAA'), - # (tts, 'AAA'), - # (ttss, 'AAA') - # ], - # ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - # ) - # def test_value_at_timestamp(self, temporal, expected): - # assert temporal.value_at_timestamp(datetime(2019, 9, 1)) == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tti, 'AAA'), + (ttds, 'AAA'), + (tts, 'AAA'), + (ttss, 'AAA') + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_value_at_timestamp(self, temporal, expected): + assert temporal.value_at_timestamp(datetime(2019, 9, 1)) == expected @pytest.mark.parametrize( 'temporal, expected', @@ -537,27 +550,27 @@ def test_end_instant(self, temporal, expected): 'temporal, expected', [ (tti, tti), - (ttds, TTextInst('BBB@2019-09-02')), - (tts, TTextInst('BBB@2019-09-02')), - (ttss, TTextInst('BBB@2019-09-02')), + (ttds, tti), + (tts, tti), + (ttss, tti), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_max_instant(self, temporal, expected): - assert temporal.max_instant() == expected + def test_min_instant(self, temporal, expected): + assert temporal.min_instant() == expected @pytest.mark.parametrize( 'temporal, expected', [ (tti, tti), - (ttds, tti), - (tts, tti), - (ttss, tti), + (ttds, TTextInst('BBB@2019-09-02')), + (tts, TTextInst('BBB@2019-09-02')), + (ttss, TTextInst('BBB@2019-09-02')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_min_instant(self, temporal, expected): - assert temporal.min_instant() == expected + def test_max_instant(self, temporal, expected): + assert temporal.max_instant() == expected @pytest.mark.parametrize( 'temporal, n, expected', @@ -671,18 +684,6 @@ def test_timestamps(self, temporal, expected): def test_segments(self, temporal, expected): assert temporal.segments() == expected - @pytest.mark.parametrize( - 'temporal, expected', - [ - (ttds, True), - (tts, True), - ], - ids=['Discrete Sequence', 'Sequence'] - ) - def test_lower_upper_inc(self, temporal, expected): - assert temporal.lower_inc() == expected - assert temporal.upper_inc() == expected - @pytest.mark.parametrize( 'temporal, expected', [ @@ -696,6 +697,33 @@ def test_lower_upper_inc(self, temporal, expected): def test_hash(self, temporal, expected): assert hash(temporal) == expected + def test_value_timestamp(self): + assert self.tti.value() == 'AAA' + assert self.tti.timestamp() == datetime(year=2019, month=9, day=1, tzinfo=timezone.utc) + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (ttds, True), + (tts, True), + ], + ids=['Discrete Sequence', 'Sequence'] + ) + def test_lower_upper_inc(self, temporal, expected): + assert temporal.lower_inc() == expected + assert temporal.upper_inc() == expected + + def test_sequenceset_sequence_functions(self): + ttss1 =TTextSeqSet('{[AAA@2019-09-01, BBB@2019-09-02],' + '[AAA@2019-09-03, AAA@2019-09-05], [CCC@2019-09-06]}') + assert ttss1.num_sequences() == 3 + assert ttss1.start_sequence() == TTextSeq('[AAA@2019-09-01, BBB@2019-09-02]') + assert ttss1.end_sequence() == TTextSeq('[CCC@2019-09-06]') + assert ttss1.sequence_n(1) == TTextSeq('[AAA@2019-09-03, AAA@2019-09-05]') + assert ttss1.sequences() == [TTextSeq('[AAA@2019-09-01, BBB@2019-09-02]'), + TTextSeq('[AAA@2019-09-03, AAA@2019-09-05]'), + TTextSeq('[CCC@2019-09-06]')] + class TestTTextTransformations(TestTText): tti = TTextInst('AAA@2019-09-01') @@ -820,6 +848,29 @@ def test_shift_tscale(self): TTextSeqSet('{[AAA@2019-09-05 00:00:00, BBB@2019-09-05 00:30:00],' '[AAA@2019-09-05 01:00:00, AAA@2019-09-05 02:00:00]}') + @pytest.mark.parametrize( + 'tint, delta, expected', + [(tti, timedelta(days=4), TTextInst('AAA@2019-09-01')), + (tti, timedelta(hours=12), TTextInst('AAA@2019-09-01')), + (ttds, timedelta(days=4), TTextSeq('{AAA@2019-09-01}')), + (ttds, timedelta(hours=12), TTextSeq('{AAA@2019-09-01, BBB@2019-09-02}')), + (tts, timedelta(days=4), TTextSeq('{AAA@2019-09-01}')), + (tts, timedelta(hours=12), TTextSeq('{AAA@2019-09-01, AAA@2019-09-01 12:00:00, BBB@2019-09-02}')), + (ttss, timedelta(days=4), + TTextSeq('{AAA@2019-09-01,AAA@2019-09-05}')), + (ttss, timedelta(hours=12), + TTextSeq('{AAA@2019-09-01, AAA@2019-09-01 12:00:00, BBB@2019-09-02,' + 'AAA@2019-09-03, AAA@2019-09-03 12:00:00, AAA@2019-09-04, ' + 'AAA@2019-09-04 12:00:00, AAA@2019-09-05}')), + ], + ids=['Instant days', 'Instant hours', + 'Discrete Sequence days', 'Discrete Sequence hours', + 'Sequence days', 'Sequence hours', + 'Sequence Set days', 'Sequence Set hours'] + ) + def test_temporal_sample(self, tint, delta, expected): + assert tint.temporal_sample(delta) == expected + class TestTTextModifications(TestTText): tti = TTextInst('AAA@2019-09-01') diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index c306c439..cfe3428a 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -214,7 +214,6 @@ 'floatspan_set_intspan', 'geoset_round', 'intspan_set_floatspan', - 'numspan_set_floatspan', 'period_tprecision', 'periodset_tprecision', 'period_shift_tscale', @@ -226,7 +225,9 @@ 'adjacent_bigintspan_bigint', 'adjacent_bigintspanset_bigint', 'adjacent_floatspan_float', + 'adjacent_floatspanset_float', 'adjacent_intspan_int', + 'adjacent_intspanset_int', 'adjacent_period_timestamp', 'adjacent_periodset_timestamp', 'adjacent_span_span', @@ -240,23 +241,33 @@ 'contained_float_floatspanset', 'contained_int_intset', 'contained_int_intspan', + 'contained_int_intspanset', 'contained_set_set', 'contained_span_span', 'contained_span_spanset', 'contained_spanset_span', 'contained_spanset_spanset', + 'contained_text_textset', 'contained_timestamp_period', + 'contained_timestamp_periodset', 'contained_timestamp_timestampset', + 'contains_bigintset_bigint', + 'contains_bigintspan_bigint', + 'contains_bigintspanset_bigint', + 'contains_floatset_float', 'contains_floatspan_float', 'contains_floatspanset_float', + 'contains_intset_int', 'contains_intspan_int', - 'contains_set_set', + 'contains_intspanset_int', 'contains_period_timestamp', 'contains_periodset_timestamp', + 'contains_set_set', 'contains_span_span', 'contains_span_spanset', 'contains_spanset_span', 'contains_spanset_spanset', + 'contains_textset_text', 'contains_timestampset_timestamp', 'overlaps_set_set', 'overlaps_span_span', @@ -693,26 +704,26 @@ 'tor_bool_tbool', 'tor_tbool_bool', 'tor_tbool_tbool', - 'add_float_tnumber', - 'add_int_tnumber', - 'add_tnumber_float', - 'add_tnumber_int', + 'add_float_tfloat', + 'add_int_tint', + 'add_tfloat_float', + 'add_tint_int', 'add_tnumber_tnumber', - 'div_float_tnumber', - 'div_int_tnumber', - 'div_tnumber_float', - 'div_tnumber_int', + 'div_float_tfloat', + 'div_int_tint', + 'div_tfloat_float', + 'div_tint_int', 'div_tnumber_tnumber', 'float_degrees', - 'mult_float_tnumber', - 'mult_int_tnumber', - 'mult_tnumber_float', - 'mult_tnumber_int', + 'mult_float_tfloat', + 'mult_int_tint', + 'mult_tfloat_float', + 'mult_tint_int', 'mult_tnumber_tnumber', - 'sub_float_tnumber', - 'sub_int_tnumber', - 'sub_tnumber_float', - 'sub_tnumber_int', + 'sub_float_tfloat', + 'sub_int_tint', + 'sub_tfloat_float', + 'sub_tint_int', 'sub_tnumber_tnumber', 'tfloat_round', 'tfloat_degrees', diff --git a/pymeos_cffi/pymeos_cffi/builder/build_helpers.py b/pymeos_cffi/pymeos_cffi/builder/build_helpers.py index dca6625b..60674ea2 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_helpers.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_helpers.py @@ -346,8 +346,6 @@ extern LWGEOM *lwgeom_from_gserialized(const GSERIALIZED *g); extern GSERIALIZED *gserialized_from_lwgeom(LWGEOM *geom, size_t *size); -extern static inline LWPOINT *lwgeom_as_lwpoint(const LWGEOM *lwgeom); - extern int32_t lwgeom_get_srid(const LWGEOM *geom); extern double lwpoint_get_x(const LWPOINT *point); diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index eae87162..7f6658b3 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -959,7 +959,6 @@ extern SpanSet *floatspanset_round(const SpanSet *ss, int maxdd); extern void floatspan_set_intspan(const Span *s1, Span *s2); extern Set *geoset_round(const Set *s, int maxdd); extern void intspan_set_floatspan(const Span *s1, Span *s2); -extern void numspan_set_floatspan(const Span *s1, Span *s2); extern Span *period_tprecision(const Span *s, const Interval *duration, TimestampTz torigin); extern SpanSet *periodset_tprecision(const SpanSet *ss, const Interval *duration, TimestampTz torigin); extern Span *period_shift_tscale(const Span *p, const Interval *shift, const Interval *duration); @@ -978,7 +977,9 @@ extern Set *timestampset_shift_tscale(const Set *ts, const Interval *shift, cons extern bool adjacent_bigintspan_bigint(const Span *s, int64 i); extern bool adjacent_bigintspanset_bigint(const SpanSet *ss, int64 i); extern bool adjacent_floatspan_float(const Span *s, double d); +extern bool adjacent_floatspanset_float(const SpanSet *ss, double d); extern bool adjacent_intspan_int(const Span *s, int i); +extern bool adjacent_intspanset_int(const SpanSet *ss, int i); extern bool adjacent_period_timestamp(const Span *p, TimestampTz t); extern bool adjacent_periodset_timestamp(const SpanSet *ps, TimestampTz t); extern bool adjacent_span_span(const Span *s1, const Span *s2); @@ -991,26 +992,35 @@ extern bool contained_float_floatset(double d, const Set *s); extern bool contained_float_floatspan(double d, const Span *s); extern bool contained_float_floatspanset(double d, const SpanSet *ss); extern bool contained_int_intset(int i, const Set *s); -extern bool contained_int_intspanset (int i, const SpanSet *ss); extern bool contained_int_intspan(int i, const Span *s); +extern bool contained_int_intspanset(int i, const SpanSet *ss); extern bool contained_set_set(const Set *s1, const Set *s2); extern bool contained_span_span(const Span *s1, const Span *s2); extern bool contained_span_spanset(const Span *s, const SpanSet *ss); extern bool contained_spanset_span(const SpanSet *ss, const Span *s); extern bool contained_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern bool contained_text_textset(text *txt, const Set *s); extern bool contained_timestamp_period(TimestampTz t, const Span *p); +extern bool contained_timestamp_periodset(TimestampTz t, const SpanSet *ss); extern bool contained_timestamp_timestampset(TimestampTz t, const Set *ts); +extern bool contains_bigintset_bigint(const Set *s, int64 i); +extern bool contains_bigintspan_bigint(const Span *s, int64 i); +extern bool contains_bigintspanset_bigint(const SpanSet *ss, int64 i); +extern bool contains_floatset_float(const Set *s, double d); extern bool contains_floatspan_float(const Span *s, double d); extern bool contains_floatspanset_float(const SpanSet *ss, double d); +extern bool contains_intset_int(const Set *s, int i); extern bool contains_intspan_int(const Span *s, int i); -extern bool contains_set_set(const Set *s1, const Set *s2); +extern bool contains_intspanset_int(const SpanSet *ss, int i); extern bool contains_period_timestamp(const Span *p, TimestampTz t); extern bool contains_periodset_timestamp(const SpanSet *ps, TimestampTz t); +extern bool contains_set_set(const Set *s1, const Set *s2); extern bool contains_span_span(const Span *s1, const Span *s2); extern bool contains_span_spanset(const Span *s, const SpanSet *ss); extern bool contains_spanset_span(const SpanSet *ss, const Span *s); extern bool contains_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool contains_timestampset_timestamp(const Set *ts, TimestampTz t); +extern bool contains_textset_text(const Set *s, text *t); +extern bool contains_timestampset_timestamp(const Set *s, TimestampTz t); extern bool overlaps_set_set(const Set *s1, const Set *s2); extern bool overlaps_span_span(const Span *s1, const Span *s2); extern bool overlaps_spanset_span(const SpanSet *ss, const Span *s); @@ -1322,7 +1332,7 @@ extern bool overafter_stbox_stbox(const STBox *box1, const STBox *box2); -extern TBox *union_tbox_tbox(const TBox *box1, const TBox *box2); +extern TBox *union_tbox_tbox(const TBox *box1, const TBox *box2, bool strict); extern bool inter_tbox_tbox(const TBox *box1, const TBox *box2, TBox *result); extern TBox *intersection_tbox_tbox(const TBox *box1, const TBox *box2); extern STBox *union_stbox_stbox(const STBox *box1, const STBox *box2, bool strict); @@ -1572,26 +1582,26 @@ extern Temporal *tor_tbool_tbool(const Temporal *temp1, const Temporal *temp2); -extern Temporal *add_float_tnumber(double d, const Temporal *tnumber); -extern Temporal *add_int_tnumber(int i, const Temporal *tnumber); -extern Temporal *add_tnumber_float(const Temporal *tnumber, double d); -extern Temporal *add_tnumber_int(const Temporal *tnumber, int i); +extern Temporal *add_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *add_int_tint(int i, const Temporal *tnumber); +extern Temporal *add_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *add_tint_int(const Temporal *tnumber, int i); extern Temporal *add_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); -extern Temporal *div_float_tnumber(double d, const Temporal *tnumber); -extern Temporal *div_int_tnumber(int i, const Temporal *tnumber); -extern Temporal *div_tnumber_float(const Temporal *tnumber, double d); -extern Temporal *div_tnumber_int(const Temporal *tnumber, int i); +extern Temporal *div_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *div_int_tint(int i, const Temporal *tnumber); +extern Temporal *div_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *div_tint_int(const Temporal *tnumber, int i); extern Temporal *div_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); extern double float_degrees(double value, bool normalize); -extern Temporal *mult_float_tnumber(double d, const Temporal *tnumber); -extern Temporal *mult_int_tnumber(int i, const Temporal *tnumber); -extern Temporal *mult_tnumber_float(const Temporal *tnumber, double d); -extern Temporal *mult_tnumber_int(const Temporal *tnumber, int i); +extern Temporal *mult_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *mult_int_tint(int i, const Temporal *tnumber); +extern Temporal *mult_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *mult_tint_int(const Temporal *tnumber, int i); extern Temporal *mult_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); -extern Temporal *sub_float_tnumber(double d, const Temporal *tnumber); -extern Temporal *sub_int_tnumber(int i, const Temporal *tnumber); -extern Temporal *sub_tnumber_float(const Temporal *tnumber, double d); -extern Temporal *sub_tnumber_int(const Temporal *tnumber, int i); +extern Temporal *sub_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *sub_int_tint(int i, const Temporal *tnumber); +extern Temporal *sub_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *sub_tint_int(const Temporal *tnumber, int i); extern Temporal *sub_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); extern Temporal *tfloat_round(const Temporal *temp, int maxdd); extern Temporal *tfloat_degrees(const Temporal *temp, bool normalize); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 6d7c0b9c..fcc8b1bf 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -1366,12 +1366,6 @@ def intspan_set_floatspan(s1: 'const Span *', s2: 'Span *') -> None: _lib.intspan_set_floatspan(s1_converted, s2_converted) -def numspan_set_floatspan(s1: 'const Span *', s2: 'Span *') -> None: - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('Span *', s2) - _lib.numspan_set_floatspan(s1_converted, s2_converted) - - def period_tprecision(s: 'const Span *', duration: 'const Interval *', torigin: int) -> 'Span *': s_converted = _ffi.cast('const Span *', s) duration_converted = _ffi.cast('const Interval *', duration) @@ -1453,12 +1447,24 @@ def adjacent_floatspan_float(s: 'const Span *', d: float) -> 'bool': return result if result != _ffi.NULL else None +def adjacent_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.adjacent_floatspanset_float(ss_converted, d) + return result if result != _ffi.NULL else None + + def adjacent_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.adjacent_intspan_int(s_converted, i) return result if result != _ffi.NULL else None +def adjacent_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.adjacent_intspanset_int(ss_converted, i) + return result if result != _ffi.NULL else None + + def adjacent_period_timestamp(p: 'const Span *', t: int) -> 'bool': p_converted = _ffi.cast('const Span *', p) t_converted = _ffi.cast('TimestampTz', t) @@ -1545,6 +1551,12 @@ def contained_int_intspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def contained_int_intspanset(i: int, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contained_int_intspanset(i, ss_converted) + return result if result != _ffi.NULL else None + + def contained_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) @@ -1580,6 +1592,13 @@ def contained_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> return result if result != _ffi.NULL else None +def contained_text_textset(txt: str, s: 'const Set *') -> 'bool': + txt_converted = cstring2text(txt) + s_converted = _ffi.cast('const Set *', s) + result = _lib.contained_text_textset(txt_converted, s_converted) + return result if result != _ffi.NULL else None + + def contained_timestamp_period(t: int, p: 'const Span *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) p_converted = _ffi.cast('const Span *', p) @@ -1587,6 +1606,13 @@ def contained_timestamp_period(t: int, p: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def contained_timestamp_periodset(t: int, ss: 'const SpanSet *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contained_timestamp_periodset(t_converted, ss_converted) + return result if result != _ffi.NULL else None + + def contained_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) ts_converted = _ffi.cast('const Set *', ts) @@ -1594,6 +1620,33 @@ def contained_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': return result if result != _ffi.NULL else None +def contains_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.contains_bigintset_bigint(s_converted, i_converted) + return result if result != _ffi.NULL else None + + +def contains_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.contains_bigintspan_bigint(s_converted, i_converted) + return result if result != _ffi.NULL else None + + +def contains_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.contains_bigintspanset_bigint(ss_converted, i_converted) + return result if result != _ffi.NULL else None + + +def contains_floatset_float(s: 'const Set *', d: float) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.contains_floatset_float(s_converted, d) + return result if result != _ffi.NULL else None + + def contains_floatspan_float(s: 'const Span *', d: float) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.contains_floatspan_float(s_converted, d) @@ -1606,16 +1659,21 @@ def contains_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': return result if result != _ffi.NULL else None +def contains_intset_int(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.contains_intset_int(s_converted, i) + return result if result != _ffi.NULL else None + + def contains_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.contains_intspan_int(s_converted, i) return result if result != _ffi.NULL else None -def contains_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.contains_set_set(s1_converted, s2_converted) +def contains_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contains_intspanset_int(ss_converted, i) return result if result != _ffi.NULL else None @@ -1633,6 +1691,13 @@ def contains_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': return result if result != _ffi.NULL else None +def contains_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.contains_set_set(s1_converted, s2_converted) + return result if result != _ffi.NULL else None + + def contains_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) @@ -1661,10 +1726,17 @@ def contains_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> return result if result != _ffi.NULL else None -def contains_timestampset_timestamp(ts: 'const Set *', t: int) -> 'bool': - ts_converted = _ffi.cast('const Set *', ts) +def contains_textset_text(s: 'const Set *', t: str) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + t_converted = cstring2text(t) + result = _lib.contains_textset_text(s_converted, t_converted) + return result if result != _ffi.NULL else None + + +def contains_timestampset_timestamp(s: 'const Set *', t: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.contains_timestampset_timestamp(ts_converted, t_converted) + result = _lib.contains_timestampset_timestamp(s_converted, t_converted) return result if result != _ffi.NULL else None @@ -3354,10 +3426,10 @@ def overafter_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool return result if result != _ffi.NULL else None -def union_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'TBox *': +def union_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *', strict: bool) -> 'TBox *': box1_converted = _ffi.cast('const TBox *', box1) box2_converted = _ffi.cast('const TBox *', box2) - result = _lib.union_tbox_tbox(box1_converted, box2_converted) + result = _lib.union_tbox_tbox(box1_converted, box2_converted, strict) return result if result != _ffi.NULL else None @@ -4668,27 +4740,27 @@ def tor_tbool_tbool(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Te return result if result != _ffi.NULL else None -def add_float_tnumber(d: float, tnumber: 'const Temporal *') -> 'Temporal *': +def add_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_float_tnumber(d, tnumber_converted) + result = _lib.add_float_tfloat(d, tnumber_converted) return result if result != _ffi.NULL else None -def add_int_tnumber(i: int, tnumber: 'const Temporal *') -> 'Temporal *': +def add_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_int_tnumber(i, tnumber_converted) + result = _lib.add_int_tint(i, tnumber_converted) return result if result != _ffi.NULL else None -def add_tnumber_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': +def add_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_tnumber_float(tnumber_converted, d) + result = _lib.add_tfloat_float(tnumber_converted, d) return result if result != _ffi.NULL else None -def add_tnumber_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': +def add_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_tnumber_int(tnumber_converted, i) + result = _lib.add_tint_int(tnumber_converted, i) return result if result != _ffi.NULL else None @@ -4699,27 +4771,27 @@ def add_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal return result if result != _ffi.NULL else None -def div_float_tnumber(d: float, tnumber: 'const Temporal *') -> 'Temporal *': +def div_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_float_tnumber(d, tnumber_converted) + result = _lib.div_float_tfloat(d, tnumber_converted) return result if result != _ffi.NULL else None -def div_int_tnumber(i: int, tnumber: 'const Temporal *') -> 'Temporal *': +def div_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_int_tnumber(i, tnumber_converted) + result = _lib.div_int_tint(i, tnumber_converted) return result if result != _ffi.NULL else None -def div_tnumber_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': +def div_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_tnumber_float(tnumber_converted, d) + result = _lib.div_tfloat_float(tnumber_converted, d) return result if result != _ffi.NULL else None -def div_tnumber_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': +def div_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_tnumber_int(tnumber_converted, i) + result = _lib.div_tint_int(tnumber_converted, i) return result if result != _ffi.NULL else None @@ -4735,27 +4807,27 @@ def float_degrees(value: float, normalize: bool) -> 'double': return result if result != _ffi.NULL else None -def mult_float_tnumber(d: float, tnumber: 'const Temporal *') -> 'Temporal *': +def mult_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_float_tnumber(d, tnumber_converted) + result = _lib.mult_float_tfloat(d, tnumber_converted) return result if result != _ffi.NULL else None -def mult_int_tnumber(i: int, tnumber: 'const Temporal *') -> 'Temporal *': +def mult_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_int_tnumber(i, tnumber_converted) + result = _lib.mult_int_tint(i, tnumber_converted) return result if result != _ffi.NULL else None -def mult_tnumber_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': +def mult_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_tnumber_float(tnumber_converted, d) + result = _lib.mult_tfloat_float(tnumber_converted, d) return result if result != _ffi.NULL else None -def mult_tnumber_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': +def mult_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_tnumber_int(tnumber_converted, i) + result = _lib.mult_tint_int(tnumber_converted, i) return result if result != _ffi.NULL else None @@ -4766,27 +4838,27 @@ def mult_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal return result if result != _ffi.NULL else None -def sub_float_tnumber(d: float, tnumber: 'const Temporal *') -> 'Temporal *': +def sub_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_float_tnumber(d, tnumber_converted) + result = _lib.sub_float_tfloat(d, tnumber_converted) return result if result != _ffi.NULL else None -def sub_int_tnumber(i: int, tnumber: 'const Temporal *') -> 'Temporal *': +def sub_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_int_tnumber(i, tnumber_converted) + result = _lib.sub_int_tint(i, tnumber_converted) return result if result != _ffi.NULL else None -def sub_tnumber_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': +def sub_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_tnumber_float(tnumber_converted, d) + result = _lib.sub_tfloat_float(tnumber_converted, d) return result if result != _ffi.NULL else None -def sub_tnumber_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': +def sub_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_tnumber_int(tnumber_converted, i) + result = _lib.sub_tint_int(tnumber_converted, i) return result if result != _ffi.NULL else None From 8103e13f319342fea1778a4fcbbca85148afd9ac Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Tue, 22 Aug 2023 13:55:23 +0200 Subject: [PATCH 002/101] Tests for temporal types --- pymeos/pymeos/boxes/stbox.py | 4 +- pymeos/pymeos/main/tpoint.py | 16 +- pymeos/pymeos/time/period.py | 40 +- pymeos/tests/main/tfloat_test.py | 7 +- pymeos/tests/main/tint_test.py | 4 +- pymeos/tests/time/period_test.py | 8 +- pymeos_cffi/pymeos_cffi/__init__.py | 104 ++- .../builder/build_pymeos_functions.py | 2 +- pymeos_cffi/pymeos_cffi/builder/meos.h | 123 +-- pymeos_cffi/pymeos_cffi/functions.py | 709 ++++++++++++------ 10 files changed, 669 insertions(+), 348 deletions(-) diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index e8898e77..720e8a47 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -155,7 +155,7 @@ def from_geometry(geom: Geometry, geodetic: bool = False) -> STBox: A new :class:`STBox` instance. MEOS Functions: - gserialized_in, geo_to_stbox + pgis_geometry_in, geo_to_stbox """ gs = geometry_to_gserialized(geom, geodetic) return STBox(_inner=geo_to_stbox(gs)) @@ -1166,7 +1166,7 @@ def tile(self, size: Optional[float] = None, duration: Optional[Union[timedelta, else datetime_to_timestamptz(self.tmin()) if self.has_t() \ else 0 gs = geometry_to_gserialized(origin) if origin is not None \ - else gserialized_in('Point(0 0 0)', -1) + else pgis_geometry_in('Point(0 0 0)', -1) tiles, dimensions = stbox_tile_list(self._inner, sz, sz, sz, dt, gs, st) x_size = dimensions[0] or 1 y_size = dimensions[1] or 1 diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 0d0084a0..bea371a2 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -1411,11 +1411,11 @@ def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: A :class:`TBool` with the result of the temporal equality relation. MEOS Functions: - teq_tgeompoint_point, teq_temporal_temporal + teq_tpoint_point, teq_temporal_temporal """ if isinstance(other, pg.Point) or isinstance(other, shp.Point): gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) - result = teq_tgeompoint_point(self._inner, gs) + result = teq_tpoint_point(self._inner, gs) else: return super().temporal_equal(other) return Temporal._factory(result) @@ -1431,11 +1431,11 @@ def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> Tem A :class:`TBool` with the result of the temporal inequality relation. MEOS Functions: - tne_tgeompoint_point, tne_temporal_temporal + tne_tpoint_point, tne_temporal_temporal """ if isinstance(other, pg.Point) or isinstance(other, shp.Point): gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tne_tgeompoint_point(self._inner, gs) + result = tne_tpoint_point(self._inner, gs) else: return super().temporal_not_equal(other) return Temporal._factory(result) @@ -1662,11 +1662,11 @@ def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: A :class:`TBool` with the result of the temporal equality relation. MEOS Functions: - teq_tgeogpoint_point, teq_temporal_temporal + teq_tpoint_point, teq_temporal_temporal """ if isinstance(other, pg.Point) or isinstance(other, shp.Point): gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) - result = teq_tgeogpoint_point(self._inner, gs) + result = teq_tpoint_point(self._inner, gs) else: return super().temporal_equal(other) return Temporal._factory(result) @@ -1682,11 +1682,11 @@ def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBo A :class:`TBool` with the result of the temporal inequality relation. MEOS Functions: - tne_tgeogpoint_point, tne_temporal_temporal + tne_tpoint_point, tne_temporal_temporal """ if isinstance(other, pg.Point) or isinstance(other, shp.Point): gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tne_tgeogpoint_point(self._inner, gs) + result = tne_tpoint_point(self._inner, gs) else: return super().temporal_not_equal(other) return Temporal._factory(result) diff --git a/pymeos/pymeos/time/period.py b/pymeos/pymeos/time/period.py index ecbd64c7..9f6e4ba0 100644 --- a/pymeos/pymeos/time/period.py +++ b/pymeos/pymeos/time/period.py @@ -315,26 +315,26 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim ) return Period(_inner=modified) - def expand(self, other: Period) -> Period: - """ - Returns a new period that includes both ``self`` and ``other`` - - Examples: - >>> Period('[2000-01-01, 2000-01-04)').expand(Period('[2000-01-05, 2000-01-10]')) - >>> 'Period([2000-01-01 00:00:00+01, 2000-01-10 00:00:00+01])' - - Args: - other: :class:`Period` instance to expand the period - - Returns: - A new :class:`Period` instance - - MEOS Functions: - span_expand - """ - copy = span_copy(self._inner) - span_expand(other._inner, copy) - return Period(_inner=copy) + # def expand(self, other: Period) -> Period: + # """ + # Returns a new period that includes both ``self`` and ``other`` + + # Examples: + # >>> Period('[2000-01-01, 2000-01-04)').expand(Period('[2000-01-05, 2000-01-10]')) + # >>> 'Period([2000-01-01 00:00:00+01, 2000-01-10 00:00:00+01])' + + # Args: + # other: :class:`Period` instance to expand the period + + # Returns: + # A new :class:`Period` instance + + # MEOS Functions: + # span_expand + # """ + # copy = span_copy(self._inner) + # span_expand(other._inner, copy) + # return Period(_inner=copy) # ------------------------- Topological Operations ------------------------ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index b1417bb3..de1c98dc 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -1973,15 +1973,16 @@ class TestTFloatSplitOperations(TestTFloat): (tfi, [TFloatInst('1@2019-09-01')]), (tfds, [TFloatSeq('{1@2019-09-01}'),TFloatSeq('{2@2019-09-02}')]), (tfs, [TFloatSeq('[1@2019-09-01, 2@2019-09-02)'),TFloatSeq('[2@2019-09-02]')]), - (tfss, [TFloatSeqSet('{[1@2019-09-01, 2@2019-09-02),[1@2019-09-03, 1@2019-09-05]}'),TFloatSeq('[2@2019-09-02]')]), + (tfss, [TFloatSeqSet('{[1@2019-09-01, 2@2019-09-02),[1@2019-09-03, 1@2019-09-05]}'), + TFloatSeqSet('{[2@2019-09-02]}')]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_value_split(self, temporal, expected): assert temporal.value_split(2) == expected - # The PyMEOS function uses as default origin the initial timestamp of the - # temporal value while in MEOS the default origin is Monday Janury 3, 2000 + ## The PyMEOS function uses as default origin the initial timestamp of the + ## temporal value while in MEOS the default origin is Monday Janury 3, 2000 @pytest.mark.parametrize( 'temporal, expected', [ diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index 7dadfdf1..f328b825 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -1817,8 +1817,8 @@ class TestTIntSplitOperations(TestTInt): def test_value_split(self, temporal, expected): assert temporal.value_split(2) == expected - # The PyMEOS function uses as default origin the initial timestamp of the - # temporal value while in MEOS the default origin is Monday Janury 3, 2000 + ## The PyMEOS function uses as default origin the initial timestamp of the + ## temporal value while in MEOS the default origin is Monday Janury 3, 2000 @pytest.mark.parametrize( 'temporal, expected', [ diff --git a/pymeos/tests/time/period_test.py b/pymeos/tests/time/period_test.py index cd8a3506..e8c51966 100644 --- a/pymeos/tests/time/period_test.py +++ b/pymeos/tests/time/period_test.py @@ -181,10 +181,10 @@ def test_shift_tscale(self): self.assert_period_equality(shifted_scaled, datetime(2019, 9, 12, 0, tzinfo=timezone.utc), datetime(2019, 9, 12, 4, tzinfo=timezone.utc), False, False) - def test_expand(self): - expanded = self.period.expand(Period('(2021-01-01 00:00:00+0, 2021-02-01 00:00:00+0)')) - self.assert_period_equality(expanded, datetime(2019, 9, 8, tzinfo=timezone.utc), - datetime(2021, 2, 1, tzinfo=timezone.utc), False, False) + # def test_expand(self): + # expanded = self.period.expand(Period('(2021-01-01 00:00:00+0, 2021-02-01 00:00:00+0)')) + # self.assert_period_equality(expanded, datetime(2019, 9, 8, tzinfo=timezone.utc), + # datetime(2021, 2, 1, tzinfo=timezone.utc), False, False) class TestPeriodTopologicalPositionFunctions(TestPeriod): diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index cfe3428a..7c230f4c 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -55,6 +55,8 @@ 'pg_interval_to_char', 'pg_to_timestamp', 'pg_to_date', + 'geography_from_hexewkb', + 'geometry_from_hexewkb', 'gserialized_as_ewkb', 'gserialized_as_ewkt', 'gserialized_as_geojson', @@ -62,9 +64,10 @@ 'gserialized_as_text', 'gserialized_from_ewkb', 'gserialized_from_geojson', - 'gserialized_from_hexewkb', - 'gserialized_from_text', - 'gserialized_in', + 'geometry_from_text', + 'geography_from_text', + 'pgis_geography_in', + 'pgis_geometry_in', 'gserialized_out', 'pgis_gserialized_same', 'bigintset_in', @@ -80,9 +83,8 @@ 'floatspanset_in', 'floatspanset_out', 'geogset_in', - 'geogset_out', + 'geoset_out', 'geomset_in', - 'geomset_out', 'geoset_as_ewkt', 'geoset_as_text', 'intset_in', @@ -118,8 +120,7 @@ 'bigintspan_make', 'floatset_make', 'floatspan_make', - 'geogset_make', - 'geomset_make', + 'geoset_make', 'intset_make', 'intspan_make', 'period_make', @@ -137,11 +138,14 @@ 'float_to_floatset', 'float_to_floatspan', 'float_to_floatspanset', + 'geog_to_geogset', + 'geom_to_geomset', 'int_to_intset', 'int_to_intspan', 'int_to_intspanset', 'set_to_spanset', 'span_to_spanset', + 'text_to_textset', 'timestamp_to_period', 'timestamp_to_periodset', 'timestamp_to_tstzset', @@ -161,7 +165,11 @@ 'floatspan_upper', 'floatspanset_lower', 'floatspanset_upper', + 'geoset_end_value', 'geoset_srid', + 'geoset_start_value', + 'geoset_value_n', + 'geoset_values', 'intset_end_value', 'intset_start_value', 'intset_value_n', @@ -204,6 +212,10 @@ 'spanset_upper_inc', 'spanset_width', 'spatialset_stbox', + 'textset_end_value', + 'textset_start_value', + 'textset_value_n', + 'textset_values', 'timestampset_end_timestamp', 'timestampset_start_timestamp', 'timestampset_timestamp_n', @@ -211,15 +223,11 @@ 'floatset_round', 'floatspan_round', 'floatspanset_round', - 'floatspan_set_intspan', 'geoset_round', - 'intspan_set_floatspan', 'period_tprecision', 'periodset_tprecision', 'period_shift_tscale', 'periodset_shift_tscale', - 'set_shift', - 'span_expand', 'timestamp_tprecision', 'timestampset_shift_tscale', 'adjacent_bigintspan_bigint', @@ -322,30 +330,69 @@ 'right_span_spanset', 'right_spanset_span', 'right_spanset_spanset', - 'bbox_union_span_span', - 'intersection_set_set', + 'intersection_bigintset_bigint', + 'intersection_bigintspan_bigint', + 'intersection_bigintspanset_bigint', + 'intersection_floatset_float', + 'intersection_floatspan_float', + 'intersection_floatspanset_float', + 'intersection_intset_int', + 'intersection_intspan_int', + 'intersection_intspanset_int', 'intersection_period_timestamp', 'intersection_periodset_timestamp', + 'intersection_set_set', 'intersection_span_span', 'intersection_spanset_span', 'intersection_spanset_spanset', + 'intersection_textset_text', 'intersection_timestampset_timestamp', - 'minus_set_set', + 'minus_bigint_bigintset', + 'minus_bigint_bigintspan', + 'minus_bigint_bigintspanset', + 'minus_bigintset_bigint', + 'minus_bigintspan_bigint', + 'minus_bigintspanset_bigint', + 'minus_float_floatset', + 'minus_float_floatspan', + 'minus_float_floatspanset', + 'minus_floatset_float', + 'minus_floatspan_float', + 'minus_floatspanset_float', + 'minus_int_intset', + 'minus_int_intspan', + 'minus_int_intspanset', + 'minus_intset_int', + 'minus_intspan_int', + 'minus_intspanset_int', 'minus_period_timestamp', 'minus_periodset_timestamp', + 'minus_set_set', 'minus_span_span', 'minus_span_spanset', 'minus_spanset_span', 'minus_spanset_spanset', + 'minus_text_textset', + 'minus_textset_text', 'minus_timestamp_period', 'minus_timestamp_periodset', 'minus_timestampset_timestamp', - 'union_set_set', + 'union_bigintset_bigint', + 'union_bigintspan_bigint', + 'union_bigintspanset_bigint', + 'union_floatset_float', + 'union_floatspan_float', + 'union_floatspanset_float', + 'union_intset_int', + 'union_intspan_int', + 'union_intspanset_int', 'union_period_timestamp', 'union_periodset_timestamp', + 'union_set_set', 'union_span_span', 'union_spanset_span', 'union_spanset_spanset', + 'union_textset_text', 'union_timestampset_timestamp', 'distance_floatspan_float', 'distance_intspan_int', @@ -410,12 +457,10 @@ 'stbox_as_hexwkb', 'stbox_in', 'stbox_out', - 'tbox_make', - 'tbox_set', - 'tbox_copy', 'stbox_make', - 'stbox_set', 'stbox_copy', + 'tbox_make', + 'tbox_copy', 'int_to_tbox', 'float_to_tbox', 'timestamp_to_tbox', @@ -467,13 +512,11 @@ 'stbox_tmax', 'stbox_tmax_inc', 'stbox_srid', - 'stbox_expand', 'stbox_expand_space', 'stbox_expand_time', 'stbox_get_space', 'stbox_round', 'stbox_set_srid', - 'tbox_expand', 'tbox_expand_value', 'tbox_expand_time', 'tbox_round', @@ -618,7 +661,6 @@ 'temporal_time', 'temporal_timestamp_n', 'temporal_timestamps', - 'temporal_values', 'tfloat_end_value', 'tfloat_max_value', 'tfloat_min_value', @@ -775,8 +817,6 @@ 'tint_ever_eq', 'tint_ever_le', 'tint_ever_lt', - 'tpoint_always_eq', - 'tpoint_ever_eq', 'ttext_always_eq', 'ttext_always_le', 'ttext_always_lt', @@ -792,18 +832,14 @@ 'temporal_ne', 'teq_bool_tbool', 'teq_float_tfloat', - 'teq_geo_tpoint', 'teq_int_tint', - 'teq_point_tgeogpoint', - 'teq_point_tgeompoint', + 'teq_point_tpoint', 'teq_tbool_bool', 'teq_temporal_temporal', 'teq_text_ttext', 'teq_tfloat_float', - 'teq_tgeogpoint_point', - 'teq_tgeompoint_point', + 'teq_tpoint_point', 'teq_tint_int', - 'teq_tpoint_geo', 'teq_ttext_text', 'tge_float_tfloat', 'tge_int_tint', @@ -835,18 +871,14 @@ 'tlt_ttext_text', 'tne_bool_tbool', 'tne_float_tfloat', - 'tne_geo_tpoint', 'tne_int_tint', - 'tne_point_tgeogpoint', - 'tne_point_tgeompoint', + 'tne_point_tpoint', 'tne_tbool_bool', 'tne_temporal_temporal', 'tne_text_ttext', 'tne_tfloat_float', - 'tne_tgeogpoint_point', - 'tne_tgeompoint_point', + 'tne_tpoint_point', 'tne_tint_int', - 'tne_tpoint_geo', 'tne_ttext_text', 'bearing_point_point', 'bearing_tpoint_point', diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index 28e9464f..56c19866 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -89,7 +89,7 @@ def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry], geodetic: Op text = f'SRID={get_srid(geom)};{text}' else: raise TypeError('Parameter geom must be either a PostGIS Geometry or a Shapely BaseGeometry') - gs = gserialized_in(text, -1) + gs = pgis_geometry_in(text, -1) if geodetic is not None: # GFlags is an 8-bit integer, where the 4th bit is the geodetic flag (0x80) # If geodetic is True, then set the 4th bit to 1, otherwise set it to 0 diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 7f6658b3..b17455a3 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -482,8 +482,6 @@ extern LWPOINT *lwpoint_make(int32_t srid, int hasz, int hasm, const POINT4D *p) extern LWGEOM *lwgeom_from_gserialized(const GSERIALIZED *g); extern GSERIALIZED *gserialized_from_lwgeom(LWGEOM *geom, size_t *size); - - extern int32_t lwgeom_get_srid(const LWGEOM *geom); extern double lwpoint_get_x(const LWPOINT *point); @@ -773,6 +771,8 @@ extern DateADT pg_to_date(text *date_txt, text *fmt); * Functions for input/output and manipulation of PostGIS types *****************************************************************************/ +extern GSERIALIZED *geography_from_hexewkb(const char *wkt); +extern GSERIALIZED *geometry_from_hexewkb(const char *wkt); extern bytea *gserialized_as_ewkb(const GSERIALIZED *geom, char *type); extern char *gserialized_as_ewkt(const GSERIALIZED *geom, int precision); extern char *gserialized_as_geojson(const GSERIALIZED *geom, int option, int precision, char *srs); @@ -780,9 +780,10 @@ extern char *gserialized_as_hexewkb(const GSERIALIZED *geom, const char *type); extern char *gserialized_as_text(const GSERIALIZED *geom, int precision); extern GSERIALIZED *gserialized_from_ewkb(const bytea *bytea_wkb, int32 srid); extern GSERIALIZED *gserialized_from_geojson(const char *geojson); -extern GSERIALIZED *gserialized_from_hexewkb(const char *wkt); -extern GSERIALIZED *gserialized_from_text(char *wkt, int srid); -extern GSERIALIZED *gserialized_in(char *input, int32 geom_typmod); +extern GSERIALIZED *geometry_from_text(char *wkt, int srid); +extern GSERIALIZED *geography_from_text(char *wkt, int srid); +extern GSERIALIZED *pgis_geography_in(char *input, int32 geom_typmod); +extern GSERIALIZED *pgis_geometry_in(char *input, int32 geom_typmod); extern char *gserialized_out(const GSERIALIZED *geom); extern bool pgis_gserialized_same(const GSERIALIZED *geom1, const GSERIALIZED *geom2); @@ -805,9 +806,8 @@ extern char *floatspan_out(const Span *s, int maxdd); extern SpanSet *floatspanset_in(const char *str); extern char *floatspanset_out(const SpanSet *ss, int maxdd); extern Set *geogset_in(const char *str); -extern char *geogset_out(const Set *set, int maxdd); +extern char *geoset_out(const Set *set, int maxdd); extern Set *geomset_in(const char *str); -extern char *geomset_out(const Set *set, int maxdd); extern char *geoset_as_ewkt(const Set *set, int maxdd); extern char *geoset_as_text(const Set *set, int maxdd); extern Set *intset_in(const char *str); @@ -848,8 +848,7 @@ extern Set *bigintset_make(const int64 *values, int count); extern Span *bigintspan_make(int64 lower, int64 upper, bool lower_inc, bool upper_inc); extern Set *floatset_make(const double *values, int count); extern Span *floatspan_make(double lower, double upper, bool lower_inc, bool upper_inc); -extern Set *geogset_make(const GSERIALIZED **values, int count); -extern Set *geomset_make(const GSERIALIZED **values, int count); +extern Set *geoset_make(const GSERIALIZED **values, int count); extern Set *intset_make(const int *values, int count); extern Span *intspan_make(int lower, int upper, bool lower_inc, bool upper_inc); extern Span *period_make(TimestampTz lower, TimestampTz upper, bool lower_inc, bool upper_inc); @@ -872,11 +871,14 @@ extern SpanSet *bigint_to_bigintspanset(int i); extern Set *float_to_floatset(double d); extern Span *float_to_floatspan(double d); extern SpanSet *float_to_floatspanset(double d); +extern Set *geog_to_geogset(GSERIALIZED *gs); +extern Set *geom_to_geomset(GSERIALIZED *gs); extern Set *int_to_intset(int i); extern Span *int_to_intspan(int i); extern SpanSet *int_to_intspanset(int i); extern SpanSet *set_to_spanset(const Set *s); extern SpanSet *span_to_spanset(const Span *s); +extern Set *text_to_textset(text *txt); extern Span *timestamp_to_period(TimestampTz t); extern SpanSet *timestamp_to_periodset(TimestampTz t); extern Set *timestamp_to_tstzset(TimestampTz t); @@ -901,7 +903,11 @@ extern double floatspan_lower(const Span *s); extern double floatspan_upper(const Span *s); extern double floatspanset_lower(const SpanSet *ss); extern double floatspanset_upper(const SpanSet *ss); +extern GSERIALIZED *geoset_end_value(const Set *s); extern int geoset_srid(const Set *set); +extern GSERIALIZED *geoset_start_value(const Set *s); +extern bool geoset_value_n(const Set *s, int n, GSERIALIZED **result); +extern GSERIALIZED **geoset_values(const Set *s); extern int intset_end_value(const Set *s); extern int intset_start_value(const Set *s); extern bool intset_value_n(const Set *s, int n, int *result); @@ -944,6 +950,10 @@ extern Span *spanset_start_span(const SpanSet *ss); extern bool spanset_upper_inc(const SpanSet *ss); extern double spanset_width(const SpanSet *ss); extern STBox *spatialset_stbox(const Set *s); +extern text *textset_end_value(const Set *s); +extern text *textset_start_value(const Set *s); +extern bool textset_value_n(const Set *s, int n, text **result); +extern text **textset_values(const Set *s); extern TimestampTz timestampset_end_timestamp(const Set *ts); extern TimestampTz timestampset_start_timestamp(const Set *ts); extern bool timestampset_timestamp_n(const Set *ts, int n, TimestampTz *result); @@ -956,15 +966,11 @@ extern TimestampTz *timestampset_values(const Set *ts); extern Set *floatset_round(const Set *s, int maxdd); extern Span *floatspan_round(const Span *s, int maxdd); extern SpanSet *floatspanset_round(const SpanSet *ss, int maxdd); -extern void floatspan_set_intspan(const Span *s1, Span *s2); extern Set *geoset_round(const Set *s, int maxdd); -extern void intspan_set_floatspan(const Span *s1, Span *s2); extern Span *period_tprecision(const Span *s, const Interval *duration, TimestampTz torigin); extern SpanSet *periodset_tprecision(const SpanSet *ss, const Interval *duration, TimestampTz torigin); extern Span *period_shift_tscale(const Span *p, const Interval *shift, const Interval *duration); extern SpanSet *periodset_shift_tscale(const SpanSet *ps, const Interval *shift, const Interval *duration); -extern Set *set_shift(const Set *s, Datum shift); -extern void span_expand(const Span *s1, Span *s2); extern TimestampTz timestamp_tprecision(TimestampTz t, const Interval *duration, TimestampTz torigin); extern Set *timestampset_shift_tscale(const Set *ts, const Interval *shift, const Interval *duration); @@ -1084,31 +1090,70 @@ extern bool right_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern void bbox_union_span_span(const Span *s1, const Span *s2, Span *result); +extern bool intersection_bigintset_bigint(const Set *s, int64 i, int64 *result); +extern bool intersection_bigintspan_bigint(const Span *s, int64 i, int64 *result); +extern bool intersection_bigintspanset_bigint(const SpanSet *ss, int64 i, int64 *result); +extern bool intersection_floatset_float(const Set *s, double d, double *result); +extern bool intersection_floatspan_float(const Span *s, double d, double *result); +extern bool intersection_floatspanset_float(const SpanSet *ss, double d, double *result); +extern bool intersection_intset_int(const Set *s, int i, int *result); +extern bool intersection_intspan_int(const Span *s, int i, int *result); +extern bool intersection_intspanset_int(const SpanSet *ss, int i, int *result); +extern bool intersection_period_timestamp(const Span *s, TimestampTz t, TimestampTz *result); +extern bool intersection_periodset_timestamp(const SpanSet *ss, TimestampTz t, TimestampTz *result); extern Set *intersection_set_set(const Set *s1, const Set *s2); -extern bool intersection_period_timestamp(const Span *p, TimestampTz t, TimestampTz *result); -extern bool intersection_periodset_timestamp(const SpanSet *ps, TimestampTz t, TimestampTz *result); extern Span *intersection_span_span(const Span *s1, const Span *s2); extern SpanSet *intersection_spanset_span(const SpanSet *ss, const Span *s); extern SpanSet *intersection_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool intersection_timestampset_timestamp(const Set *ts, const TimestampTz t, TimestampTz *result); +extern bool intersection_textset_text(const Set *s, const text *txt, text **result); +extern bool intersection_timestampset_timestamp(const Set *s, TimestampTz t, TimestampTz *result); +extern bool minus_bigint_bigintset(int64 i, const Set *s, int64 *result); +extern bool minus_bigint_bigintspan(int64 i, const Span *s, int64 *result); +extern bool minus_bigint_bigintspanset(int64 i, const SpanSet *ss, int64 *result); +extern Set *minus_bigintset_bigint(const Set *s, int64 i); +extern SpanSet *minus_bigintspan_bigint(const Span *s, int64 i); +extern SpanSet *minus_bigintspanset_bigint(const SpanSet *ss, int64 i); +extern bool minus_float_floatset(double d, const Set *s, double *result); +extern bool minus_float_floatspan(double d, const Span *s, double *result); +extern bool minus_float_floatspanset(double d, const SpanSet *ss, double *result); +extern Set *minus_floatset_float(const Set *s, double d); +extern SpanSet *minus_floatspan_float(const Span *s, double d); +extern SpanSet *minus_floatspanset_float(const SpanSet *ss, double d); +extern bool minus_int_intset(int i, const Set *s, int *result); +extern bool minus_int_intspan(int i, const Span *s, int *result); +extern bool minus_int_intspanset(int i, const SpanSet *ss, int *result); +extern Set *minus_intset_int(const Set *s, int i); +extern SpanSet *minus_intspan_int(const Span *s, int i); +extern SpanSet *minus_intspanset_int(const SpanSet *ss, int i); +extern SpanSet *minus_period_timestamp(const Span *s, TimestampTz t); +extern SpanSet *minus_periodset_timestamp(const SpanSet *ss, TimestampTz t); extern Set *minus_set_set(const Set *s1, const Set *s2); -extern SpanSet *minus_period_timestamp(const Span *p, TimestampTz t); -extern SpanSet *minus_periodset_timestamp(const SpanSet *ps, TimestampTz t); extern SpanSet *minus_span_span(const Span *s1, const Span *s2); extern SpanSet *minus_span_spanset(const Span *s, const SpanSet *ss); extern SpanSet *minus_spanset_span(const SpanSet *ss, const Span *s); extern SpanSet *minus_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool minus_timestamp_period(TimestampTz t, const Span *p, TimestampTz *result); -extern bool minus_timestamp_periodset(TimestampTz t, const SpanSet *ps, TimestampTz *result); -extern Set *minus_timestampset_timestamp(const Set *ts, TimestampTz t); +extern bool minus_text_textset(const text *txt, const Set *s, text **result); +extern Set *minus_textset_text(const Set *s, const text *txt); +extern bool minus_timestamp_period(TimestampTz t, const Span *s, TimestampTz *result); +extern bool minus_timestamp_periodset(TimestampTz t, const SpanSet *ss, TimestampTz *result); +extern Set *minus_timestampset_timestamp(const Set *s, TimestampTz t); +extern Set *union_bigintset_bigint(const Set *s, int64 i); +extern SpanSet *union_bigintspan_bigint(const Span *s, int64 i); +extern SpanSet *union_bigintspanset_bigint(const SpanSet *ss, int64 i); +extern Set *union_floatset_float(const Set *s, double d); +extern SpanSet *union_floatspan_float(const Span *s, double d); +extern SpanSet *union_floatspanset_float(const SpanSet *ss, double d); +extern Set *union_intset_int(const Set *s, int i); +extern SpanSet *union_intspan_int(const Span *s, int i); +extern SpanSet *union_intspanset_int(const SpanSet *ss, int i); +extern SpanSet *union_period_timestamp(const Span *s, TimestampTz t); +extern SpanSet *union_periodset_timestamp(SpanSet *ss, TimestampTz t); extern Set *union_set_set(const Set *s1, const Set *s2); -extern SpanSet *union_period_timestamp(const Span *p, TimestampTz t); -extern SpanSet *union_periodset_timestamp(SpanSet *ps, TimestampTz t); extern SpanSet *union_span_span(const Span *s1, const Span *s2); extern SpanSet *union_spanset_span(const SpanSet *ss, const Span *s); extern SpanSet *union_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern Set *union_timestampset_timestamp(const Set *ts, const TimestampTz t); +extern Set *union_textset_text(const Set *s, text *txt); +extern Set *union_timestampset_timestamp(const Set *s, const TimestampTz t); @@ -1199,14 +1244,11 @@ extern char *stbox_out(const STBox *box, int maxdd); -extern TBox *tbox_make(const Span *s, const Span *p); -extern void tbox_set(const Span *s, const Span *p, TBox *box); -extern TBox *tbox_copy(const TBox *box); extern STBox * stbox_make(bool hasx, bool hasz, bool geodetic, int32 srid, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, const Span *p); -extern void stbox_set(bool hasx, bool hasz, bool geodetic, int32 srid, double xmin, double xmax, - double ymin, double ymax, double zmin, double zmax, const Span *p, STBox *box); extern STBox *stbox_copy(const STBox *box); +extern TBox *tbox_make(const Span *s, const Span *p); +extern TBox *tbox_copy(const TBox *box); @@ -1273,13 +1315,11 @@ extern int32 stbox_srid(const STBox *box); -extern void stbox_expand(const STBox *box1, STBox *box2); extern STBox *stbox_expand_space(const STBox *box, double d); extern STBox *stbox_expand_time(const STBox *box, const Interval *interval); extern STBox *stbox_get_space(const STBox *box); extern STBox *stbox_round(const STBox *box, int maxdd); extern STBox *stbox_set_srid(const STBox *box, int32 srid); -extern void tbox_expand(const TBox *box1, TBox *box2); extern TBox *tbox_expand_value(const TBox *box, const double d); extern TBox *tbox_expand_time(const TBox *box, const Interval *interval); extern TBox *tbox_round(const TBox *box, int maxdd); @@ -1471,7 +1511,6 @@ extern char *temporal_subtype(const Temporal *temp); extern SpanSet *temporal_time(const Temporal *temp); extern bool temporal_timestamp_n(const Temporal *temp, int n, TimestampTz *result); extern TimestampTz *temporal_timestamps(const Temporal *temp, int *count); -extern Datum *temporal_values(const Temporal *temp, int *count); extern double tfloat_end_value(const Temporal *temp); extern double tfloat_max_value(const Temporal *temp); extern double tfloat_min_value(const Temporal *temp); @@ -1668,8 +1707,6 @@ extern bool tint_always_lt(const Temporal *temp, int i); extern bool tint_ever_eq(const Temporal *temp, int i); extern bool tint_ever_le(const Temporal *temp, int i); extern bool tint_ever_lt(const Temporal *temp, int i); -extern bool tpoint_always_eq(const Temporal *temp, Datum value); -extern bool tpoint_ever_eq(const Temporal *temp, Datum value); extern bool ttext_always_eq(const Temporal *temp, text *txt); extern bool ttext_always_le(const Temporal *temp, text *txt); extern bool ttext_always_lt(const Temporal *temp, text *txt); @@ -1690,18 +1727,14 @@ extern bool temporal_lt(const Temporal *temp1, const Temporal *temp2); extern bool temporal_ne(const Temporal *temp1, const Temporal *temp2); extern Temporal *teq_bool_tbool(bool b, const Temporal *temp); extern Temporal *teq_float_tfloat(double d, const Temporal *temp); -extern Temporal *teq_geo_tpoint(const GSERIALIZED *geo, const Temporal *tpoint); extern Temporal *teq_int_tint(int i, const Temporal *temp); -extern Temporal *teq_point_tgeogpoint(const GSERIALIZED *gs, const Temporal *temp); -extern Temporal *teq_point_tgeompoint(const GSERIALIZED *gs, const Temporal *temp); +extern Temporal *teq_point_tpoint(const GSERIALIZED *gs, const Temporal *temp); extern Temporal *teq_tbool_bool(const Temporal *temp, bool b); extern Temporal *teq_temporal_temporal(const Temporal *temp1, const Temporal *temp2); extern Temporal *teq_text_ttext(const text *txt, const Temporal *temp); extern Temporal *teq_tfloat_float(const Temporal *temp, double d); -extern Temporal *teq_tgeogpoint_point(const Temporal *temp, const GSERIALIZED *gs); -extern Temporal *teq_tgeompoint_point(const Temporal *temp, const GSERIALIZED *gs); +extern Temporal *teq_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); extern Temporal *teq_tint_int(const Temporal *temp, int i); -extern Temporal *teq_tpoint_geo(const Temporal *tpoint, const GSERIALIZED *geo); extern Temporal *teq_ttext_text(const Temporal *temp, const text *txt); extern Temporal *tge_float_tfloat(double d, const Temporal *temp); extern Temporal *tge_int_tint(int i, const Temporal *temp); @@ -1733,18 +1766,14 @@ extern Temporal *tlt_tint_int(const Temporal *temp, int i); extern Temporal *tlt_ttext_text(const Temporal *temp, const text *txt); extern Temporal *tne_bool_tbool(bool b, const Temporal *temp); extern Temporal *tne_float_tfloat(double d, const Temporal *temp); -extern Temporal *tne_geo_tpoint(const GSERIALIZED *geo, const Temporal *tpoint); extern Temporal *tne_int_tint(int i, const Temporal *temp); -extern Temporal *tne_point_tgeogpoint(const GSERIALIZED *gs, const Temporal *temp); -extern Temporal *tne_point_tgeompoint(const GSERIALIZED *gs, const Temporal *temp); +extern Temporal *tne_point_tpoint(const GSERIALIZED *gs, const Temporal *temp); extern Temporal *tne_tbool_bool(const Temporal *temp, bool b); extern Temporal *tne_temporal_temporal(const Temporal *temp1, const Temporal *temp2); extern Temporal *tne_text_ttext(const text *txt, const Temporal *temp); extern Temporal *tne_tfloat_float(const Temporal *temp, double d); -extern Temporal *tne_tgeogpoint_point(const Temporal *temp, const GSERIALIZED *gs); -extern Temporal *tne_tgeompoint_point(const Temporal *temp, const GSERIALIZED *gs); +extern Temporal *tne_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); extern Temporal *tne_tint_int(const Temporal *temp, int i); -extern Temporal *tne_tpoint_geo(const Temporal *tpoint, const GSERIALIZED *geo); extern Temporal *tne_ttext_text(const Temporal *temp, const text *txt); /***************************************************************************** diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index fcc8b1bf..82952275 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -49,7 +49,7 @@ def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry], geodetic: Op text = f'SRID={get_srid(geom)};{text}' else: raise TypeError('Parameter geom must be either a PostGIS Geometry or a Shapely BaseGeometry') - gs = gserialized_in(text, -1) + gs = pgis_geometry_in(text, -1) if geodetic is not None: # GFlags is an 8-bit integer, where the 4th bit is the geodetic flag (0x80) # If geodetic is True, then set the 4th bit to 1, otherwise set it to 0 @@ -352,6 +352,18 @@ def pg_to_date(date_txt: str, fmt: str) -> 'DateADT': return result if result != _ffi.NULL else None +def geography_from_hexewkb(wkt: str) -> 'GSERIALIZED *': + wkt_converted = wkt.encode('utf-8') + result = _lib.geography_from_hexewkb(wkt_converted) + return result if result != _ffi.NULL else None + + +def geometry_from_hexewkb(wkt: str) -> 'GSERIALIZED *': + wkt_converted = wkt.encode('utf-8') + result = _lib.geometry_from_hexewkb(wkt_converted) + return result if result != _ffi.NULL else None + + def gserialized_as_ewkb(geom: 'const GSERIALIZED *', type: str) -> 'bytea *': geom_converted = _ffi.cast('const GSERIALIZED *', geom) type_converted = type.encode('utf-8') @@ -402,22 +414,29 @@ def gserialized_from_geojson(geojson: str) -> 'GSERIALIZED *': return result if result != _ffi.NULL else None -def gserialized_from_hexewkb(wkt: str) -> 'GSERIALIZED *': +def geometry_from_text(wkt: str, srid: int) -> 'GSERIALIZED *': wkt_converted = wkt.encode('utf-8') - result = _lib.gserialized_from_hexewkb(wkt_converted) + result = _lib.geometry_from_text(wkt_converted, srid) return result if result != _ffi.NULL else None -def gserialized_from_text(wkt: str, srid: int) -> 'GSERIALIZED *': +def geography_from_text(wkt: str, srid: int) -> 'GSERIALIZED *': wkt_converted = wkt.encode('utf-8') - result = _lib.gserialized_from_text(wkt_converted, srid) + result = _lib.geography_from_text(wkt_converted, srid) return result if result != _ffi.NULL else None -def gserialized_in(input: str, geom_typmod: int) -> 'GSERIALIZED *': +def pgis_geography_in(input: str, geom_typmod: int) -> 'GSERIALIZED *': input_converted = input.encode('utf-8') geom_typmod_converted = _ffi.cast('int32', geom_typmod) - result = _lib.gserialized_in(input_converted, geom_typmod_converted) + result = _lib.pgis_geography_in(input_converted, geom_typmod_converted) + return result if result != _ffi.NULL else None + + +def pgis_geometry_in(input: str, geom_typmod: int) -> 'GSERIALIZED *': + input_converted = input.encode('utf-8') + geom_typmod_converted = _ffi.cast('int32', geom_typmod) + result = _lib.pgis_geometry_in(input_converted, geom_typmod_converted) return result if result != _ffi.NULL else None @@ -519,9 +538,9 @@ def geogset_in(string: str) -> 'Set *': return result if result != _ffi.NULL else None -def geogset_out(set: 'const Set *', maxdd: int) -> str: +def geoset_out(set: 'const Set *', maxdd: int) -> str: set_converted = _ffi.cast('const Set *', set) - result = _lib.geogset_out(set_converted, maxdd) + result = _lib.geoset_out(set_converted, maxdd) result = _ffi.string(result).decode('utf-8') return result if result != _ffi.NULL else None @@ -532,13 +551,6 @@ def geomset_in(string: str) -> 'Set *': return result if result != _ffi.NULL else None -def geomset_out(set: 'const Set *', maxdd: int) -> str: - set_converted = _ffi.cast('const Set *', set) - result = _lib.geomset_out(set_converted, maxdd) - result = _ffi.string(result).decode('utf-8') - return result if result != _ffi.NULL else None - - def geoset_as_ewkt(set: 'const Set *', maxdd: int) -> str: set_converted = _ffi.cast('const Set *', set) result = _lib.geoset_as_ewkt(set_converted, maxdd) @@ -779,15 +791,9 @@ def floatspan_make(lower: float, upper: float, lower_inc: bool, upper_inc: bool) return result if result != _ffi.NULL else None -def geogset_make(values: 'const GSERIALIZED **', count: int) -> 'Set *': - values_converted = [_ffi.cast('const GSERIALIZED *', x) for x in values] - result = _lib.geogset_make(values_converted, count) - return result if result != _ffi.NULL else None - - -def geomset_make(values: 'const GSERIALIZED **', count: int) -> 'Set *': +def geoset_make(values: 'const GSERIALIZED **', count: int) -> 'Set *': values_converted = [_ffi.cast('const GSERIALIZED *', x) for x in values] - result = _lib.geomset_make(values_converted, count) + result = _lib.geoset_make(values_converted, count) return result if result != _ffi.NULL else None @@ -888,6 +894,18 @@ def float_to_floatspanset(d: float) -> 'SpanSet *': return result if result != _ffi.NULL else None +def geog_to_geogset(gs: 'GSERIALIZED *') -> 'Set *': + gs_converted = _ffi.cast('GSERIALIZED *', gs) + result = _lib.geog_to_geogset(gs_converted) + return result if result != _ffi.NULL else None + + +def geom_to_geomset(gs: 'GSERIALIZED *') -> 'Set *': + gs_converted = _ffi.cast('GSERIALIZED *', gs) + result = _lib.geom_to_geomset(gs_converted) + return result if result != _ffi.NULL else None + + def int_to_intset(i: int) -> 'Set *': result = _lib.int_to_intset(i) return result if result != _ffi.NULL else None @@ -915,6 +933,12 @@ def span_to_spanset(s: 'const Span *') -> 'SpanSet *': return result if result != _ffi.NULL else None +def text_to_textset(txt: str) -> 'Set *': + txt_converted = cstring2text(txt) + result = _lib.text_to_textset(txt_converted) + return result if result != _ffi.NULL else None + + def timestamp_to_period(t: int) -> 'Span *': t_converted = _ffi.cast('TimestampTz', t) result = _lib.timestamp_to_period(t_converted) @@ -1035,12 +1059,39 @@ def floatspanset_upper(ss: 'const SpanSet *') -> 'double': return result if result != _ffi.NULL else None +def geoset_end_value(s: 'const Set *') -> 'GSERIALIZED *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.geoset_end_value(s_converted) + return result if result != _ffi.NULL else None + + def geoset_srid(set: 'const Set *') -> 'int': set_converted = _ffi.cast('const Set *', set) result = _lib.geoset_srid(set_converted) return result if result != _ffi.NULL else None +def geoset_start_value(s: 'const Set *') -> 'GSERIALIZED *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.geoset_start_value(s_converted) + return result if result != _ffi.NULL else None + + +def geoset_value_n(s: 'const Set *', n: int) -> 'GSERIALIZED **': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.geoset_value_n(s_converted, n, out_result) + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def geoset_values(s: 'const Set *') -> 'GSERIALIZED **': + s_converted = _ffi.cast('const Set *', s) + result = _lib.geoset_values(s_converted) + return result if result != _ffi.NULL else None + + def intset_end_value(s: 'const Set *') -> 'int': s_converted = _ffi.cast('const Set *', s) result = _lib.intset_end_value(s_converted) @@ -1303,6 +1354,35 @@ def spatialset_stbox(s: 'const Set *') -> 'STBox *': return result if result != _ffi.NULL else None +def textset_end_value(s: 'const Set *') -> str: + s_converted = _ffi.cast('const Set *', s) + result = _lib.textset_end_value(s_converted) + result = text2cstring(result) + return result if result != _ffi.NULL else None + + +def textset_start_value(s: 'const Set *') -> str: + s_converted = _ffi.cast('const Set *', s) + result = _lib.textset_start_value(s_converted) + result = text2cstring(result) + return result if result != _ffi.NULL else None + + +def textset_value_n(s: 'const Set *', n: int) -> 'text **': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('text **') + result = _lib.textset_value_n(s_converted, n, out_result) + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def textset_values(s: 'const Set *') -> 'text **': + s_converted = _ffi.cast('const Set *', s) + result = _lib.textset_values(s_converted) + return result if result != _ffi.NULL else None + + def timestampset_end_timestamp(ts: 'const Set *') -> 'TimestampTz': ts_converted = _ffi.cast('const Set *', ts) result = _lib.timestampset_end_timestamp(ts_converted) @@ -1348,24 +1428,12 @@ def floatspanset_round(ss: 'const SpanSet *', maxdd: int) -> 'SpanSet *': return result if result != _ffi.NULL else None -def floatspan_set_intspan(s1: 'const Span *', s2: 'Span *') -> None: - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('Span *', s2) - _lib.floatspan_set_intspan(s1_converted, s2_converted) - - def geoset_round(s: 'const Set *', maxdd: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) result = _lib.geoset_round(s_converted, maxdd) return result if result != _ffi.NULL else None -def intspan_set_floatspan(s1: 'const Span *', s2: 'Span *') -> None: - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('Span *', s2) - _lib.intspan_set_floatspan(s1_converted, s2_converted) - - def period_tprecision(s: 'const Span *', duration: 'const Interval *', torigin: int) -> 'Span *': s_converted = _ffi.cast('const Span *', s) duration_converted = _ffi.cast('const Interval *', duration) @@ -1398,19 +1466,6 @@ def periodset_shift_tscale(ps: 'const SpanSet *', shift: "Optional['const Interv return result if result != _ffi.NULL else None -def set_shift(s: 'const Set *', shift: 'Datum') -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - shift_converted = _ffi.cast('Datum', shift) - result = _lib.set_shift(s_converted, shift_converted) - return result if result != _ffi.NULL else None - - -def span_expand(s1: 'const Span *', s2: 'Span *') -> None: - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('Span *', s2) - _lib.span_expand(s1_converted, s2_converted) - - def timestamp_tprecision(t: int, duration: 'const Interval *', torigin: int) -> 'TimestampTz': t_converted = _ffi.cast('TimestampTz', t) duration_converted = _ffi.cast('const Interval *', duration) @@ -2095,42 +2150,117 @@ def right_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bo return result if result != _ffi.NULL else None -def bbox_union_span_span(s1: 'const Span *', s2: 'const Span *') -> 'Span *': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - out_result = _ffi.new('Span *') - _lib.bbox_union_span_span(s1_converted, s2_converted, out_result) - return out_result if out_result!= _ffi.NULL else None +def intersection_bigintset_bigint(s: 'const Set *', i: int) -> 'int64': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintset_bigint(s_converted, i_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None +def intersection_bigintspan_bigint(s: 'const Span *', i: int) -> 'int64': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintspan_bigint(s_converted, i_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def intersection_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.intersection_set_set(s1_converted, s2_converted) - return result if result != _ffi.NULL else None + +def intersection_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'int64': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintspanset_bigint(ss_converted, i_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def intersection_period_timestamp(p: 'const Span *', t: int) -> int: - p_converted = _ffi.cast('const Span *', p) +def intersection_floatset_float(s: 'const Set *', d: float) -> 'double': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('double *') + result = _lib.intersection_floatset_float(s_converted, d, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_floatspan_float(s: 'const Span *', d: float) -> 'double': + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('double *') + result = _lib.intersection_floatspan_float(s_converted, d, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('double *') + result = _lib.intersection_floatspanset_float(ss_converted, d, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_intset_int(s: 'const Set *', i: int) -> 'int': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('int *') + result = _lib.intersection_intset_int(s_converted, i, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_intspan_int(s: 'const Span *', i: int) -> 'int': + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('int *') + result = _lib.intersection_intspan_int(s_converted, i, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_intspanset_int(ss: 'const SpanSet *', i: int) -> 'int': + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('int *') + result = _lib.intersection_intspanset_int(ss_converted, i, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_period_timestamp(s: 'const Span *', t: int) -> int: + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_period_timestamp(p_converted, t_converted, out_result) + result = _lib.intersection_period_timestamp(s_converted, t_converted, out_result) if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def intersection_periodset_timestamp(ps: 'const SpanSet *', t: int) -> int: - ps_converted = _ffi.cast('const SpanSet *', ps) +def intersection_periodset_timestamp(ss: 'const SpanSet *', t: int) -> int: + ss_converted = _ffi.cast('const SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_periodset_timestamp(ps_converted, t_converted, out_result) + result = _lib.intersection_periodset_timestamp(ss_converted, t_converted, out_result) if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None +def intersection_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.intersection_set_set(s1_converted, s2_converted) + return result if result != _ffi.NULL else None + + def intersection_span_span(s1: 'const Span *', s2: 'const Span *') -> 'Span *': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) @@ -2152,34 +2282,185 @@ def intersection_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') return result if result != _ffi.NULL else None -def intersection_timestampset_timestamp(ts: 'const Set *', t: int) -> int: - ts_converted = _ffi.cast('const Set *', ts) - t_converted = _ffi.cast('const TimestampTz', t) +def intersection_textset_text(s: 'const Set *', txt: str) -> 'text **': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + out_result = _ffi.new('text **') + result = _lib.intersection_textset_text(s_converted, txt_converted, out_result) + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def intersection_timestampset_timestamp(s: 'const Set *', t: int) -> int: + s_converted = _ffi.cast('const Set *', s) + t_converted = _ffi.cast('TimestampTz', t) out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_timestampset_timestamp(ts_converted, t_converted, out_result) + result = _lib.intersection_timestampset_timestamp(s_converted, t_converted, out_result) if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def minus_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.minus_set_set(s1_converted, s2_converted) +def minus_bigint_bigintset(i: int, s: 'const Set *') -> 'int64': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintset(i_converted, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_bigint_bigintspan(i: int, s: 'const Span *') -> 'int64': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintspan(i_converted, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'int64': + i_converted = _ffi.cast('int64', i) + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintspanset(i_converted, ss_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintset_bigint(s_converted, i_converted) return result if result != _ffi.NULL else None -def minus_period_timestamp(p: 'const Span *', t: int) -> 'SpanSet *': - p_converted = _ffi.cast('const Span *', p) +def minus_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintspan_bigint(s_converted, i_converted) + return result if result != _ffi.NULL else None + + +def minus_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintspanset_bigint(ss_converted, i_converted) + return result if result != _ffi.NULL else None + + +def minus_float_floatset(d: float, s: 'const Set *') -> 'double': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatset(d, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_float_floatspan(d: float, s: 'const Span *') -> 'double': + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatspan(d, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatspanset(d, ss_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_floatset_float(s: 'const Set *', d: float) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.minus_floatset_float(s_converted, d) + return result if result != _ffi.NULL else None + + +def minus_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.minus_floatspan_float(s_converted, d) + return result if result != _ffi.NULL else None + + +def minus_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.minus_floatspanset_float(ss_converted, d) + return result if result != _ffi.NULL else None + + +def minus_int_intset(i: int, s: 'const Set *') -> 'int': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('int *') + result = _lib.minus_int_intset(i, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_int_intspan(i: int, s: 'const Span *') -> 'int': + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('int *') + result = _lib.minus_int_intspan(i, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_int_intspanset(i: int, ss: 'const SpanSet *') -> 'int': + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('int *') + result = _lib.minus_int_intspanset(i, ss_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_intset_int(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.minus_intset_int(s_converted, i) + return result if result != _ffi.NULL else None + + +def minus_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.minus_intspan_int(s_converted, i) + return result if result != _ffi.NULL else None + + +def minus_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.minus_intspanset_int(ss_converted, i) + return result if result != _ffi.NULL else None + + +def minus_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_period_timestamp(p_converted, t_converted) + result = _lib.minus_period_timestamp(s_converted, t_converted) return result if result != _ffi.NULL else None -def minus_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'SpanSet *': - ps_converted = _ffi.cast('const SpanSet *', ps) +def minus_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_periodset_timestamp(ps_converted, t_converted) + result = _lib.minus_periodset_timestamp(ss_converted, t_converted) + return result if result != _ffi.NULL else None + + +def minus_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.minus_set_set(s1_converted, s2_converted) return result if result != _ffi.NULL else None @@ -2211,51 +2492,125 @@ def minus_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'Sp return result if result != _ffi.NULL else None -def minus_timestamp_period(t: int, p: 'const Span *') -> int: +def minus_text_textset(txt: str, s: 'const Set *') -> 'text **': + txt_converted = cstring2text(txt) + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('text **') + result = _lib.minus_text_textset(txt_converted, s_converted, out_result) + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def minus_textset_text(s: 'const Set *', txt: str) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.minus_textset_text(s_converted, txt_converted) + return result if result != _ffi.NULL else None + + +def minus_timestamp_period(t: int, s: 'const Span *') -> int: t_converted = _ffi.cast('TimestampTz', t) - p_converted = _ffi.cast('const Span *', p) + s_converted = _ffi.cast('const Span *', s) out_result = _ffi.new('TimestampTz *') - result = _lib.minus_timestamp_period(t_converted, p_converted, out_result) + result = _lib.minus_timestamp_period(t_converted, s_converted, out_result) if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def minus_timestamp_periodset(t: int, ps: 'const SpanSet *') -> int: +def minus_timestamp_periodset(t: int, ss: 'const SpanSet *') -> int: t_converted = _ffi.cast('TimestampTz', t) - ps_converted = _ffi.cast('const SpanSet *', ps) + ss_converted = _ffi.cast('const SpanSet *', ss) out_result = _ffi.new('TimestampTz *') - result = _lib.minus_timestamp_periodset(t_converted, ps_converted, out_result) + result = _lib.minus_timestamp_periodset(t_converted, ss_converted, out_result) if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def minus_timestampset_timestamp(ts: 'const Set *', t: int) -> 'Set *': - ts_converted = _ffi.cast('const Set *', ts) +def minus_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_timestampset_timestamp(ts_converted, t_converted) + result = _lib.minus_timestampset_timestamp(s_converted, t_converted) return result if result != _ffi.NULL else None -def union_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.union_set_set(s1_converted, s2_converted) +def union_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintset_bigint(s_converted, i_converted) return result if result != _ffi.NULL else None -def union_period_timestamp(p: 'const Span *', t: int) -> 'SpanSet *': - p_converted = _ffi.cast('const Span *', p) +def union_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintspan_bigint(s_converted, i_converted) + return result if result != _ffi.NULL else None + + +def union_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintspanset_bigint(ss_converted, i_converted) + return result if result != _ffi.NULL else None + + +def union_floatset_float(s: 'const Set *', d: float) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.union_floatset_float(s_converted, d) + return result if result != _ffi.NULL else None + + +def union_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.union_floatspan_float(s_converted, d) + return result if result != _ffi.NULL else None + + +def union_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.union_floatspanset_float(ss_converted, d) + return result if result != _ffi.NULL else None + + +def union_intset_int(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.union_intset_int(s_converted, i) + return result if result != _ffi.NULL else None + + +def union_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.union_intspan_int(s_converted, i) + return result if result != _ffi.NULL else None + + +def union_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.union_intspanset_int(ss_converted, i) + return result if result != _ffi.NULL else None + + +def union_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.union_period_timestamp(p_converted, t_converted) + result = _lib.union_period_timestamp(s_converted, t_converted) return result if result != _ffi.NULL else None -def union_periodset_timestamp(ps: 'SpanSet *', t: int) -> 'SpanSet *': - ps_converted = _ffi.cast('SpanSet *', ps) +def union_periodset_timestamp(ss: 'SpanSet *', t: int) -> 'SpanSet *': + ss_converted = _ffi.cast('SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.union_periodset_timestamp(ps_converted, t_converted) + result = _lib.union_periodset_timestamp(ss_converted, t_converted) + return result if result != _ffi.NULL else None + + +def union_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.union_set_set(s1_converted, s2_converted) return result if result != _ffi.NULL else None @@ -2280,10 +2635,17 @@ def union_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'Sp return result if result != _ffi.NULL else None -def union_timestampset_timestamp(ts: 'const Set *', t: int) -> 'Set *': - ts_converted = _ffi.cast('const Set *', ts) +def union_textset_text(s: 'const Set *', txt: str) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.union_textset_text(s_converted, txt_converted) + return result if result != _ffi.NULL else None + + +def union_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('const TimestampTz', t) - result = _lib.union_timestampset_timestamp(ts_converted, t_converted) + result = _lib.union_timestampset_timestamp(s_converted, t_converted) return result if result != _ffi.NULL else None @@ -2722,43 +3084,29 @@ def stbox_out(box: 'const STBox *', maxdd: int) -> str: return result if result != _ffi.NULL else None -def tbox_make(s: "Optional['const Span *']", p: "Optional['const Span *']") -> 'TBox *': - s_converted = _ffi.cast('const Span *', s) if s is not None else _ffi.NULL +def stbox_make(hasx: bool, hasz: bool, geodetic: bool, srid: int, xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, p: "Optional['const Span *']") -> 'STBox *': + srid_converted = _ffi.cast('int32', srid) p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL - result = _lib.tbox_make(s_converted, p_converted) + result = _lib.stbox_make(hasx, hasz, geodetic, srid_converted, xmin, xmax, ymin, ymax, zmin, zmax, p_converted) return result if result != _ffi.NULL else None -def tbox_set(s: 'const Span *', p: 'const Span *', box: 'TBox *') -> None: - s_converted = _ffi.cast('const Span *', s) - p_converted = _ffi.cast('const Span *', p) - box_converted = _ffi.cast('TBox *', box) - _lib.tbox_set(s_converted, p_converted, box_converted) - - -def tbox_copy(box: 'const TBox *') -> 'TBox *': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_copy(box_converted) +def stbox_copy(box: 'const STBox *') -> 'STBox *': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_copy(box_converted) return result if result != _ffi.NULL else None -def stbox_make(hasx: bool, hasz: bool, geodetic: bool, srid: int, xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, p: "Optional['const Span *']") -> 'STBox *': - srid_converted = _ffi.cast('int32', srid) +def tbox_make(s: "Optional['const Span *']", p: "Optional['const Span *']") -> 'TBox *': + s_converted = _ffi.cast('const Span *', s) if s is not None else _ffi.NULL p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL - result = _lib.stbox_make(hasx, hasz, geodetic, srid_converted, xmin, xmax, ymin, ymax, zmin, zmax, p_converted) + result = _lib.tbox_make(s_converted, p_converted) return result if result != _ffi.NULL else None -def stbox_set(hasx: bool, hasz: bool, geodetic: bool, srid: int, xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, p: 'const Span *', box: 'STBox *') -> None: - srid_converted = _ffi.cast('int32', srid) - p_converted = _ffi.cast('const Span *', p) - box_converted = _ffi.cast('STBox *', box) - _lib.stbox_set(hasx, hasz, geodetic, srid_converted, xmin, xmax, ymin, ymax, zmin, zmax, p_converted, box_converted) - - -def stbox_copy(box: 'const STBox *') -> 'STBox *': - box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_copy(box_converted) +def tbox_copy(box: 'const TBox *') -> 'TBox *': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_copy(box_converted) return result if result != _ffi.NULL else None @@ -3124,12 +3472,6 @@ def stbox_srid(box: 'const STBox *') -> 'int32': return result if result != _ffi.NULL else None -def stbox_expand(box1: 'const STBox *', box2: 'STBox *') -> None: - box1_converted = _ffi.cast('const STBox *', box1) - box2_converted = _ffi.cast('STBox *', box2) - _lib.stbox_expand(box1_converted, box2_converted) - - def stbox_expand_space(box: 'const STBox *', d: float) -> 'STBox *': box_converted = _ffi.cast('const STBox *', box) result = _lib.stbox_expand_space(box_converted, d) @@ -3162,12 +3504,6 @@ def stbox_set_srid(box: 'const STBox *', srid: int) -> 'STBox *': return result if result != _ffi.NULL else None -def tbox_expand(box1: 'const TBox *', box2: 'TBox *') -> None: - box1_converted = _ffi.cast('const TBox *', box1) - box2_converted = _ffi.cast('TBox *', box2) - _lib.tbox_expand(box1_converted, box2_converted) - - def tbox_expand_value(box: 'const TBox *', d: 'const double') -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) d_converted = _ffi.cast('const double', d) @@ -4146,13 +4482,6 @@ def temporal_timestamps(temp: 'const Temporal *') -> "Tuple['TimestampTz *', 'in return result if result != _ffi.NULL else None, count[0] -def temporal_values(temp: 'const Temporal *') -> "Tuple['Datum *', 'int']": - temp_converted = _ffi.cast('const Temporal *', temp) - count = _ffi.new('int *') - result = _lib.temporal_values(temp_converted, count) - return result if result != _ffi.NULL else None, count[0] - - def tfloat_end_value(temp: 'const Temporal *') -> 'double': temp_converted = _ffi.cast('const Temporal *', temp) result = _lib.tfloat_end_value(temp_converted) @@ -5198,20 +5527,6 @@ def tint_ever_lt(temp: 'const Temporal *', i: int) -> 'bool': return result if result != _ffi.NULL else None -def tpoint_always_eq(temp: 'const Temporal *', value: 'Datum') -> 'bool': - temp_converted = _ffi.cast('const Temporal *', temp) - value_converted = _ffi.cast('Datum', value) - result = _lib.tpoint_always_eq(temp_converted, value_converted) - return result if result != _ffi.NULL else None - - -def tpoint_ever_eq(temp: 'const Temporal *', value: 'Datum') -> 'bool': - temp_converted = _ffi.cast('const Temporal *', temp) - value_converted = _ffi.cast('Datum', value) - result = _lib.tpoint_ever_eq(temp_converted, value_converted) - return result if result != _ffi.NULL else None - - def ttext_always_eq(temp: 'const Temporal *', txt: str) -> 'bool': temp_converted = _ffi.cast('const Temporal *', temp) txt_converted = cstring2text(txt) @@ -5315,30 +5630,16 @@ def teq_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': return result if result != _ffi.NULL else None -def teq_geo_tpoint(geo: 'const GSERIALIZED *', tpoint: 'const Temporal *') -> 'Temporal *': - geo_converted = _ffi.cast('const GSERIALIZED *', geo) - tpoint_converted = _ffi.cast('const Temporal *', tpoint) - result = _lib.teq_geo_tpoint(geo_converted, tpoint_converted) - return result if result != _ffi.NULL else None - - def teq_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) result = _lib.teq_int_tint(i, temp_converted) return result if result != _ffi.NULL else None -def teq_point_tgeogpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': +def teq_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_point_tgeogpoint(gs_converted, temp_converted) - return result if result != _ffi.NULL else None - - -def teq_point_tgeompoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_point_tgeompoint(gs_converted, temp_converted) + result = _lib.teq_point_tpoint(gs_converted, temp_converted) return result if result != _ffi.NULL else None @@ -5368,17 +5669,10 @@ def teq_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': return result if result != _ffi.NULL else None -def teq_tgeogpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': +def teq_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.teq_tgeogpoint_point(temp_converted, gs_converted) - return result if result != _ffi.NULL else None - - -def teq_tgeompoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.teq_tgeompoint_point(temp_converted, gs_converted) + result = _lib.teq_tpoint_point(temp_converted, gs_converted) return result if result != _ffi.NULL else None @@ -5388,13 +5682,6 @@ def teq_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': return result if result != _ffi.NULL else None -def teq_tpoint_geo(tpoint: 'const Temporal *', geo: 'const GSERIALIZED *') -> 'Temporal *': - tpoint_converted = _ffi.cast('const Temporal *', tpoint) - geo_converted = _ffi.cast('const GSERIALIZED *', geo) - result = _lib.teq_tpoint_geo(tpoint_converted, geo_converted) - return result if result != _ffi.NULL else None - - def teq_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) txt_converted = cstring2text(txt) @@ -5594,30 +5881,16 @@ def tne_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': return result if result != _ffi.NULL else None -def tne_geo_tpoint(geo: 'const GSERIALIZED *', tpoint: 'const Temporal *') -> 'Temporal *': - geo_converted = _ffi.cast('const GSERIALIZED *', geo) - tpoint_converted = _ffi.cast('const Temporal *', tpoint) - result = _lib.tne_geo_tpoint(geo_converted, tpoint_converted) - return result if result != _ffi.NULL else None - - def tne_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) result = _lib.tne_int_tint(i, temp_converted) return result if result != _ffi.NULL else None -def tne_point_tgeogpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_point_tgeogpoint(gs_converted, temp_converted) - return result if result != _ffi.NULL else None - - -def tne_point_tgeompoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': +def tne_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_point_tgeompoint(gs_converted, temp_converted) + result = _lib.tne_point_tpoint(gs_converted, temp_converted) return result if result != _ffi.NULL else None @@ -5647,17 +5920,10 @@ def tne_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': return result if result != _ffi.NULL else None -def tne_tgeogpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.tne_tgeogpoint_point(temp_converted, gs_converted) - return result if result != _ffi.NULL else None - - -def tne_tgeompoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': +def tne_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.tne_tgeompoint_point(temp_converted, gs_converted) + result = _lib.tne_tpoint_point(temp_converted, gs_converted) return result if result != _ffi.NULL else None @@ -5667,13 +5933,6 @@ def tne_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': return result if result != _ffi.NULL else None -def tne_tpoint_geo(tpoint: 'const Temporal *', geo: 'const GSERIALIZED *') -> 'Temporal *': - tpoint_converted = _ffi.cast('const Temporal *', tpoint) - geo_converted = _ffi.cast('const GSERIALIZED *', geo) - result = _lib.tne_tpoint_geo(tpoint_converted, geo_converted) - return result if result != _ffi.NULL else None - - def tne_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) txt_converted = cstring2text(txt) From 6073f65987a19a6871baee1b7803df67b4b8c0c6 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 22 Aug 2023 16:12:17 +0200 Subject: [PATCH 003/101] Update *set_make functions and remove unused modifiers --- pymeos/pymeos/main/tint.py | 8 +- pymeos_cffi/pymeos_cffi/__init__.py | 86 ++- .../builder/build_pymeos_functions.py | 47 +- .../build_pymeos_functions_modifiers.py | 69 +- pymeos_cffi/pymeos_cffi/builder/meos.h | 104 ++- pymeos_cffi/pymeos_cffi/functions.py | 645 +++++++++++++----- 6 files changed, 654 insertions(+), 305 deletions(-) diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index ff8a7b0c..9f720103 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -667,13 +667,7 @@ def minus(self, other: Union[int, List[int], if isinstance(other, int): result = tint_minus_value(self._inner, other) elif isinstance(other, list) and isinstance(other[0], int): - # result = reduce(tint_minus_value, other, self._inner) - # result = tint_minus_values(self._inner, other) - # results = [tint_minus_value(self._inner, value) for value in other if other is not None] - # result = temporal_merge_array(results, len(results)) - result = tint_minus_value(self._inner, other) - for i in 1..len(other): - result = result.minus_value(other[i]) + result = temporal_minus_values(self._inner, intset_make(other, len(other))) else: return super().minus(other) return Temporal._factory(result) diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index cfe3428a..a2b4d0fc 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -62,9 +62,10 @@ 'gserialized_as_text', 'gserialized_from_ewkb', 'gserialized_from_geojson', - 'gserialized_from_hexewkb', - 'gserialized_from_text', - 'gserialized_in', + 'geometry_from_text', + 'geography_from_text', + 'pgis_geography_in', + 'pgis_geometry_in', 'gserialized_out', 'pgis_gserialized_same', 'bigintset_in', @@ -80,9 +81,8 @@ 'floatspanset_in', 'floatspanset_out', 'geogset_in', - 'geogset_out', + 'geoset_out', 'geomset_in', - 'geomset_out', 'geoset_as_ewkt', 'geoset_as_text', 'intset_in', @@ -118,8 +118,7 @@ 'bigintspan_make', 'floatset_make', 'floatspan_make', - 'geogset_make', - 'geomset_make', + 'geoset_make', 'intset_make', 'intspan_make', 'period_make', @@ -137,11 +136,14 @@ 'float_to_floatset', 'float_to_floatspan', 'float_to_floatspanset', + 'geog_to_geogset', + 'geom_to_geomset', 'int_to_intset', 'int_to_intspan', 'int_to_intspanset', 'set_to_spanset', 'span_to_spanset', + 'text_to_textset', 'timestamp_to_period', 'timestamp_to_periodset', 'timestamp_to_tstzset', @@ -161,7 +163,11 @@ 'floatspan_upper', 'floatspanset_lower', 'floatspanset_upper', + 'geoset_end_value', 'geoset_srid', + 'geoset_start_value', + 'geoset_value_n', + 'geoset_values', 'intset_end_value', 'intset_start_value', 'intset_value_n', @@ -204,6 +210,10 @@ 'spanset_upper_inc', 'spanset_width', 'spatialset_stbox', + 'textset_end_value', + 'textset_start_value', + 'textset_value_n', + 'textset_values', 'timestampset_end_timestamp', 'timestampset_start_timestamp', 'timestampset_timestamp_n', @@ -211,15 +221,11 @@ 'floatset_round', 'floatspan_round', 'floatspanset_round', - 'floatspan_set_intspan', 'geoset_round', - 'intspan_set_floatspan', 'period_tprecision', 'periodset_tprecision', 'period_shift_tscale', 'periodset_shift_tscale', - 'set_shift', - 'span_expand', 'timestamp_tprecision', 'timestampset_shift_tscale', 'adjacent_bigintspan_bigint', @@ -322,30 +328,69 @@ 'right_span_spanset', 'right_spanset_span', 'right_spanset_spanset', - 'bbox_union_span_span', - 'intersection_set_set', + 'intersection_bigintset_bigint', + 'intersection_bigintspan_bigint', + 'intersection_bigintspanset_bigint', + 'intersection_floatset_float', + 'intersection_floatspan_float', + 'intersection_floatspanset_float', + 'intersection_intset_int', + 'intersection_intspan_int', + 'intersection_intspanset_int', 'intersection_period_timestamp', 'intersection_periodset_timestamp', + 'intersection_set_set', 'intersection_span_span', 'intersection_spanset_span', 'intersection_spanset_spanset', + 'intersection_textset_text', 'intersection_timestampset_timestamp', - 'minus_set_set', + 'minus_bigint_bigintset', + 'minus_bigint_bigintspan', + 'minus_bigint_bigintspanset', + 'minus_bigintset_bigint', + 'minus_bigintspan_bigint', + 'minus_bigintspanset_bigint', + 'minus_float_floatset', + 'minus_float_floatspan', + 'minus_float_floatspanset', + 'minus_floatset_float', + 'minus_floatspan_float', + 'minus_floatspanset_float', + 'minus_int_intset', + 'minus_int_intspan', + 'minus_int_intspanset', + 'minus_intset_int', + 'minus_intspan_int', + 'minus_intspanset_int', 'minus_period_timestamp', 'minus_periodset_timestamp', + 'minus_set_set', 'minus_span_span', 'minus_span_spanset', 'minus_spanset_span', 'minus_spanset_spanset', + 'minus_text_textset', + 'minus_textset_text', 'minus_timestamp_period', 'minus_timestamp_periodset', 'minus_timestampset_timestamp', - 'union_set_set', + 'union_bigintset_bigint', + 'union_bigintspan_bigint', + 'union_bigintspanset_bigint', + 'union_floatset_float', + 'union_floatspan_float', + 'union_floatspanset_float', + 'union_intset_int', + 'union_intspan_int', + 'union_intspanset_int', 'union_period_timestamp', 'union_periodset_timestamp', + 'union_set_set', 'union_span_span', 'union_spanset_span', 'union_spanset_spanset', + 'union_textset_text', 'union_timestampset_timestamp', 'distance_floatspan_float', 'distance_intspan_int', @@ -410,12 +455,10 @@ 'stbox_as_hexwkb', 'stbox_in', 'stbox_out', - 'tbox_make', - 'tbox_set', - 'tbox_copy', 'stbox_make', - 'stbox_set', 'stbox_copy', + 'tbox_make', + 'tbox_copy', 'int_to_tbox', 'float_to_tbox', 'timestamp_to_tbox', @@ -467,13 +510,11 @@ 'stbox_tmax', 'stbox_tmax_inc', 'stbox_srid', - 'stbox_expand', 'stbox_expand_space', 'stbox_expand_time', 'stbox_get_space', 'stbox_round', 'stbox_set_srid', - 'tbox_expand', 'tbox_expand_value', 'tbox_expand_time', 'tbox_round', @@ -618,7 +659,6 @@ 'temporal_time', 'temporal_timestamp_n', 'temporal_timestamps', - 'temporal_values', 'tfloat_end_value', 'tfloat_max_value', 'tfloat_min_value', @@ -775,8 +815,6 @@ 'tint_ever_eq', 'tint_ever_le', 'tint_ever_lt', - 'tpoint_always_eq', - 'tpoint_ever_eq', 'ttext_always_eq', 'ttext_always_le', 'ttext_always_lt', diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index 28e9464f..d8f95c43 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -147,18 +147,7 @@ def as_tsequenceset(temporal: 'Temporal *') -> 'TSequenceSet *': 'cstring2text': cstring2text_modifier, 'text2cstring': text2cstring_modifier, 'timestampset_make': timestampset_make_modifier, - 'tint_at_values': tint_at_values_modifier, - 'tint_minus_values': tint_minus_values_modifier, - 'tfloat_at_values': tfloat_at_values_modifier, - 'tfloat_minus_values': tfloat_minus_values_modifier, - 'tbool_at_values': tbool_at_values_modifier, - 'tbool_minus_values': tbool_minus_values_modifier, - 'ttext_at_values': array_length_remover_modifier('values_converted'), - 'ttext_minus_values': array_length_remover_modifier('values_converted'), - 'tpoint_at_values': array_length_remover_modifier('values_converted'), - 'tpoint_minus_values': array_length_remover_modifier('values_converted'), 'gserialized_from_lwgeom': gserialized_from_lwgeom_modifier, - 'tpointseq_make_coords': tpointseq_make_coords_modifier, 'spanset_make': spanset_make_modifier, 'temporal_from_wkb': from_wkb_modifier('temporal_from_wkb', 'Temporal'), 'set_from_wkb': from_wkb_modifier('set_from_wkb', 'Set'), @@ -172,6 +161,11 @@ def as_tsequenceset(temporal: 'Temporal *') -> 'TSequenceSet *': 'spanset_as_wkb': as_wkb_modifier, 'tbox_as_wkb': as_wkb_modifier, 'stbox_as_wkb': as_wkb_modifier, + 'intset_make': array_parameter_modifier('values', 'count'), + 'bigintset_make': array_parameter_modifier('values', 'count'), + 'floatset_make': array_parameter_modifier('values', 'count'), + 'textset_make': array_parameter_modifier('values', 'count'), + 'geoset_make': array_length_remover_modifier('values', 'count'), } # List of result function parameters in tuples of (function, parameter) @@ -221,7 +215,6 @@ def as_tsequenceset(temporal: 'Temporal *') -> 'TSequenceSet *': ('tbox_make', 'p'), ('tbox_make', 's'), ('stbox_make', 'p'), - ('tpointseq_make_coords', 'zcoords'), ('temporal_tcount_transfn', 'state'), ('temporal_extent_transfn', 'p'), ('tnumber_extent_transfn', 'box'), @@ -243,13 +236,6 @@ def as_tsequenceset(temporal: 'Temporal *') -> 'TSequenceSet *': ('period_tcount_transfn', 'interval'), ('periodset_tcount_transfn', 'interval'), ('timestamp_extent_transfn', 'p'), - ('timestampset_extent_transfn', 'p'), - ('period_extent_transfn', 'p'), - ('periodset_extent_transfn', 'p'), - ('timestamp_tunion_transfn', 'state'), - ('timestampset_tunion_transfn', 'state'), - ('period_tunion_transfn', 'state'), - ('periodset_tunion_transfn', 'state'), ('timestamp_tcount_transfn', 'state'), ('timestampset_tcount_transfn', 'state'), ('period_tcount_transfn', 'state'), @@ -281,6 +267,21 @@ def is_output_parameter(function: str, parameter: Parameter) -> bool: return (function, parameter.name) in output_parameters +def check_modifiers(functions: List[str]) -> None: + for func in function_modifiers.keys(): + if func not in functions: + print(f'Modifier defined for non-existent function {func}') + for func, param in result_parameters: + if func not in functions: + print(f'Result parameter defined for non-existent function {func} ({param})') + for func, param in output_parameters: + if func not in functions: + print(f'Output parameter defined for non-existent function {func} ({param})') + for func, param in nullable_parameters: + if func not in functions: + print(f'Nullable Parameter defined for non-existent function {func} ({param})') + + def main(): with open('pymeos_cffi/builder/meos.h') as f: content = f.read() @@ -310,15 +311,19 @@ def main(): file.write(function_string) file.write('\n\n\n') + functions = [] with open('pymeos_cffi/functions.py', 'r') as funcs, open('pymeos_cffi/__init__.py', 'w+') as init: content = funcs.read() - f_names = re.finditer(r'def (\w+)\(', content) + matches = list(re.finditer(r'def (\w+)\(', content)) init.write('from .functions import *\n\n') init.write('__all__ = [\n') - for fn in f_names: + for fn in matches: + functions.append(fn.group(1)) init.write(f" '{fn.group(1)}',\n") init.write(']\n') + check_modifiers(functions) + def get_params(function: str, inner_params: str) -> List[Parameter]: return [p for p in (get_param(function, param.strip()) for param in inner_params.split(',')) if p is not None] diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py index 91fe41eb..211e8e1d 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py @@ -1,4 +1,5 @@ -from typing import Callable +import re +from typing import Callable, Optional def array_length_remover_modifier(list_name: str, length_param_name: str = 'count') -> Callable[[str], str]: @@ -7,6 +8,24 @@ def array_length_remover_modifier(list_name: str, length_param_name: str = 'coun .replace(f', {length_param_name}', f', len({list_name})') +def array_parameter_modifier(list_name: str, length_param_name: Optional[str] = None) -> Callable[[str], str]: + def custom_array_modifier(function: str) -> str: + type_regex = list_name + r": '([\w \*]+)'" + match = next(re.finditer(type_regex, function)) + whole_type = match.group(1) + base_type = ' '.join(whole_type.split(' ')[:-1]) + function = function \ + .replace(match.group(0), f"{list_name}: 'List[{base_type}]'") \ + .replace(f"_ffi.cast('{whole_type}', {list_name})", f"_ffi.new('{base_type} []', {list_name})") + if length_param_name: + function = function \ + .replace(f', {length_param_name}: int', '') \ + .replace(f', {length_param_name}', f', len({list_name})') + return function + + return custom_array_modifier + + def cstring2text_modifier(_: str) -> str: return """def cstring2text(cstring: str) -> 'text *': cstring_converted = cstring.encode('utf-8') @@ -43,42 +62,6 @@ def timestampset_make_modifier(function: str) -> str: "values_converted = [_ffi.cast('const TimestampTz', x) for x in values]") -def tbool_at_values_modifier(function: str) -> str: - return function \ - .replace("values: 'bool *', count: int", 'values: List[bool]') \ - .replace("_ffi.cast('bool *', values)", - "_ffi.new('bool []', values)") \ - .replace(', count', ', len(values_converted)') - - -def tbool_minus_values_modifier(function: str) -> str: - return tbool_at_values_modifier(function) - - -def tint_at_values_modifier(function: str) -> str: - return function \ - .replace("values: 'int *', count: int", 'values: List[int]') \ - .replace("_ffi.cast('int *', values)", - "_ffi.new('int []', values)") \ - .replace(', count', ', len(values_converted)') - - -def tint_minus_values_modifier(function: str) -> str: - return tint_at_values_modifier(function) - - -def tfloat_at_values_modifier(function: str) -> str: - return function \ - .replace("values: 'double *', count: int", 'values: List[float]') \ - .replace("_ffi.cast('double *', values)", - "_ffi.new('double []', values)") \ - .replace(', count', ', len(values_converted)') - - -def tfloat_minus_values_modifier(function: str) -> str: - return tfloat_at_values_modifier(function) - - def spanset_make_modifier(function: str) -> str: return function \ .replace("spans: 'Span *', count: int", "spans: 'List[Span *]'") \ @@ -91,15 +74,3 @@ def gserialized_from_lwgeom_modifier(function: str) -> str: return function \ .replace(", size: 'size_t *'", '') \ .replace("_ffi.cast('size_t *', size)", '_ffi.NULL') - - -def tpointseq_make_coords_modifier(function: str) -> str: - return function \ - .replace('times: int', "times: 'const TimestampTz *'") \ - .replace(" xcoords_converted = _ffi.cast('const double *', xcoords)\n", '') \ - .replace(" ycoords_converted = _ffi.cast('const double *', ycoords)\n", '') \ - .replace(" times_converted = _ffi.cast('const TimestampTz *', times)\n", '') \ - .replace("_ffi.cast('const double *', zcoords)", 'zcoords') \ - .replace('xcoords_converted', 'xcoords') \ - .replace('ycoords_converted', 'ycoords') \ - .replace('times_converted', 'times') diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 7f6658b3..709baf7a 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -780,9 +780,11 @@ extern char *gserialized_as_hexewkb(const GSERIALIZED *geom, const char *type); extern char *gserialized_as_text(const GSERIALIZED *geom, int precision); extern GSERIALIZED *gserialized_from_ewkb(const bytea *bytea_wkb, int32 srid); extern GSERIALIZED *gserialized_from_geojson(const char *geojson); -extern GSERIALIZED *gserialized_from_hexewkb(const char *wkt); -extern GSERIALIZED *gserialized_from_text(char *wkt, int srid); -extern GSERIALIZED *gserialized_in(char *input, int32 geom_typmod); + +extern GSERIALIZED *geometry_from_text(char *wkt, int srid); +extern GSERIALIZED *geography_from_text(char *wkt, int srid); +extern GSERIALIZED *pgis_geography_in(char *input, int32 geom_typmod); +extern GSERIALIZED *pgis_geometry_in(char *input, int32 geom_typmod); extern char *gserialized_out(const GSERIALIZED *geom); extern bool pgis_gserialized_same(const GSERIALIZED *geom1, const GSERIALIZED *geom2); @@ -805,9 +807,8 @@ extern char *floatspan_out(const Span *s, int maxdd); extern SpanSet *floatspanset_in(const char *str); extern char *floatspanset_out(const SpanSet *ss, int maxdd); extern Set *geogset_in(const char *str); -extern char *geogset_out(const Set *set, int maxdd); +extern char *geoset_out(const Set *set, int maxdd); extern Set *geomset_in(const char *str); -extern char *geomset_out(const Set *set, int maxdd); extern char *geoset_as_ewkt(const Set *set, int maxdd); extern char *geoset_as_text(const Set *set, int maxdd); extern Set *intset_in(const char *str); @@ -848,8 +849,7 @@ extern Set *bigintset_make(const int64 *values, int count); extern Span *bigintspan_make(int64 lower, int64 upper, bool lower_inc, bool upper_inc); extern Set *floatset_make(const double *values, int count); extern Span *floatspan_make(double lower, double upper, bool lower_inc, bool upper_inc); -extern Set *geogset_make(const GSERIALIZED **values, int count); -extern Set *geomset_make(const GSERIALIZED **values, int count); +extern Set *geoset_make(const GSERIALIZED **values, int count); extern Set *intset_make(const int *values, int count); extern Span *intspan_make(int lower, int upper, bool lower_inc, bool upper_inc); extern Span *period_make(TimestampTz lower, TimestampTz upper, bool lower_inc, bool upper_inc); @@ -872,11 +872,14 @@ extern SpanSet *bigint_to_bigintspanset(int i); extern Set *float_to_floatset(double d); extern Span *float_to_floatspan(double d); extern SpanSet *float_to_floatspanset(double d); +extern Set *geog_to_geogset(GSERIALIZED *gs); +extern Set *geom_to_geomset(GSERIALIZED *gs); extern Set *int_to_intset(int i); extern Span *int_to_intspan(int i); extern SpanSet *int_to_intspanset(int i); extern SpanSet *set_to_spanset(const Set *s); extern SpanSet *span_to_spanset(const Span *s); +extern Set *text_to_textset(text *txt); extern Span *timestamp_to_period(TimestampTz t); extern SpanSet *timestamp_to_periodset(TimestampTz t); extern Set *timestamp_to_tstzset(TimestampTz t); @@ -901,7 +904,11 @@ extern double floatspan_lower(const Span *s); extern double floatspan_upper(const Span *s); extern double floatspanset_lower(const SpanSet *ss); extern double floatspanset_upper(const SpanSet *ss); +extern GSERIALIZED *geoset_end_value(const Set *s); extern int geoset_srid(const Set *set); +extern GSERIALIZED *geoset_start_value(const Set *s); +extern bool geoset_value_n(const Set *s, int n, GSERIALIZED **result); +extern GSERIALIZED **geoset_values(const Set *s); extern int intset_end_value(const Set *s); extern int intset_start_value(const Set *s); extern bool intset_value_n(const Set *s, int n, int *result); @@ -944,6 +951,10 @@ extern Span *spanset_start_span(const SpanSet *ss); extern bool spanset_upper_inc(const SpanSet *ss); extern double spanset_width(const SpanSet *ss); extern STBox *spatialset_stbox(const Set *s); +extern text *textset_end_value(const Set *s); +extern text *textset_start_value(const Set *s); +extern bool textset_value_n(const Set *s, int n, text **result); +extern text **textset_values(const Set *s); extern TimestampTz timestampset_end_timestamp(const Set *ts); extern TimestampTz timestampset_start_timestamp(const Set *ts); extern bool timestampset_timestamp_n(const Set *ts, int n, TimestampTz *result); @@ -956,15 +967,11 @@ extern TimestampTz *timestampset_values(const Set *ts); extern Set *floatset_round(const Set *s, int maxdd); extern Span *floatspan_round(const Span *s, int maxdd); extern SpanSet *floatspanset_round(const SpanSet *ss, int maxdd); -extern void floatspan_set_intspan(const Span *s1, Span *s2); extern Set *geoset_round(const Set *s, int maxdd); -extern void intspan_set_floatspan(const Span *s1, Span *s2); extern Span *period_tprecision(const Span *s, const Interval *duration, TimestampTz torigin); extern SpanSet *periodset_tprecision(const SpanSet *ss, const Interval *duration, TimestampTz torigin); extern Span *period_shift_tscale(const Span *p, const Interval *shift, const Interval *duration); extern SpanSet *periodset_shift_tscale(const SpanSet *ps, const Interval *shift, const Interval *duration); -extern Set *set_shift(const Set *s, Datum shift); -extern void span_expand(const Span *s1, Span *s2); extern TimestampTz timestamp_tprecision(TimestampTz t, const Interval *duration, TimestampTz torigin); extern Set *timestampset_shift_tscale(const Set *ts, const Interval *shift, const Interval *duration); @@ -1084,31 +1091,70 @@ extern bool right_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern void bbox_union_span_span(const Span *s1, const Span *s2, Span *result); +extern bool intersection_bigintset_bigint(const Set *s, int64 i, int64 *result); +extern bool intersection_bigintspan_bigint(const Span *s, int64 i, int64 *result); +extern bool intersection_bigintspanset_bigint(const SpanSet *ss, int64 i, int64 *result); +extern bool intersection_floatset_float(const Set *s, double d, double *result); +extern bool intersection_floatspan_float(const Span *s, double d, double *result); +extern bool intersection_floatspanset_float(const SpanSet *ss, double d, double *result); +extern bool intersection_intset_int(const Set *s, int i, int *result); +extern bool intersection_intspan_int(const Span *s, int i, int *result); +extern bool intersection_intspanset_int(const SpanSet *ss, int i, int *result); +extern bool intersection_period_timestamp(const Span *s, TimestampTz t, TimestampTz *result); +extern bool intersection_periodset_timestamp(const SpanSet *ss, TimestampTz t, TimestampTz *result); extern Set *intersection_set_set(const Set *s1, const Set *s2); -extern bool intersection_period_timestamp(const Span *p, TimestampTz t, TimestampTz *result); -extern bool intersection_periodset_timestamp(const SpanSet *ps, TimestampTz t, TimestampTz *result); extern Span *intersection_span_span(const Span *s1, const Span *s2); extern SpanSet *intersection_spanset_span(const SpanSet *ss, const Span *s); extern SpanSet *intersection_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool intersection_timestampset_timestamp(const Set *ts, const TimestampTz t, TimestampTz *result); +extern bool intersection_textset_text(const Set *s, const text *txt, text **result); +extern bool intersection_timestampset_timestamp(const Set *s, TimestampTz t, TimestampTz *result); +extern bool minus_bigint_bigintset(int64 i, const Set *s, int64 *result); +extern bool minus_bigint_bigintspan(int64 i, const Span *s, int64 *result); +extern bool minus_bigint_bigintspanset(int64 i, const SpanSet *ss, int64 *result); +extern Set *minus_bigintset_bigint(const Set *s, int64 i); +extern SpanSet *minus_bigintspan_bigint(const Span *s, int64 i); +extern SpanSet *minus_bigintspanset_bigint(const SpanSet *ss, int64 i); +extern bool minus_float_floatset(double d, const Set *s, double *result); +extern bool minus_float_floatspan(double d, const Span *s, double *result); +extern bool minus_float_floatspanset(double d, const SpanSet *ss, double *result); +extern Set *minus_floatset_float(const Set *s, double d); +extern SpanSet *minus_floatspan_float(const Span *s, double d); +extern SpanSet *minus_floatspanset_float(const SpanSet *ss, double d); +extern bool minus_int_intset(int i, const Set *s, int *result); +extern bool minus_int_intspan(int i, const Span *s, int *result); +extern bool minus_int_intspanset(int i, const SpanSet *ss, int *result); +extern Set *minus_intset_int(const Set *s, int i); +extern SpanSet *minus_intspan_int(const Span *s, int i); +extern SpanSet *minus_intspanset_int(const SpanSet *ss, int i); +extern SpanSet *minus_period_timestamp(const Span *s, TimestampTz t); +extern SpanSet *minus_periodset_timestamp(const SpanSet *ss, TimestampTz t); extern Set *minus_set_set(const Set *s1, const Set *s2); -extern SpanSet *minus_period_timestamp(const Span *p, TimestampTz t); -extern SpanSet *minus_periodset_timestamp(const SpanSet *ps, TimestampTz t); extern SpanSet *minus_span_span(const Span *s1, const Span *s2); extern SpanSet *minus_span_spanset(const Span *s, const SpanSet *ss); extern SpanSet *minus_spanset_span(const SpanSet *ss, const Span *s); extern SpanSet *minus_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool minus_timestamp_period(TimestampTz t, const Span *p, TimestampTz *result); -extern bool minus_timestamp_periodset(TimestampTz t, const SpanSet *ps, TimestampTz *result); -extern Set *minus_timestampset_timestamp(const Set *ts, TimestampTz t); +extern bool minus_text_textset(const text *txt, const Set *s, text **result); +extern Set *minus_textset_text(const Set *s, const text *txt); +extern bool minus_timestamp_period(TimestampTz t, const Span *s, TimestampTz *result); +extern bool minus_timestamp_periodset(TimestampTz t, const SpanSet *ss, TimestampTz *result); +extern Set *minus_timestampset_timestamp(const Set *s, TimestampTz t); +extern Set *union_bigintset_bigint(const Set *s, int64 i); +extern SpanSet *union_bigintspan_bigint(const Span *s, int64 i); +extern SpanSet *union_bigintspanset_bigint(const SpanSet *ss, int64 i); +extern Set *union_floatset_float(const Set *s, double d); +extern SpanSet *union_floatspan_float(const Span *s, double d); +extern SpanSet *union_floatspanset_float(const SpanSet *ss, double d); +extern Set *union_intset_int(const Set *s, int i); +extern SpanSet *union_intspan_int(const Span *s, int i); +extern SpanSet *union_intspanset_int(const SpanSet *ss, int i); +extern SpanSet *union_period_timestamp(const Span *s, TimestampTz t); +extern SpanSet *union_periodset_timestamp(SpanSet *ss, TimestampTz t); extern Set *union_set_set(const Set *s1, const Set *s2); -extern SpanSet *union_period_timestamp(const Span *p, TimestampTz t); -extern SpanSet *union_periodset_timestamp(SpanSet *ps, TimestampTz t); extern SpanSet *union_span_span(const Span *s1, const Span *s2); extern SpanSet *union_spanset_span(const SpanSet *ss, const Span *s); extern SpanSet *union_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern Set *union_timestampset_timestamp(const Set *ts, const TimestampTz t); +extern Set *union_textset_text(const Set *s, text *txt); +extern Set *union_timestampset_timestamp(const Set *s, const TimestampTz t); @@ -1199,14 +1245,11 @@ extern char *stbox_out(const STBox *box, int maxdd); -extern TBox *tbox_make(const Span *s, const Span *p); -extern void tbox_set(const Span *s, const Span *p, TBox *box); -extern TBox *tbox_copy(const TBox *box); extern STBox * stbox_make(bool hasx, bool hasz, bool geodetic, int32 srid, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, const Span *p); -extern void stbox_set(bool hasx, bool hasz, bool geodetic, int32 srid, double xmin, double xmax, - double ymin, double ymax, double zmin, double zmax, const Span *p, STBox *box); extern STBox *stbox_copy(const STBox *box); +extern TBox *tbox_make(const Span *s, const Span *p); +extern TBox *tbox_copy(const TBox *box); @@ -1273,13 +1316,11 @@ extern int32 stbox_srid(const STBox *box); -extern void stbox_expand(const STBox *box1, STBox *box2); extern STBox *stbox_expand_space(const STBox *box, double d); extern STBox *stbox_expand_time(const STBox *box, const Interval *interval); extern STBox *stbox_get_space(const STBox *box); extern STBox *stbox_round(const STBox *box, int maxdd); extern STBox *stbox_set_srid(const STBox *box, int32 srid); -extern void tbox_expand(const TBox *box1, TBox *box2); extern TBox *tbox_expand_value(const TBox *box, const double d); extern TBox *tbox_expand_time(const TBox *box, const Interval *interval); extern TBox *tbox_round(const TBox *box, int maxdd); @@ -1471,7 +1512,6 @@ extern char *temporal_subtype(const Temporal *temp); extern SpanSet *temporal_time(const Temporal *temp); extern bool temporal_timestamp_n(const Temporal *temp, int n, TimestampTz *result); extern TimestampTz *temporal_timestamps(const Temporal *temp, int *count); -extern Datum *temporal_values(const Temporal *temp, int *count); extern double tfloat_end_value(const Temporal *temp); extern double tfloat_max_value(const Temporal *temp); extern double tfloat_min_value(const Temporal *temp); @@ -1668,8 +1708,6 @@ extern bool tint_always_lt(const Temporal *temp, int i); extern bool tint_ever_eq(const Temporal *temp, int i); extern bool tint_ever_le(const Temporal *temp, int i); extern bool tint_ever_lt(const Temporal *temp, int i); -extern bool tpoint_always_eq(const Temporal *temp, Datum value); -extern bool tpoint_ever_eq(const Temporal *temp, Datum value); extern bool ttext_always_eq(const Temporal *temp, text *txt); extern bool ttext_always_le(const Temporal *temp, text *txt); extern bool ttext_always_lt(const Temporal *temp, text *txt); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index fcc8b1bf..53aa7737 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -402,22 +402,29 @@ def gserialized_from_geojson(geojson: str) -> 'GSERIALIZED *': return result if result != _ffi.NULL else None -def gserialized_from_hexewkb(wkt: str) -> 'GSERIALIZED *': +def geometry_from_text(wkt: str, srid: int) -> 'GSERIALIZED *': wkt_converted = wkt.encode('utf-8') - result = _lib.gserialized_from_hexewkb(wkt_converted) + result = _lib.geometry_from_text(wkt_converted, srid) return result if result != _ffi.NULL else None -def gserialized_from_text(wkt: str, srid: int) -> 'GSERIALIZED *': +def geography_from_text(wkt: str, srid: int) -> 'GSERIALIZED *': wkt_converted = wkt.encode('utf-8') - result = _lib.gserialized_from_text(wkt_converted, srid) + result = _lib.geography_from_text(wkt_converted, srid) return result if result != _ffi.NULL else None -def gserialized_in(input: str, geom_typmod: int) -> 'GSERIALIZED *': +def pgis_geography_in(input: str, geom_typmod: int) -> 'GSERIALIZED *': input_converted = input.encode('utf-8') geom_typmod_converted = _ffi.cast('int32', geom_typmod) - result = _lib.gserialized_in(input_converted, geom_typmod_converted) + result = _lib.pgis_geography_in(input_converted, geom_typmod_converted) + return result if result != _ffi.NULL else None + + +def pgis_geometry_in(input: str, geom_typmod: int) -> 'GSERIALIZED *': + input_converted = input.encode('utf-8') + geom_typmod_converted = _ffi.cast('int32', geom_typmod) + result = _lib.pgis_geometry_in(input_converted, geom_typmod_converted) return result if result != _ffi.NULL else None @@ -519,9 +526,9 @@ def geogset_in(string: str) -> 'Set *': return result if result != _ffi.NULL else None -def geogset_out(set: 'const Set *', maxdd: int) -> str: +def geoset_out(set: 'const Set *', maxdd: int) -> str: set_converted = _ffi.cast('const Set *', set) - result = _lib.geogset_out(set_converted, maxdd) + result = _lib.geoset_out(set_converted, maxdd) result = _ffi.string(result).decode('utf-8') return result if result != _ffi.NULL else None @@ -532,13 +539,6 @@ def geomset_in(string: str) -> 'Set *': return result if result != _ffi.NULL else None -def geomset_out(set: 'const Set *', maxdd: int) -> str: - set_converted = _ffi.cast('const Set *', set) - result = _lib.geomset_out(set_converted, maxdd) - result = _ffi.string(result).decode('utf-8') - return result if result != _ffi.NULL else None - - def geoset_as_ewkt(set: 'const Set *', maxdd: int) -> str: set_converted = _ffi.cast('const Set *', set) result = _lib.geoset_as_ewkt(set_converted, maxdd) @@ -755,9 +755,9 @@ def timestampset_out(set: 'const Set *') -> str: return result if result != _ffi.NULL else None -def bigintset_make(values: 'const int64 *', count: int) -> 'Set *': - values_converted = _ffi.cast('const int64 *', values) - result = _lib.bigintset_make(values_converted, count) +def bigintset_make(values: 'List[const int64]') -> 'Set *': + values_converted = _ffi.new('const int64 []', values) + result = _lib.bigintset_make(values_converted, len(values)) return result if result != _ffi.NULL else None @@ -768,9 +768,9 @@ def bigintspan_make(lower: int, upper: int, lower_inc: bool, upper_inc: bool) -> return result if result != _ffi.NULL else None -def floatset_make(values: 'const double *', count: int) -> 'Set *': - values_converted = _ffi.cast('const double *', values) - result = _lib.floatset_make(values_converted, count) +def floatset_make(values: 'List[const double]') -> 'Set *': + values_converted = _ffi.new('const double []', values) + result = _lib.floatset_make(values_converted, len(values)) return result if result != _ffi.NULL else None @@ -779,21 +779,15 @@ def floatspan_make(lower: float, upper: float, lower_inc: bool, upper_inc: bool) return result if result != _ffi.NULL else None -def geogset_make(values: 'const GSERIALIZED **', count: int) -> 'Set *': - values_converted = [_ffi.cast('const GSERIALIZED *', x) for x in values] - result = _lib.geogset_make(values_converted, count) - return result if result != _ffi.NULL else None - - -def geomset_make(values: 'const GSERIALIZED **', count: int) -> 'Set *': +def geoset_make(values: 'const GSERIALIZED **') -> 'Set *': values_converted = [_ffi.cast('const GSERIALIZED *', x) for x in values] - result = _lib.geomset_make(values_converted, count) + result = _lib.geoset_make(values_converted, len(values)) return result if result != _ffi.NULL else None -def intset_make(values: 'const int *', count: int) -> 'Set *': - values_converted = _ffi.cast('const int *', values) - result = _lib.intset_make(values_converted, count) +def intset_make(values: 'List[const int]') -> 'Set *': + values_converted = _ffi.new('const int []', values) + result = _lib.intset_make(values_converted, len(values)) return result if result != _ffi.NULL else None @@ -845,9 +839,9 @@ def spanset_make_free(spans: 'Span *', count: int, normalize: bool) -> 'SpanSet return result if result != _ffi.NULL else None -def textset_make(values: 'const text **', count: int) -> 'Set *': +def textset_make(values: 'List[const text]') -> 'Set *': values_converted = [_ffi.cast('const text *', x) for x in values] - result = _lib.textset_make(values_converted, count) + result = _lib.textset_make(values_converted, len(values)) return result if result != _ffi.NULL else None @@ -888,6 +882,18 @@ def float_to_floatspanset(d: float) -> 'SpanSet *': return result if result != _ffi.NULL else None +def geog_to_geogset(gs: 'GSERIALIZED *') -> 'Set *': + gs_converted = _ffi.cast('GSERIALIZED *', gs) + result = _lib.geog_to_geogset(gs_converted) + return result if result != _ffi.NULL else None + + +def geom_to_geomset(gs: 'GSERIALIZED *') -> 'Set *': + gs_converted = _ffi.cast('GSERIALIZED *', gs) + result = _lib.geom_to_geomset(gs_converted) + return result if result != _ffi.NULL else None + + def int_to_intset(i: int) -> 'Set *': result = _lib.int_to_intset(i) return result if result != _ffi.NULL else None @@ -915,6 +921,12 @@ def span_to_spanset(s: 'const Span *') -> 'SpanSet *': return result if result != _ffi.NULL else None +def text_to_textset(txt: str) -> 'Set *': + txt_converted = cstring2text(txt) + result = _lib.text_to_textset(txt_converted) + return result if result != _ffi.NULL else None + + def timestamp_to_period(t: int) -> 'Span *': t_converted = _ffi.cast('TimestampTz', t) result = _lib.timestamp_to_period(t_converted) @@ -1035,12 +1047,39 @@ def floatspanset_upper(ss: 'const SpanSet *') -> 'double': return result if result != _ffi.NULL else None +def geoset_end_value(s: 'const Set *') -> 'GSERIALIZED *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.geoset_end_value(s_converted) + return result if result != _ffi.NULL else None + + def geoset_srid(set: 'const Set *') -> 'int': set_converted = _ffi.cast('const Set *', set) result = _lib.geoset_srid(set_converted) return result if result != _ffi.NULL else None +def geoset_start_value(s: 'const Set *') -> 'GSERIALIZED *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.geoset_start_value(s_converted) + return result if result != _ffi.NULL else None + + +def geoset_value_n(s: 'const Set *', n: int) -> 'GSERIALIZED **': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.geoset_value_n(s_converted, n, out_result) + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def geoset_values(s: 'const Set *') -> 'GSERIALIZED **': + s_converted = _ffi.cast('const Set *', s) + result = _lib.geoset_values(s_converted) + return result if result != _ffi.NULL else None + + def intset_end_value(s: 'const Set *') -> 'int': s_converted = _ffi.cast('const Set *', s) result = _lib.intset_end_value(s_converted) @@ -1303,6 +1342,35 @@ def spatialset_stbox(s: 'const Set *') -> 'STBox *': return result if result != _ffi.NULL else None +def textset_end_value(s: 'const Set *') -> str: + s_converted = _ffi.cast('const Set *', s) + result = _lib.textset_end_value(s_converted) + result = text2cstring(result) + return result if result != _ffi.NULL else None + + +def textset_start_value(s: 'const Set *') -> str: + s_converted = _ffi.cast('const Set *', s) + result = _lib.textset_start_value(s_converted) + result = text2cstring(result) + return result if result != _ffi.NULL else None + + +def textset_value_n(s: 'const Set *', n: int) -> 'text **': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('text **') + result = _lib.textset_value_n(s_converted, n, out_result) + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def textset_values(s: 'const Set *') -> 'text **': + s_converted = _ffi.cast('const Set *', s) + result = _lib.textset_values(s_converted) + return result if result != _ffi.NULL else None + + def timestampset_end_timestamp(ts: 'const Set *') -> 'TimestampTz': ts_converted = _ffi.cast('const Set *', ts) result = _lib.timestampset_end_timestamp(ts_converted) @@ -1348,24 +1416,12 @@ def floatspanset_round(ss: 'const SpanSet *', maxdd: int) -> 'SpanSet *': return result if result != _ffi.NULL else None -def floatspan_set_intspan(s1: 'const Span *', s2: 'Span *') -> None: - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('Span *', s2) - _lib.floatspan_set_intspan(s1_converted, s2_converted) - - def geoset_round(s: 'const Set *', maxdd: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) result = _lib.geoset_round(s_converted, maxdd) return result if result != _ffi.NULL else None -def intspan_set_floatspan(s1: 'const Span *', s2: 'Span *') -> None: - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('Span *', s2) - _lib.intspan_set_floatspan(s1_converted, s2_converted) - - def period_tprecision(s: 'const Span *', duration: 'const Interval *', torigin: int) -> 'Span *': s_converted = _ffi.cast('const Span *', s) duration_converted = _ffi.cast('const Interval *', duration) @@ -1398,19 +1454,6 @@ def periodset_shift_tscale(ps: 'const SpanSet *', shift: "Optional['const Interv return result if result != _ffi.NULL else None -def set_shift(s: 'const Set *', shift: 'Datum') -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - shift_converted = _ffi.cast('Datum', shift) - result = _lib.set_shift(s_converted, shift_converted) - return result if result != _ffi.NULL else None - - -def span_expand(s1: 'const Span *', s2: 'Span *') -> None: - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('Span *', s2) - _lib.span_expand(s1_converted, s2_converted) - - def timestamp_tprecision(t: int, duration: 'const Interval *', torigin: int) -> 'TimestampTz': t_converted = _ffi.cast('TimestampTz', t) duration_converted = _ffi.cast('const Interval *', duration) @@ -2095,42 +2138,117 @@ def right_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bo return result if result != _ffi.NULL else None -def bbox_union_span_span(s1: 'const Span *', s2: 'const Span *') -> 'Span *': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - out_result = _ffi.new('Span *') - _lib.bbox_union_span_span(s1_converted, s2_converted, out_result) - return out_result if out_result!= _ffi.NULL else None +def intersection_bigintset_bigint(s: 'const Set *', i: int) -> 'int64': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintset_bigint(s_converted, i_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None +def intersection_bigintspan_bigint(s: 'const Span *', i: int) -> 'int64': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintspan_bigint(s_converted, i_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def intersection_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.intersection_set_set(s1_converted, s2_converted) - return result if result != _ffi.NULL else None +def intersection_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'int64': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintspanset_bigint(ss_converted, i_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def intersection_period_timestamp(p: 'const Span *', t: int) -> int: - p_converted = _ffi.cast('const Span *', p) + +def intersection_floatset_float(s: 'const Set *', d: float) -> 'double': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('double *') + result = _lib.intersection_floatset_float(s_converted, d, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_floatspan_float(s: 'const Span *', d: float) -> 'double': + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('double *') + result = _lib.intersection_floatspan_float(s_converted, d, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('double *') + result = _lib.intersection_floatspanset_float(ss_converted, d, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_intset_int(s: 'const Set *', i: int) -> 'int': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('int *') + result = _lib.intersection_intset_int(s_converted, i, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_intspan_int(s: 'const Span *', i: int) -> 'int': + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('int *') + result = _lib.intersection_intspan_int(s_converted, i, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_intspanset_int(ss: 'const SpanSet *', i: int) -> 'int': + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('int *') + result = _lib.intersection_intspanset_int(ss_converted, i, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_period_timestamp(s: 'const Span *', t: int) -> int: + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_period_timestamp(p_converted, t_converted, out_result) + result = _lib.intersection_period_timestamp(s_converted, t_converted, out_result) if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def intersection_periodset_timestamp(ps: 'const SpanSet *', t: int) -> int: - ps_converted = _ffi.cast('const SpanSet *', ps) +def intersection_periodset_timestamp(ss: 'const SpanSet *', t: int) -> int: + ss_converted = _ffi.cast('const SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_periodset_timestamp(ps_converted, t_converted, out_result) + result = _lib.intersection_periodset_timestamp(ss_converted, t_converted, out_result) if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None +def intersection_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.intersection_set_set(s1_converted, s2_converted) + return result if result != _ffi.NULL else None + + def intersection_span_span(s1: 'const Span *', s2: 'const Span *') -> 'Span *': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) @@ -2152,34 +2270,185 @@ def intersection_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') return result if result != _ffi.NULL else None -def intersection_timestampset_timestamp(ts: 'const Set *', t: int) -> int: - ts_converted = _ffi.cast('const Set *', ts) - t_converted = _ffi.cast('const TimestampTz', t) +def intersection_textset_text(s: 'const Set *', txt: str) -> 'text **': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + out_result = _ffi.new('text **') + result = _lib.intersection_textset_text(s_converted, txt_converted, out_result) + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def intersection_timestampset_timestamp(s: 'const Set *', t: int) -> int: + s_converted = _ffi.cast('const Set *', s) + t_converted = _ffi.cast('TimestampTz', t) out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_timestampset_timestamp(ts_converted, t_converted, out_result) + result = _lib.intersection_timestampset_timestamp(s_converted, t_converted, out_result) if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def minus_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.minus_set_set(s1_converted, s2_converted) +def minus_bigint_bigintset(i: int, s: 'const Set *') -> 'int64': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintset(i_converted, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_bigint_bigintspan(i: int, s: 'const Span *') -> 'int64': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintspan(i_converted, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'int64': + i_converted = _ffi.cast('int64', i) + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintspanset(i_converted, ss_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintset_bigint(s_converted, i_converted) return result if result != _ffi.NULL else None -def minus_period_timestamp(p: 'const Span *', t: int) -> 'SpanSet *': - p_converted = _ffi.cast('const Span *', p) +def minus_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintspan_bigint(s_converted, i_converted) + return result if result != _ffi.NULL else None + + +def minus_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintspanset_bigint(ss_converted, i_converted) + return result if result != _ffi.NULL else None + + +def minus_float_floatset(d: float, s: 'const Set *') -> 'double': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatset(d, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_float_floatspan(d: float, s: 'const Span *') -> 'double': + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatspan(d, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatspanset(d, ss_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_floatset_float(s: 'const Set *', d: float) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.minus_floatset_float(s_converted, d) + return result if result != _ffi.NULL else None + + +def minus_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.minus_floatspan_float(s_converted, d) + return result if result != _ffi.NULL else None + + +def minus_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.minus_floatspanset_float(ss_converted, d) + return result if result != _ffi.NULL else None + + +def minus_int_intset(i: int, s: 'const Set *') -> 'int': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('int *') + result = _lib.minus_int_intset(i, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_int_intspan(i: int, s: 'const Span *') -> 'int': + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('int *') + result = _lib.minus_int_intspan(i, s_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_int_intspanset(i: int, ss: 'const SpanSet *') -> 'int': + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('int *') + result = _lib.minus_int_intspanset(i, ss_converted, out_result) + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_intset_int(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.minus_intset_int(s_converted, i) + return result if result != _ffi.NULL else None + + +def minus_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.minus_intspan_int(s_converted, i) + return result if result != _ffi.NULL else None + + +def minus_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.minus_intspanset_int(ss_converted, i) + return result if result != _ffi.NULL else None + + +def minus_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_period_timestamp(p_converted, t_converted) + result = _lib.minus_period_timestamp(s_converted, t_converted) return result if result != _ffi.NULL else None -def minus_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'SpanSet *': - ps_converted = _ffi.cast('const SpanSet *', ps) +def minus_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_periodset_timestamp(ps_converted, t_converted) + result = _lib.minus_periodset_timestamp(ss_converted, t_converted) + return result if result != _ffi.NULL else None + + +def minus_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.minus_set_set(s1_converted, s2_converted) return result if result != _ffi.NULL else None @@ -2211,51 +2480,125 @@ def minus_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'Sp return result if result != _ffi.NULL else None -def minus_timestamp_period(t: int, p: 'const Span *') -> int: +def minus_text_textset(txt: str, s: 'const Set *') -> 'text **': + txt_converted = cstring2text(txt) + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('text **') + result = _lib.minus_text_textset(txt_converted, s_converted, out_result) + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def minus_textset_text(s: 'const Set *', txt: str) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.minus_textset_text(s_converted, txt_converted) + return result if result != _ffi.NULL else None + + +def minus_timestamp_period(t: int, s: 'const Span *') -> int: t_converted = _ffi.cast('TimestampTz', t) - p_converted = _ffi.cast('const Span *', p) + s_converted = _ffi.cast('const Span *', s) out_result = _ffi.new('TimestampTz *') - result = _lib.minus_timestamp_period(t_converted, p_converted, out_result) + result = _lib.minus_timestamp_period(t_converted, s_converted, out_result) if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def minus_timestamp_periodset(t: int, ps: 'const SpanSet *') -> int: +def minus_timestamp_periodset(t: int, ss: 'const SpanSet *') -> int: t_converted = _ffi.cast('TimestampTz', t) - ps_converted = _ffi.cast('const SpanSet *', ps) + ss_converted = _ffi.cast('const SpanSet *', ss) out_result = _ffi.new('TimestampTz *') - result = _lib.minus_timestamp_periodset(t_converted, ps_converted, out_result) + result = _lib.minus_timestamp_periodset(t_converted, ss_converted, out_result) if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def minus_timestampset_timestamp(ts: 'const Set *', t: int) -> 'Set *': - ts_converted = _ffi.cast('const Set *', ts) +def minus_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_timestampset_timestamp(ts_converted, t_converted) + result = _lib.minus_timestampset_timestamp(s_converted, t_converted) return result if result != _ffi.NULL else None -def union_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.union_set_set(s1_converted, s2_converted) +def union_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintset_bigint(s_converted, i_converted) return result if result != _ffi.NULL else None -def union_period_timestamp(p: 'const Span *', t: int) -> 'SpanSet *': - p_converted = _ffi.cast('const Span *', p) +def union_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintspan_bigint(s_converted, i_converted) + return result if result != _ffi.NULL else None + + +def union_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintspanset_bigint(ss_converted, i_converted) + return result if result != _ffi.NULL else None + + +def union_floatset_float(s: 'const Set *', d: float) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.union_floatset_float(s_converted, d) + return result if result != _ffi.NULL else None + + +def union_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.union_floatspan_float(s_converted, d) + return result if result != _ffi.NULL else None + + +def union_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.union_floatspanset_float(ss_converted, d) + return result if result != _ffi.NULL else None + + +def union_intset_int(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.union_intset_int(s_converted, i) + return result if result != _ffi.NULL else None + + +def union_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.union_intspan_int(s_converted, i) + return result if result != _ffi.NULL else None + + +def union_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.union_intspanset_int(ss_converted, i) + return result if result != _ffi.NULL else None + + +def union_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.union_period_timestamp(p_converted, t_converted) + result = _lib.union_period_timestamp(s_converted, t_converted) return result if result != _ffi.NULL else None -def union_periodset_timestamp(ps: 'SpanSet *', t: int) -> 'SpanSet *': - ps_converted = _ffi.cast('SpanSet *', ps) +def union_periodset_timestamp(ss: 'SpanSet *', t: int) -> 'SpanSet *': + ss_converted = _ffi.cast('SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.union_periodset_timestamp(ps_converted, t_converted) + result = _lib.union_periodset_timestamp(ss_converted, t_converted) + return result if result != _ffi.NULL else None + + +def union_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.union_set_set(s1_converted, s2_converted) return result if result != _ffi.NULL else None @@ -2280,10 +2623,17 @@ def union_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'Sp return result if result != _ffi.NULL else None -def union_timestampset_timestamp(ts: 'const Set *', t: int) -> 'Set *': - ts_converted = _ffi.cast('const Set *', ts) +def union_textset_text(s: 'const Set *', txt: str) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.union_textset_text(s_converted, txt_converted) + return result if result != _ffi.NULL else None + + +def union_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('const TimestampTz', t) - result = _lib.union_timestampset_timestamp(ts_converted, t_converted) + result = _lib.union_timestampset_timestamp(s_converted, t_converted) return result if result != _ffi.NULL else None @@ -2722,43 +3072,29 @@ def stbox_out(box: 'const STBox *', maxdd: int) -> str: return result if result != _ffi.NULL else None -def tbox_make(s: "Optional['const Span *']", p: "Optional['const Span *']") -> 'TBox *': - s_converted = _ffi.cast('const Span *', s) if s is not None else _ffi.NULL +def stbox_make(hasx: bool, hasz: bool, geodetic: bool, srid: int, xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, p: "Optional['const Span *']") -> 'STBox *': + srid_converted = _ffi.cast('int32', srid) p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL - result = _lib.tbox_make(s_converted, p_converted) + result = _lib.stbox_make(hasx, hasz, geodetic, srid_converted, xmin, xmax, ymin, ymax, zmin, zmax, p_converted) return result if result != _ffi.NULL else None -def tbox_set(s: 'const Span *', p: 'const Span *', box: 'TBox *') -> None: - s_converted = _ffi.cast('const Span *', s) - p_converted = _ffi.cast('const Span *', p) - box_converted = _ffi.cast('TBox *', box) - _lib.tbox_set(s_converted, p_converted, box_converted) - - -def tbox_copy(box: 'const TBox *') -> 'TBox *': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_copy(box_converted) +def stbox_copy(box: 'const STBox *') -> 'STBox *': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_copy(box_converted) return result if result != _ffi.NULL else None -def stbox_make(hasx: bool, hasz: bool, geodetic: bool, srid: int, xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, p: "Optional['const Span *']") -> 'STBox *': - srid_converted = _ffi.cast('int32', srid) +def tbox_make(s: "Optional['const Span *']", p: "Optional['const Span *']") -> 'TBox *': + s_converted = _ffi.cast('const Span *', s) if s is not None else _ffi.NULL p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL - result = _lib.stbox_make(hasx, hasz, geodetic, srid_converted, xmin, xmax, ymin, ymax, zmin, zmax, p_converted) + result = _lib.tbox_make(s_converted, p_converted) return result if result != _ffi.NULL else None -def stbox_set(hasx: bool, hasz: bool, geodetic: bool, srid: int, xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, p: 'const Span *', box: 'STBox *') -> None: - srid_converted = _ffi.cast('int32', srid) - p_converted = _ffi.cast('const Span *', p) - box_converted = _ffi.cast('STBox *', box) - _lib.stbox_set(hasx, hasz, geodetic, srid_converted, xmin, xmax, ymin, ymax, zmin, zmax, p_converted, box_converted) - - -def stbox_copy(box: 'const STBox *') -> 'STBox *': - box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_copy(box_converted) +def tbox_copy(box: 'const TBox *') -> 'TBox *': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_copy(box_converted) return result if result != _ffi.NULL else None @@ -3124,12 +3460,6 @@ def stbox_srid(box: 'const STBox *') -> 'int32': return result if result != _ffi.NULL else None -def stbox_expand(box1: 'const STBox *', box2: 'STBox *') -> None: - box1_converted = _ffi.cast('const STBox *', box1) - box2_converted = _ffi.cast('STBox *', box2) - _lib.stbox_expand(box1_converted, box2_converted) - - def stbox_expand_space(box: 'const STBox *', d: float) -> 'STBox *': box_converted = _ffi.cast('const STBox *', box) result = _lib.stbox_expand_space(box_converted, d) @@ -3162,12 +3492,6 @@ def stbox_set_srid(box: 'const STBox *', srid: int) -> 'STBox *': return result if result != _ffi.NULL else None -def tbox_expand(box1: 'const TBox *', box2: 'TBox *') -> None: - box1_converted = _ffi.cast('const TBox *', box1) - box2_converted = _ffi.cast('TBox *', box2) - _lib.tbox_expand(box1_converted, box2_converted) - - def tbox_expand_value(box: 'const TBox *', d: 'const double') -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) d_converted = _ffi.cast('const double', d) @@ -4146,13 +4470,6 @@ def temporal_timestamps(temp: 'const Temporal *') -> "Tuple['TimestampTz *', 'in return result if result != _ffi.NULL else None, count[0] -def temporal_values(temp: 'const Temporal *') -> "Tuple['Datum *', 'int']": - temp_converted = _ffi.cast('const Temporal *', temp) - count = _ffi.new('int *') - result = _lib.temporal_values(temp_converted, count) - return result if result != _ffi.NULL else None, count[0] - - def tfloat_end_value(temp: 'const Temporal *') -> 'double': temp_converted = _ffi.cast('const Temporal *', temp) result = _lib.tfloat_end_value(temp_converted) @@ -5198,20 +5515,6 @@ def tint_ever_lt(temp: 'const Temporal *', i: int) -> 'bool': return result if result != _ffi.NULL else None -def tpoint_always_eq(temp: 'const Temporal *', value: 'Datum') -> 'bool': - temp_converted = _ffi.cast('const Temporal *', temp) - value_converted = _ffi.cast('Datum', value) - result = _lib.tpoint_always_eq(temp_converted, value_converted) - return result if result != _ffi.NULL else None - - -def tpoint_ever_eq(temp: 'const Temporal *', value: 'Datum') -> 'bool': - temp_converted = _ffi.cast('const Temporal *', temp) - value_converted = _ffi.cast('Datum', value) - result = _lib.tpoint_ever_eq(temp_converted, value_converted) - return result if result != _ffi.NULL else None - - def ttext_always_eq(temp: 'const Temporal *', txt: str) -> 'bool': temp_converted = _ffi.cast('const Temporal *', temp) txt_converted = cstring2text(txt) From 23c35b51891db831a6fbbc75edc5dcb188215b66 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 22 Aug 2023 16:27:05 +0200 Subject: [PATCH 004/101] Update gserialized calls --- pymeos/pymeos/boxes/stbox.py | 31 +++---- pymeos/pymeos/main/tpoint.py | 82 +++++++++---------- pymeos_cffi/pymeos_cffi/__init__.py | 2 + .../builder/build_pymeos_functions.py | 32 ++++++-- pymeos_cffi/pymeos_cffi/functions.py | 58 ++++++++----- 5 files changed, 121 insertions(+), 84 deletions(-) diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index e8898e77..ad746d98 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -42,7 +42,7 @@ class STBox: def _get_box(self, other: Union[Geometry, STBox, Temporal, Time], allow_space_only: bool = True, allow_time_only: bool = False) -> STBox: if allow_space_only and isinstance(other, get_args(Geometry)): - other_box = geo_to_stbox(geometry_to_gserialized(other, self.geodetic())) + other_box = geo_to_stbox(geo_to_gserialized(other, self.geodetic())) elif isinstance(other, STBox): other_box = other._inner elif isinstance(other, TPoint): @@ -157,7 +157,7 @@ def from_geometry(geom: Geometry, geodetic: bool = False) -> STBox: MEOS Functions: gserialized_in, geo_to_stbox """ - gs = geometry_to_gserialized(geom, geodetic) + gs = geo_to_gserialized(geom, geodetic) return STBox(_inner=geo_to_stbox(gs)) @staticmethod @@ -175,14 +175,13 @@ def from_time(time: Time) -> STBox: timestamp_to_stbox, timestampset_to_stbox, period_to_stbox, periodset_to_stbox """ if isinstance(time, datetime): - result = (datetime, datetime) + result = timestamp_to_stbox(datetime_to_timestamptz(time)) elif isinstance(time, TimestampSet): - result = (time.start_timestamp(), time.end_timestamp()) + result = timestampset_to_stbox(time._inner) elif isinstance(time, Period): - result = (time.lower, time.upper, time.lower_inc, time.upper_inc) + result = period_to_stbox(time._inner) elif isinstance(time, PeriodSet): - result = (time.start_period().lower, time.end_period().upper, - time.start_period().lower_inc, time.end_period().upper_inc) + result = periodset_to_stbox(time._inner) else: raise TypeError(f'Operation not supported with type {time.__class__}') return STBox(_inner=result) @@ -204,7 +203,7 @@ def from_geometry_time(geometry: Geometry, time: Union[datetime, Period], MEOS Functions: geo_timestamp_to_stbox, geo_period_to_stbox """ - gs = geometry_to_gserialized(geometry, geodetic) + gs = geo_to_gserialized(geometry, geodetic) if isinstance(time, datetime): result = geo_timestamp_to_stbox(gs, datetime_to_timestamptz(time)) elif isinstance(time, Period): @@ -248,7 +247,7 @@ def from_expanding_bounding_box(value: Union[Geometry, TPoint, STBox], geo_expand_space, tpoint_expand_space, stbox_expand_space """ if isinstance(value, get_args(Geometry)): - gs = geometry_to_gserialized(value, geodetic) + gs = geo_to_gserialized(value, geodetic) result = geo_expand_space(gs, expansion) elif isinstance(value, TPoint): result = tpoint_expand_space(value._inner, expansion) @@ -647,12 +646,13 @@ def round(self, maxdd : int = 0) -> STBox: return STBox(_inner=new_inner) # ------------------------- Set Operations -------------------------------- - def union(self, other: STBox, strict: Optional[bool] = True) -> STBox: + def union(self, other: STBox, strict: Optional[bool] = False) -> STBox: """ - Returns the union of `self` with `other`. Fails if the union is not contiguous. + Returns the union of `self` with `other`. Args: other: spatiotemporal box to merge with + strict: Whether to fail if the boxes do not intersect. Returns: A :class:`STBox` instance. @@ -675,7 +675,7 @@ def __add__(self, other): MEOS Functions: union_stbox_stbox """ - return self.union(other) + return self.union(other, False) # TODO: Check returning None for empty intersection is the desired behaviour def intersection(self, other: STBox) -> Optional[STBox]: @@ -1061,7 +1061,7 @@ def nearest_approach_distance(self, other: Union[Geometry, STBox, TPoint]) -> fl nad_stbox_geo, nad_stbox_stbox """ if isinstance(other, get_args(Geometry)): - gs = geometry_to_gserialized(other, self.geodetic()) + gs = geo_to_gserialized(other, self.geodetic()) return nad_stbox_geo(self._inner, gs) elif isinstance(other, STBox): return nad_stbox_stbox(self._inner, other._inner) @@ -1165,8 +1165,9 @@ def tile(self, size: Optional[float] = None, duration: Optional[Union[timedelta, else pg_timestamptz_in(start, -1) if isinstance(start, str) \ else datetime_to_timestamptz(self.tmin()) if self.has_t() \ else 0 - gs = geometry_to_gserialized(origin) if origin is not None \ - else gserialized_in('Point(0 0 0)', -1) + gs = geo_to_gserialized(origin, self.geodetic()) if origin is not None \ + else pgis_geography_in('Point(0 0 0)', -1) if self.geodetic() \ + else pgis_geometry_in('Point(0 0 0)', -1) tiles, dimensions = stbox_tile_list(self._inner, sz, sz, sz, dt, gs, st) x_size = dimensions[0] or 1 y_size = dimensions[1] or 1 diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 0d0084a0..ec50f997 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -16,7 +16,7 @@ from ..time import * if TYPE_CHECKING: - from ..boxes import STBox + from ..boxes import STBox, Box TG = TypeVar('TG', bound='TPoint') TI = TypeVar('TI', bound='TPointInst') @@ -310,7 +310,7 @@ def bearing(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint]) -> TFloa bearing_tpoint_point, bearing_tpoint_tpoint """ if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = bearing_tpoint_point(self._inner, gs, False) elif isinstance(other, TPoint): result = bearing_tpoint_tpoint(self._inner, other._inner) @@ -457,10 +457,10 @@ def at(self, """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = tpoint_at_value(self._inner, gs) elif isinstance(other, list): - gss = [geometry_to_gserialized(gm, isinstance(self, TGeogPoint)) for gm in other] + gss = [geo_to_gserialized(gm, isinstance(self, TGeogPoint)) for gm in other] results = [tpoint_at_value(self._inner, gs) for gs in gss] result = temporal_merge_array(results, len(results)) elif isinstance(other, STBox): @@ -487,10 +487,10 @@ def minus(self, """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = tpoint_minus_value(self._inner, gs) elif isinstance(other, list): - gss = [geometry_to_gserialized(gm, isinstance(self, TGeogPoint)) for gm in other] + gss = [geo_to_gserialized(gm, isinstance(self, TGeogPoint)) for gm in other] result = reduce(tpoint_minus_value, gss, self._inner) elif isinstance(other, STBox): result = tpoint_minus_stbox(self._inner, other._inner, True) @@ -695,7 +695,7 @@ def is_ever_contained_in(self, container: Union[pg.Geometry, shpb.BaseGeometry, """ from ..boxes import STBox if isinstance(container, pg.Geometry) or isinstance(container, shpb.BaseGeometry): - gs = geometry_to_gserialized(container, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(container, isinstance(self, TGeogPoint)) result = econtains_geo_tpoint(gs, self._inner) elif isinstance(container, STBox): result = econtains_geo_tpoint(stbox_to_geo(container._inner), self._inner) @@ -718,7 +718,7 @@ def is_ever_disjoint(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = edisjoint_tpoint_geo(self._inner, gs) elif isinstance(other, STBox): result = edisjoint_tpoint_geo(self._inner, stbox_to_geo(other._inner)) @@ -745,7 +745,7 @@ def is_ever_within_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, T """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = edwithin_tpoint_geo(self._inner, gs, distance) elif isinstance(other, STBox): result = edwithin_tpoint_geo(self._inner, stbox_to_geo(other._inner), distance) @@ -770,7 +770,7 @@ def ever_intersects(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, S """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = eintersects_tpoint_geo(self._inner, gs) elif isinstance(other, STBox): result = eintersects_tpoint_geo(self._inner, stbox_to_geo(other._inner)) @@ -795,7 +795,7 @@ def ever_touches(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> b """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = etouches_tpoint_geo(self._inner, gs) elif isinstance(other, STBox): result = etouches_tpoint_geo(self._inner, stbox_to_geo(other._inner)) @@ -819,7 +819,7 @@ def is_spatially_contained_in(self, container: Union[pg.Geometry, shpb.BaseGeome """ from ..boxes import STBox if isinstance(container, pg.Geometry) or isinstance(container, shpb.BaseGeometry): - gs = geometry_to_gserialized(container, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(container, isinstance(self, TGeogPoint)) result = tcontains_geo_tpoint(gs, self._inner, False, False) elif isinstance(container, STBox): gs = stbox_to_geo(container._inner) @@ -843,7 +843,7 @@ def disjoint(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBool """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = tdisjoint_tpoint_geo(self._inner, gs, False, False) elif isinstance(other, STBox): result = tdisjoint_tpoint_geo(self._inner, stbox_to_geo(other._inner), False, False) @@ -867,7 +867,7 @@ def within_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, S """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = tdwithin_tpoint_geo(self._inner, gs, distance, False, False) elif isinstance(other, STBox): result = tdwithin_tpoint_geo(self._inner, stbox_to_geo(other._inner), distance, False, False) @@ -892,7 +892,7 @@ def intersects(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBo """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = tintersects_tpoint_geo(self._inner, gs, False, False) elif isinstance(other, STBox): result = tintersects_tpoint_geo(self._inner, stbox_to_geo(other._inner), False, False) @@ -915,7 +915,7 @@ def touches(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBool: """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = ttouches_tpoint_geo(self._inner, gs, False, False) elif isinstance(other, STBox): result = ttouches_tpoint_geo(self._inner, stbox_to_geo(other._inner), False, False) @@ -939,7 +939,7 @@ def distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, STBox]) """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = distance_tpoint_geo(self._inner, gs) elif isinstance(other, STBox): result = distance_tpoint_geo(self._inner, stbox_to_geo(other._inner)) @@ -964,7 +964,7 @@ def nearest_approach_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) return nad_tpoint_geo(self._inner, gs) elif isinstance(other, STBox): return nad_tpoint_stbox(self._inner, other._inner) @@ -987,7 +987,7 @@ def nearest_approach_instant(self, other: Union[pg.Geometry, shpb.BaseGeometry, nai_tpoint_geo, nai_tpoint_tpoint """ if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = nai_tpoint_geo(self._inner, gs) elif isinstance(other, TPoint): result = nai_tpoint_tpoint(self._inner, other._inner) @@ -1010,7 +1010,7 @@ def shortest_line(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint]) -> shortestline_tpoint_geo, shortestline_tpoint_tpoint """ if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = shortestline_tpoint_geo(self._inner, gs) elif isinstance(other, TPoint): result = shortestline_tpoint_tpoint(self._inner, other._inner) @@ -1204,7 +1204,6 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], base: Tempo Args: value: The base geometry. base: The temporal object defining the time frame. - interpolation: The interpolation method. Returns: A new :class:`TGeomPoint` object. @@ -1212,7 +1211,7 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], base: Tempo MEOS Functions: tgeompoint_from_base_temp """ - gs = geometry_to_gserialized(value, False) + gs = geometry_to_gserialized(value) result = tgeompoint_from_base_temp(gs, base._inner) return Temporal._factory(result) @@ -1249,7 +1248,7 @@ def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Time, tgeompointinst_make, tgeompointseq_from_base_timestampset, tgeompointseq_from_base_period, tgeompointseqset_from_base_periodset """ - gs = geometry_to_gserialized(value, False) + gs = geometry_to_gserialized(value) if isinstance(base, datetime): return TGeomPointInst(_inner=tgeompointinst_make(gs, datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): @@ -1316,7 +1315,7 @@ def always_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: MEOS Functions: tgeompoint_always_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geometry_to_gserialized(value) return tgeompoint_always_eq(self._inner, gs) def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: @@ -1332,7 +1331,7 @@ def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool MEOS Functions: tgeompoint_ever_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geometry_to_gserialized(value) return not tgeompoint_ever_eq(self._inner, gs) def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: @@ -1348,7 +1347,7 @@ def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: MEOS Functions: tgeompoint_ever_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geometry_to_gserialized(value) return tgeompoint_ever_eq(self._inner, gs) def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: @@ -1364,7 +1363,7 @@ def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: MEOS Functions: tgeompoint_always_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geometry_to_gserialized(value) return not tgeompoint_always_eq(self._inner, gs) def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: @@ -1380,7 +1379,7 @@ def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: MEOS Functions: tgeompoint_ever_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geometry_to_gserialized(value) return not tgeompoint_ever_eq(self._inner, gs) def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: @@ -1396,7 +1395,7 @@ def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: MEOS Functions: tgeompoint_always_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geometry_to_gserialized(value) return tgeompoint_always_eq(self._inner, gs) # ------------------------- Temporal Comparisons -------------------------- @@ -1414,7 +1413,7 @@ def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: teq_tgeompoint_point, teq_temporal_temporal """ if isinstance(other, pg.Point) or isinstance(other, shp.Point): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geometry_to_gserialized(other) result = teq_tgeompoint_point(self._inner, gs) else: return super().temporal_equal(other) @@ -1434,7 +1433,7 @@ def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> Tem tne_tgeompoint_point, tne_temporal_temporal """ if isinstance(other, pg.Point) or isinstance(other, shp.Point): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geometry_to_gserialized(other) result = tne_tgeompoint_point(self._inner, gs) else: return super().temporal_not_equal(other) @@ -1483,7 +1482,6 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], base: Tempo Args: value: The base geometry. base: The temporal object defining the time frame. - interpolation: The interpolation method. Returns: A new :class:`TGeogPoint` object. @@ -1491,7 +1489,7 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], base: Tempo MEOS Functions: tgeogpoint_from_base_temp """ - gs = geometry_to_gserialized(value, True) + gs = geography_to_gserialized(value) result = tgeogpoint_from_base_temp(gs, base._inner) return Temporal._factory(result) @@ -1528,7 +1526,7 @@ def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Time, tgeogpointinst_make, tgeogpointseq_from_base_timestampset, tgeogpointseq_from_base_period, tgeogpointseqset_from_base_periodset """ - gs = geometry_to_gserialized(value, True) + gs = geography_to_gserialized(value) if isinstance(base, datetime): return TGeogPointInst(_inner=tgeogpointinst_make(gs, datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): @@ -1567,7 +1565,7 @@ def always_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: MEOS Functions: tgeogpoint_always_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geography_to_gserialized(value) return tgeogpoint_always_eq(self._inner, gs) def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: @@ -1583,7 +1581,7 @@ def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool MEOS Functions: tgeogpoint_ever_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geography_to_gserialized(value) return not tgeogpoint_ever_eq(self._inner, gs) def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: @@ -1599,7 +1597,7 @@ def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: MEOS Functions: tgeogpoint_ever_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geography_to_gserialized(value) return tgeogpoint_ever_eq(self._inner, gs) def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: @@ -1615,7 +1613,7 @@ def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: MEOS Functions: tgeogpoint_always_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geography_to_gserialized(value) return not tgeogpoint_always_eq(self._inner, gs) def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: @@ -1631,7 +1629,7 @@ def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: MEOS Functions: tgeogpoint_ever_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geography_to_gserialized(value) return not tgeogpoint_ever_eq(self._inner, gs) def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: @@ -1647,7 +1645,7 @@ def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: MEOS Functions: tgeogpoint_always_eq """ - gs = geometry_to_gserialized(value, isinstance(self, TGeogPoint)) + gs = geography_to_gserialized(value) return tgeogpoint_always_eq(self._inner, gs) # ------------------------- Temporal Comparisons -------------------------- @@ -1665,7 +1663,7 @@ def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: teq_tgeogpoint_point, teq_temporal_temporal """ if isinstance(other, pg.Point) or isinstance(other, shp.Point): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geography_to_gserialized(other) result = teq_tgeogpoint_point(self._inner, gs) else: return super().temporal_equal(other) @@ -1685,7 +1683,7 @@ def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBo tne_tgeogpoint_point, tne_temporal_temporal """ if isinstance(other, pg.Point) or isinstance(other, shp.Point): - gs = geometry_to_gserialized(other, isinstance(self, TGeogPoint)) + gs = geography_to_gserialized(other) result = tne_tgeogpoint_point(self._inner, gs) else: return super().temporal_not_equal(other) diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index a2b4d0fc..0d0b22fd 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -7,7 +7,9 @@ 'timestamptz_to_datetime', 'timedelta_to_interval', 'interval_to_timedelta', + 'geo_to_gserialized', 'geometry_to_gserialized', + 'geography_to_gserialized', 'gserialized_to_shapely_point', 'gserialized_to_shapely_geometry', 'intrange_to_intspan', diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index d8f95c43..eee36478 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -78,22 +78,40 @@ def interval_to_timedelta(interval: Any) -> timedelta: return timedelta(days=interval.day, microseconds=interval.time) -def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry], geodetic: Optional[bool] = None) -> 'GSERIALIZED *': +def geo_to_gserialized(geom: Union[pg.Geometry, BaseGeometry], geodetic: bool) -> 'GSERIALIZED *': + if geodetic: + return geography_to_gserialized(geom) + else: + return geometry_to_gserialized(geom) + + +def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': + if isinstance(geom, pg.Geometry): + text = geom.to_ewkb() + # if geom.has_srid(): + # text = f'SRID={geom.srid};{text}' + elif isinstance(geom, BaseGeometry): + text = wkb.dumps(geom, hex=True) + if get_srid(geom) > 0: + text = f'SRID={get_srid(geom)};{text}' + else: + raise TypeError('Parameter geom must be either a PostGIS Geometry or a Shapely BaseGeometry') + gs = pgis_geometry_in(text, -1) + return gs + + +def geography_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': if isinstance(geom, pg.Geometry): text = geom.to_ewkb() # if geom.has_srid(): - # text = f'SRID={geom.srid};{text}' + # text = f'SRID={geom.srid};{text}' elif isinstance(geom, BaseGeometry): text = wkb.dumps(geom, hex=True) if get_srid(geom) > 0: text = f'SRID={get_srid(geom)};{text}' else: raise TypeError('Parameter geom must be either a PostGIS Geometry or a Shapely BaseGeometry') - gs = gserialized_in(text, -1) - if geodetic is not None: - # GFlags is an 8-bit integer, where the 4th bit is the geodetic flag (0x80) - # If geodetic is True, then set the 4th bit to 1, otherwise set it to 0 - gs.gflags = (gs.gflags | 0x08) if geodetic else (gs.gflags & 0xF7) + gs = pgis_geography_in(text, -1) return gs diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 53aa7737..2f44e414 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -38,22 +38,40 @@ def interval_to_timedelta(interval: Any) -> timedelta: return timedelta(days=interval.day, microseconds=interval.time) -def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry], geodetic: Optional[bool] = None) -> 'GSERIALIZED *': +def geo_to_gserialized(geom: Union[pg.Geometry, BaseGeometry], geodetic: bool) -> 'GSERIALIZED *': + if geodetic: + return geography_to_gserialized(geom) + else: + return geometry_to_gserialized(geom) + + +def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': + if isinstance(geom, pg.Geometry): + text = geom.to_ewkb() + # if geom.has_srid(): + # text = f'SRID={geom.srid};{text}' + elif isinstance(geom, BaseGeometry): + text = wkb.dumps(geom, hex=True) + if get_srid(geom) > 0: + text = f'SRID={get_srid(geom)};{text}' + else: + raise TypeError('Parameter geom must be either a PostGIS Geometry or a Shapely BaseGeometry') + gs = pgis_geometry_in(text, -1) + return gs + + +def geography_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': if isinstance(geom, pg.Geometry): text = geom.to_ewkb() # if geom.has_srid(): - # text = f'SRID={geom.srid};{text}' + # text = f'SRID={geom.srid};{text}' elif isinstance(geom, BaseGeometry): text = wkb.dumps(geom, hex=True) if get_srid(geom) > 0: text = f'SRID={get_srid(geom)};{text}' else: raise TypeError('Parameter geom must be either a PostGIS Geometry or a Shapely BaseGeometry') - gs = gserialized_in(text, -1) - if geodetic is not None: - # GFlags is an 8-bit integer, where the 4th bit is the geodetic flag (0x80) - # If geodetic is True, then set the 4th bit to 1, otherwise set it to 0 - gs.gflags = (gs.gflags | 0x08) if geodetic else (gs.gflags & 0xF7) + gs = pgis_geography_in(text, -1) return gs @@ -755,9 +773,9 @@ def timestampset_out(set: 'const Set *') -> str: return result if result != _ffi.NULL else None -def bigintset_make(values: 'List[const int64]') -> 'Set *': - values_converted = _ffi.new('const int64 []', values) - result = _lib.bigintset_make(values_converted, len(values)) +def bigintset_make(values: 'const int64 *', count: int) -> 'Set *': + values_converted = _ffi.cast('const int64 *', values) + result = _lib.bigintset_make(values_converted, count) return result if result != _ffi.NULL else None @@ -768,9 +786,9 @@ def bigintspan_make(lower: int, upper: int, lower_inc: bool, upper_inc: bool) -> return result if result != _ffi.NULL else None -def floatset_make(values: 'List[const double]') -> 'Set *': - values_converted = _ffi.new('const double []', values) - result = _lib.floatset_make(values_converted, len(values)) +def floatset_make(values: 'const double *', count: int) -> 'Set *': + values_converted = _ffi.cast('const double *', values) + result = _lib.floatset_make(values_converted, count) return result if result != _ffi.NULL else None @@ -779,15 +797,15 @@ def floatspan_make(lower: float, upper: float, lower_inc: bool, upper_inc: bool) return result if result != _ffi.NULL else None -def geoset_make(values: 'const GSERIALIZED **') -> 'Set *': +def geoset_make(values: 'const GSERIALIZED **', count: int) -> 'Set *': values_converted = [_ffi.cast('const GSERIALIZED *', x) for x in values] - result = _lib.geoset_make(values_converted, len(values)) + result = _lib.geoset_make(values_converted, count) return result if result != _ffi.NULL else None -def intset_make(values: 'List[const int]') -> 'Set *': - values_converted = _ffi.new('const int []', values) - result = _lib.intset_make(values_converted, len(values)) +def intset_make(values: 'const int *', count: int) -> 'Set *': + values_converted = _ffi.cast('const int *', values) + result = _lib.intset_make(values_converted, count) return result if result != _ffi.NULL else None @@ -839,9 +857,9 @@ def spanset_make_free(spans: 'Span *', count: int, normalize: bool) -> 'SpanSet return result if result != _ffi.NULL else None -def textset_make(values: 'List[const text]') -> 'Set *': +def textset_make(values: 'const text **', count: int) -> 'Set *': values_converted = [_ffi.cast('const text *', x) for x in values] - result = _lib.textset_make(values_converted, len(values)) + result = _lib.textset_make(values_converted, count) return result if result != _ffi.NULL else None From 28ba7a396fa1de1c1827168893f4b1ad581fc132 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 22 Aug 2023 16:53:34 +0200 Subject: [PATCH 005/101] Fix *_to_gserialized calls (use wkt instead of wkb) --- .../builder/build_pymeos_functions.py | 8 ++--- pymeos_cffi/pymeos_cffi/functions.py | 34 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index eee36478..ccb88027 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -87,11 +87,11 @@ def geo_to_gserialized(geom: Union[pg.Geometry, BaseGeometry], geodetic: bool) - def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': if isinstance(geom, pg.Geometry): - text = geom.to_ewkb() + text = geom.wkt # if geom.has_srid(): # text = f'SRID={geom.srid};{text}' elif isinstance(geom, BaseGeometry): - text = wkb.dumps(geom, hex=True) + text = wkt.dumps(geom) if get_srid(geom) > 0: text = f'SRID={get_srid(geom)};{text}' else: @@ -102,11 +102,11 @@ def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALI def geography_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': if isinstance(geom, pg.Geometry): - text = geom.to_ewkb() + text = geom.wkt # if geom.has_srid(): # text = f'SRID={geom.srid};{text}' elif isinstance(geom, BaseGeometry): - text = wkb.dumps(geom, hex=True) + text = wkt.dumps(geom) if get_srid(geom) > 0: text = f'SRID={get_srid(geom)};{text}' else: diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 2f44e414..6d0619fe 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -47,11 +47,11 @@ def geo_to_gserialized(geom: Union[pg.Geometry, BaseGeometry], geodetic: bool) - def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': if isinstance(geom, pg.Geometry): - text = geom.to_ewkb() + text = geom.wkt # if geom.has_srid(): # text = f'SRID={geom.srid};{text}' elif isinstance(geom, BaseGeometry): - text = wkb.dumps(geom, hex=True) + text = wkt.dumps(geom) if get_srid(geom) > 0: text = f'SRID={get_srid(geom)};{text}' else: @@ -62,11 +62,11 @@ def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALI def geography_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': if isinstance(geom, pg.Geometry): - text = geom.to_ewkb() + text = geom.wkt # if geom.has_srid(): # text = f'SRID={geom.srid};{text}' elif isinstance(geom, BaseGeometry): - text = wkb.dumps(geom, hex=True) + text = wkt.dumps(geom) if get_srid(geom) > 0: text = f'SRID={get_srid(geom)};{text}' else: @@ -773,9 +773,9 @@ def timestampset_out(set: 'const Set *') -> str: return result if result != _ffi.NULL else None -def bigintset_make(values: 'const int64 *', count: int) -> 'Set *': - values_converted = _ffi.cast('const int64 *', values) - result = _lib.bigintset_make(values_converted, count) +def bigintset_make(values: 'List[const int64]') -> 'Set *': + values_converted = _ffi.new('const int64 []', values) + result = _lib.bigintset_make(values_converted, len(values)) return result if result != _ffi.NULL else None @@ -786,9 +786,9 @@ def bigintspan_make(lower: int, upper: int, lower_inc: bool, upper_inc: bool) -> return result if result != _ffi.NULL else None -def floatset_make(values: 'const double *', count: int) -> 'Set *': - values_converted = _ffi.cast('const double *', values) - result = _lib.floatset_make(values_converted, count) +def floatset_make(values: 'List[const double]') -> 'Set *': + values_converted = _ffi.new('const double []', values) + result = _lib.floatset_make(values_converted, len(values)) return result if result != _ffi.NULL else None @@ -797,15 +797,15 @@ def floatspan_make(lower: float, upper: float, lower_inc: bool, upper_inc: bool) return result if result != _ffi.NULL else None -def geoset_make(values: 'const GSERIALIZED **', count: int) -> 'Set *': +def geoset_make(values: 'const GSERIALIZED **') -> 'Set *': values_converted = [_ffi.cast('const GSERIALIZED *', x) for x in values] - result = _lib.geoset_make(values_converted, count) + result = _lib.geoset_make(values_converted, len(values)) return result if result != _ffi.NULL else None -def intset_make(values: 'const int *', count: int) -> 'Set *': - values_converted = _ffi.cast('const int *', values) - result = _lib.intset_make(values_converted, count) +def intset_make(values: 'List[const int]') -> 'Set *': + values_converted = _ffi.new('const int []', values) + result = _lib.intset_make(values_converted, len(values)) return result if result != _ffi.NULL else None @@ -857,9 +857,9 @@ def spanset_make_free(spans: 'Span *', count: int, normalize: bool) -> 'SpanSet return result if result != _ffi.NULL else None -def textset_make(values: 'const text **', count: int) -> 'Set *': +def textset_make(values: 'List[const text]') -> 'Set *': values_converted = [_ffi.cast('const text *', x) for x in values] - result = _lib.textset_make(values_converted, count) + result = _lib.textset_make(values_converted, len(values)) return result if result != _ffi.NULL else None From 7ac3f040e7030c274fa5d44711b8b5eab4a18bb6 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Tue, 22 Aug 2023 17:05:03 +0200 Subject: [PATCH 006/101] Tests for temporal types --- pymeos/pymeos/boxes/tbox.py | 2 +- pymeos/pymeos/main/tfloat.py | 8 +- pymeos/pymeos/main/tint.py | 10 +-- pymeos/tests/boxes/stbox_test.py | 36 ++++----- pymeos/tests/boxes/tbox_test.py | 36 ++++----- pymeos/tests/main/tgeogpoint_test.py | 105 ++++++++++++++------------- pymeos/tests/main/tgeompoint_test.py | 26 +++---- pymeos/tests/main/tint_test.py | 1 + 8 files changed, 113 insertions(+), 111 deletions(-) diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index 49f9eb74..211ba8b0 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -240,7 +240,7 @@ def __str__(self, max_decimals: int = 15): """ return tbox_out(self._inner, max_decimals) - def __repr__(self, max_decimals=15): + def __repr__(self, max_decimals: int = 15): """ Returns a string representation of ``self``. diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index 0a74e9ff..dfa6bd7c 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -82,7 +82,7 @@ def from_base_time(value: float, base: Time, interpolation: TInterpolation = Non raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Output ---------------------------------------- - def __str__(self, max_decimals=15): + def __str__(self, max_decimals: int = 15): """ Returns a string representation of `self`. @@ -94,12 +94,12 @@ def __str__(self, max_decimals=15): """ return tfloat_out(self._inner, max_decimals) - def as_wkt(self, precision: int = 15) -> str: + def as_wkt(self, max_decimals: int = 15) -> str: """ Returns a WKT representation of `self`. Args: - precision: The number of decimals to use. + max_decimals: The number of decimals to use. Returns: A WKT representation of `self`. @@ -107,7 +107,7 @@ def as_wkt(self, precision: int = 15) -> str: MEOS Functions: tfloat_out """ - return tfloat_out(self._inner, precision) + return tfloat_out(self._inner, max_decimals) # ------------------------- Conversions ---------------------------------- def to_tint(self) -> TInt: diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index ff8a7b0c..6a6d2f49 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -669,11 +669,11 @@ def minus(self, other: Union[int, List[int], elif isinstance(other, list) and isinstance(other[0], int): # result = reduce(tint_minus_value, other, self._inner) # result = tint_minus_values(self._inner, other) - # results = [tint_minus_value(self._inner, value) for value in other if other is not None] - # result = temporal_merge_array(results, len(results)) - result = tint_minus_value(self._inner, other) - for i in 1..len(other): - result = result.minus_value(other[i]) + results = [tint_minus_value(self._inner, value) for value in other if other is not None] + result = temporal_merge_array(results, len(results)) + # result = tint_minus_value(self._inner, other) + # for i in 1..len(other): + # result = result.minus_value(other[i]) else: return super().minus(other) return Temporal._factory(result) diff --git a/pymeos/tests/boxes/stbox_test.py b/pymeos/tests/boxes/stbox_test.py index 16d19526..011238ab 100644 --- a/pymeos/tests/boxes/stbox_test.py +++ b/pymeos/tests/boxes/stbox_test.py @@ -55,24 +55,24 @@ def test_from_geometry_constructor(self, geometry, expected): assert isinstance(stb, STBox) assert str(stb) == expected - # @pytest.mark.parametrize( - # 'time, expected', - # [ - # (datetime(2019, 9, 1), - # 'STBOX T([2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - # (TimestampSet('{2019-09-01, 2019-09-02}'), - # 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (Period('[2019-09-01, 2019-09-02]'), - # 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (PeriodSet('{[2019-09-01, 2019-09-02],[2019-09-03, 2019-09-05]}'), - # 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # ], - # ids=['Timestamp', 'TimestampSet', 'Period', 'PeriodSet'] - # ) - # def test_from_time_constructor(self, time, expected): - # stb = STBox.from_time(time) - # assert isinstance(stb, STBox) - # assert str(stb) == expected + @pytest.mark.parametrize( + 'time, expected', + [ + (datetime(2019, 9, 1), + 'STBOX T([2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + (TimestampSet('{2019-09-01, 2019-09-02}'), + 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (Period('[2019-09-01, 2019-09-02]'), + 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (PeriodSet('{[2019-09-01, 2019-09-02],[2019-09-03, 2019-09-05]}'), + 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + ], + ids=['Timestamp', 'TimestampSet', 'Period', 'PeriodSet'] + ) + def test_from_time_constructor(self, time, expected): + stb = STBox.from_time(time) + assert isinstance(stb, STBox) + assert str(stb) == expected @pytest.mark.parametrize( 'geometry, time, expected', diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index 7c12bc1e..ef9e11db 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -84,24 +84,24 @@ def test_from_value_constructor(self, value, expected): assert isinstance(tb, TBox) assert str(tb) == expected - # @pytest.mark.parametrize( - # 'time, expected', - # [ - # (datetime(2019, 9, 1), - # 'TBOX T([2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - # (TimestampSet('{2019-09-01, 2019-09-02}'), - # 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (Period('[2019-09-01, 2019-09-02]'), - # 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (PeriodSet('{[2019-09-01, 2019-09-02],[2019-09-03, 2019-09-05]}'), - # 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # ], - # ids=['Timestamp', 'TimestampSet', 'Period', 'PeriodSet'] - # ) - # def test_from_time_constructor(self, time, expected): - # tb = TBox.from_time(time) - # assert isinstance(tb, TBox) - # assert str(tb) == expected + @pytest.mark.parametrize( + 'time, expected', + [ + (datetime(2019, 9, 1), + 'TBOX T([2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + (TimestampSet('{2019-09-01, 2019-09-02}'), + 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (Period('[2019-09-01, 2019-09-02]'), + 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (PeriodSet('{[2019-09-01, 2019-09-02],[2019-09-03, 2019-09-05]}'), + 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + ], + ids=['Timestamp', 'TimestampSet', 'Period', 'PeriodSet'] + ) + def test_from_time_constructor(self, time, expected): + tb = TBox.from_time(time) + assert isinstance(tb, TBox) + assert str(tb) == expected # @pytest.mark.parametrize( # 'value, time, expected', diff --git a/pymeos/tests/main/tgeogpoint_test.py b/pymeos/tests/main/tgeogpoint_test.py index 6069d5c2..f160a6ab 100644 --- a/pymeos/tests/main/tgeogpoint_test.py +++ b/pymeos/tests/main/tgeogpoint_test.py @@ -81,22 +81,22 @@ def test_string_constructor(self, source, type, interpolation, expected): assert tp.interpolation() == interpolation assert str(tp) == expected - # @pytest.mark.parametrize( - # 'source, type, expected', - # [ - # ('[Point(1 1)@2019-09-01, Point(1.25 1.25)@2019-09-02, Point(1.5 1.5)@2019-09-03, Point(2 2)@2019-09-05]', TGeogPointSeq, - # '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-05 00:00:00+00]'), - # ('{[Point(1 1)@2019-09-01, Point(1.25 1.25)@2019-09-02, Point(1.5 1.5)@2019-09-03, Point(2 2)@2019-09-05],' - # '[Point(1 1)@2019-09-07, Point(1 1)@2019-09-08, Point(1 1)@2019-09-09]}', TGeogPointSeqSet, - # '{[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-05 00:00:00+00], ' - # '[POINT(1 1)@2019-09-07 00:00:00+00, POINT(1 1)@2019-09-09 00:00:00+00]}'), - # ], - # ids=['Sequence', 'SequenceSet'] - # ) - # def test_string_constructor_normalization(self, source, type, expected): - # tp = type(source, normalize=True) - # assert isinstance(tp, type) - # assert str(tp) == expected + @pytest.mark.parametrize( + 'source, type, expected', + [ + ('[Point(1 1)@2019-09-01, Point(1.249919068145015 1.250040436011492)@2019-09-02, Point(2 2)@2019-09-05]', TGeogPointSeq, + '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-05 00:00:00+00]'), + ('{[Point(1 1)@2019-09-01, POINT(1.249919068145015 1.250040436011492)@2019-09-02, Point(2 2)@2019-09-05],' + '[Point(1 1)@2019-09-07, Point(1 1)@2019-09-08, Point(1 1)@2019-09-09]}', TGeogPointSeqSet, + '{[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-05 00:00:00+00], ' + '[POINT(1 1)@2019-09-07 00:00:00+00, POINT(1 1)@2019-09-09 00:00:00+00]}'), + ], + ids=['Sequence', 'SequenceSet'] + ) + def test_string_constructor_normalization(self, source, type, expected): + tp = type(source, normalize=True) + assert isinstance(tp, type) + assert str(tp) == expected @pytest.mark.parametrize( 'value, timestamp', @@ -112,42 +112,43 @@ def test_value_timestamp_instant_constructor(self, value, timestamp): tpi = TGeogPointInst(point=value, timestamp=timestamp) assert str(tpi) == 'POINT(1 1)@2019-09-01 00:00:00+00' - # @pytest.mark.parametrize( - # 'list, interpolation, normalize, expected', - # [ - # (['Point(1 1)@2019-09-01', 'Point(2 2)@2019-09-03'], TInterpolation.DISCRETE, False, - # '{POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00}'), - # (['Point(1 1)@2019-09-01', 'Point(2 2)@2019-09-03'], TInterpolation.LINEAR, False, - # '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), - # ([TGeogPointInst('Point(1 1)@2019-09-01'), TGeogPointInst('Point(2 2)@2019-09-03')], TInterpolation.DISCRETE, False, - # '{POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00}'), - # ([TGeogPointInst('Point(1 1)@2019-09-01'), TGeogPointInst('Point(2 2)@2019-09-03')], TInterpolation.LINEAR, False, - # '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), - # (['Point(1 1)@2019-09-01', TGeogPointInst('Point(2 2)@2019-09-03')], TInterpolation.DISCRETE, False, - # '{POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00}'), - # (['Point(1 1)@2019-09-01', TGeogPointInst('Point(2 2)@2019-09-03')], TInterpolation.LINEAR, False, - # '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), - - # (['Point(1 1)@2019-09-01', 'Point(1.5 1.5)@2019-09-02', 'Point(2 2)@2019-09-03'], TInterpolation.LINEAR, True, - # '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), - # ([TGeogPointInst('Point(1 1)@2019-09-01'), TGeogPointInst('Point(1.5 1.5)@2019-09-02'), TGeogPointInst('Point(2 2)@2019-09-03')], - # TInterpolation.LINEAR, True, - # '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), - # (['Point(1 1)@2019-09-01', 'Point(1.5 1.5)@2019-09-02', TGeogPointInst('Point(2 2)@2019-09-03')], TInterpolation.LINEAR, True, - # '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), - # ], - # ids=['String Discrete', 'String Linear', 'TGeogPointInst Discrete', 'TGeogPointInst Linear', 'Mixed Discrete', - # 'Mixed Linear', 'String Linear Normalized', 'TGeogPointInst Linear Normalized', - # 'Mixed Linear Normalized'] - # ) - # def test_instant_list_sequence_constructor(self, list, interpolation, normalize, expected): - # tps = TGeogPointSeq(instant_list=list, interpolation=interpolation, normalize=normalize, upper_inc=True) - # assert str(tps) == expected - # assert tps.interpolation() == interpolation - - # tps2 = TGeogPointSeq.from_instants(list, interpolation=interpolation, normalize=normalize, upper_inc=True) - # assert str(tps2) == expected - # assert tps2.interpolation() == interpolation + @pytest.mark.parametrize( + 'list, interpolation, normalize, expected', + [ + (['Point(1 1)@2019-09-01', 'Point(2 2)@2019-09-03'], TInterpolation.DISCRETE, False, + '{POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00}'), + (['Point(1 1)@2019-09-01', 'Point(2 2)@2019-09-03'], TInterpolation.LINEAR, False, + '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), + ([TGeogPointInst('Point(1 1)@2019-09-01'), TGeogPointInst('Point(2 2)@2019-09-03')], TInterpolation.DISCRETE, False, + '{POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00}'), + ([TGeogPointInst('Point(1 1)@2019-09-01'), TGeogPointInst('Point(2 2)@2019-09-03')], TInterpolation.LINEAR, False, + '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), + (['Point(1 1)@2019-09-01', TGeogPointInst('Point(2 2)@2019-09-03')], TInterpolation.DISCRETE, False, + '{POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00}'), + (['Point(1 1)@2019-09-01', TGeogPointInst('Point(2 2)@2019-09-03')], TInterpolation.LINEAR, False, + '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), + + (['Point(1 1)@2019-09-01', 'Point(1.499885736561676 1.500057091479197)@2019-09-02', 'Point(2 2)@2019-09-03'], TInterpolation.LINEAR, True, + '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), + ([TGeogPointInst('Point(1 1)@2019-09-01'), TGeogPointInst('Point(1.499885736561676 1.500057091479197)@2019-09-02'), TGeogPointInst('Point(2 2)@2019-09-03')], + TInterpolation.LINEAR, True, + '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), + (['Point(1 1)@2019-09-01', 'Point(1.499885736561676 1.500057091479197)@2019-09-02', TGeogPointInst('Point(2 2)@2019-09-03')], TInterpolation.LINEAR, True, + '[POINT(1 1)@2019-09-01 00:00:00+00, POINT(2 2)@2019-09-03 00:00:00+00]'), + ], + ids=['String Discrete', 'String Linear', 'TGeogPointInst Discrete', 'TGeogPointInst Linear', + 'Mixed Discrete', + 'Mixed Linear', 'String Linear Normalized', 'TGeogPointInst Linear Normalized', + 'Mixed Linear Normalized'] + ) + def test_instant_list_sequence_constructor(self, list, interpolation, normalize, expected): + tps = TGeogPointSeq(instant_list=list, interpolation=interpolation, normalize=normalize, upper_inc=True) + assert str(tps) == expected + assert tps.interpolation() == interpolation + + tps2 = TGeogPointSeq.from_instants(list, interpolation=interpolation, normalize=normalize, upper_inc=True) + assert str(tps2) == expected + assert tps2.interpolation() == interpolation @pytest.mark.parametrize( 'temporal', diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index 55d0a1ca..17b5e9ff 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -844,19 +844,19 @@ def test_length(self, temporal, expected): def test_cumulative_length(self, temporal, expected): assert temporal.cumulative_length() == expected - # @pytest.mark.parametrize( - # 'temporal, expected', - # [ - # (tpi, None), - # (tpds, None), - # (tps, TFloatSeq('Interp=Step;[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02]') / 3600 / 24), - # (tpss, TFloatSeqSet('Interp=Step;{[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02],' - # '[0@2019-09-03, 0@2019-09-05]}') / 3600 / 24), - # ], - # ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - # ) - # def test_speed(self, temporal, expected): - # assert temporal.speed() == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, None), + (tpds, None), + (tps, TFloatSeq('Interp=Step;[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02]') / 3600 / 24), + (tpss, TFloatSeqSet('Interp=Step;{[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02],' + '[0@2019-09-03, 0@2019-09-05]}') / 3600 / 24), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_speed(self, temporal, expected): + assert temporal.speed() == expected @pytest.mark.parametrize( 'temporal, expected', diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index f328b825..1bec8a73 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -1482,6 +1482,7 @@ def test_minus_max(self, temporal, expected): def test_minus_min(self, temporal, expected): assert temporal.minus_min() == expected + # TODO ADD the tests that are currently commented out in at and minus @pytest.mark.parametrize( 'temporal, restrictor', [ From a4b72332e0575af1415e3d6a88acde17cfdea158 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 22 Aug 2023 17:24:36 +0200 Subject: [PATCH 007/101] Fix Box from_time constructors --- pymeos/pymeos/boxes/tbox.py | 25 +++++---- pymeos/tests/boxes/__init__.py | 0 pymeos/tests/boxes/stbox_test.py | 36 ++++++------- pymeos/tests/boxes/tbox_test.py | 92 ++++++++++++++++---------------- 4 files changed, 76 insertions(+), 77 deletions(-) create mode 100644 pymeos/tests/boxes/__init__.py diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index 49f9eb74..179841b4 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -48,10 +48,10 @@ def __init__(self, string: Optional[str] = None, *, xmax: Optional[Union[str, int, float]] = None, tmin: Optional[Union[str, datetime]] = None, tmax: Optional[Union[str, datetime]] = None, - xmin_inc: Optional[bool] = True, - xmax_inc: Optional[bool] = False, - tmin_inc: Optional[bool] = True, - tmax_inc: Optional[bool] = False, + xmin_inc: bool = True, + xmax_inc: bool = False, + tmin_inc: bool = True, + tmax_inc: bool = False, _inner=None): assert (_inner is not None) or (string is not None) != ( (xmin is not None and xmax is not None) or (tmin is not None and tmax is not None)), \ @@ -162,14 +162,13 @@ def from_time(time: Time) -> TBox: timestamp_to_tbox, timestampset_to_tbox, period_to_tbox, periodset_to_tbox """ if isinstance(time, datetime): - result = (datetime, datetime) + result = timestamp_to_tbox(datetime_to_timestamptz(time)) elif isinstance(time, TimestampSet): - result = (time.start_timestamp(), time.end_timestamp()) + result = timestampset_to_tbox(time._inner) elif isinstance(time, Period): - result = (time.lower, time.upper, time.lower_inc, time.upper_inc) + result = period_to_tbox(time._inner) elif isinstance(time, PeriodSet): - result = (time.start_period().lower, time.end_period().upper, - time.start_period().lower_inc, time.end_period().upper_inc) + result = periodset_to_tbox(time._inner) else: raise TypeError(f'Operation not supported with type {time.__class__}') return TBox(_inner=result) @@ -194,19 +193,19 @@ def from_value_time(value: Union[int, float, intrange, floatrange], if isinstance(value, int) and isinstance(time, datetime): result = int_timestamp_to_tbox(value, datetime_to_timestamptz(time)) elif isinstance(value, int) and isinstance(time, Period): - result = int_period_to_tbox(value, time) + result = int_period_to_tbox(value, time._inner) elif isinstance(value, float) and isinstance(time, datetime): result = float_timestamp_to_tbox(value, datetime_to_timestamptz(time)) elif isinstance(value, float) and isinstance(time, Period): - result = float_period_to_tbox(value, time) + result = float_period_to_tbox(value, time._inner) elif isinstance(value, intrange) and isinstance(time, datetime): result = span_timestamp_to_tbox(intrange_to_intspan(value), datetime_to_timestamptz(time)) elif isinstance(value, intrange) and isinstance(time, Period): - result = span_period_to_tbox(intrange_to_intspan(value), time) + result = span_period_to_tbox(intrange_to_intspan(value), time._inner) elif isinstance(value, floatrange) and isinstance(time, datetime): result = span_timestamp_to_tbox(floatrange_to_floatspan(value), datetime_to_timestamptz(time)) elif isinstance(value, floatrange) and isinstance(time, Period): - result = span_period_to_tbox(floatrange_to_floatspan(value), time) + result = span_period_to_tbox(floatrange_to_floatspan(value), time._inner) else: raise TypeError(f'Operation not supported with types {value.__class__} and {time.__class__}') return TBox(_inner=result) diff --git a/pymeos/tests/boxes/__init__.py b/pymeos/tests/boxes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymeos/tests/boxes/stbox_test.py b/pymeos/tests/boxes/stbox_test.py index 16d19526..5bd83410 100644 --- a/pymeos/tests/boxes/stbox_test.py +++ b/pymeos/tests/boxes/stbox_test.py @@ -55,24 +55,24 @@ def test_from_geometry_constructor(self, geometry, expected): assert isinstance(stb, STBox) assert str(stb) == expected - # @pytest.mark.parametrize( - # 'time, expected', - # [ - # (datetime(2019, 9, 1), - # 'STBOX T([2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - # (TimestampSet('{2019-09-01, 2019-09-02}'), - # 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (Period('[2019-09-01, 2019-09-02]'), - # 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (PeriodSet('{[2019-09-01, 2019-09-02],[2019-09-03, 2019-09-05]}'), - # 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # ], - # ids=['Timestamp', 'TimestampSet', 'Period', 'PeriodSet'] - # ) - # def test_from_time_constructor(self, time, expected): - # stb = STBox.from_time(time) - # assert isinstance(stb, STBox) - # assert str(stb) == expected + @pytest.mark.parametrize( + 'time, expected', + [ + (datetime(2019, 9, 1), + 'STBOX T([2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + (TimestampSet('{2019-09-01, 2019-09-02}'), + 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (Period('[2019-09-01, 2019-09-02]'), + 'STBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (PeriodSet('{[2019-09-01, 2019-09-02],[2019-09-03, 2019-09-05]}'), + 'STBOX T([2019-09-01 00:00:00+00, 2019-09-05 00:00:00+00])'), + ], + ids=['Timestamp', 'TimestampSet', 'Period', 'PeriodSet'] + ) + def test_from_time_constructor(self, time, expected): + stb = STBox.from_time(time) + assert isinstance(stb, STBox) + assert str(stb) == expected @pytest.mark.parametrize( 'geometry, time, expected', diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index 7c12bc1e..fa10a2b4 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -84,52 +84,52 @@ def test_from_value_constructor(self, value, expected): assert isinstance(tb, TBox) assert str(tb) == expected - # @pytest.mark.parametrize( - # 'time, expected', - # [ - # (datetime(2019, 9, 1), - # 'TBOX T([2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - # (TimestampSet('{2019-09-01, 2019-09-02}'), - # 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (Period('[2019-09-01, 2019-09-02]'), - # 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (PeriodSet('{[2019-09-01, 2019-09-02],[2019-09-03, 2019-09-05]}'), - # 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # ], - # ids=['Timestamp', 'TimestampSet', 'Period', 'PeriodSet'] - # ) - # def test_from_time_constructor(self, time, expected): - # tb = TBox.from_time(time) - # assert isinstance(tb, TBox) - # assert str(tb) == expected - - # @pytest.mark.parametrize( - # 'value, time, expected', - # [ - # (1, datetime(2019, 9, 1), - # 'TBOXINT XT([1, 2),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - # (1.5, datetime(2019, 9, 1), - # 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - # (intrange(1, 2, True, True), datetime(2019, 9, 1), - # 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - # (floatrange(1.5, 2.5, True, True), datetime(2019, 9, 1), - # 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - # (1, Period('[2019-09-01, 2019-09-02]'), - # 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (1.5, Period('[2019-09-01, 2019-09-02]'), - # 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (intrange(1, 2, True, True), Period('[2019-09-01, 2019-09-02]'), - # 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # (floatrange(1.5, 2.5, True, True), Period('[2019-09-01, 2019-09-02]'), - # 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - # ], - # ids=['int-Timestamp', 'float-Timestamp', 'intrange-Timestamp', 'floatrange-Timestamp', - # 'int-Period', 'float-Period', 'intrange-Period', 'floatrange-Period',] - # ) - # def test_from_value_time_constructor(self, value, time, expected): - # tb = TBox.from_value_time(value, time) - # assert isinstance(tb, TBox) - # assert str(tb) == expected + @pytest.mark.parametrize( + 'time, expected', + [ + (datetime(2019, 9, 1), + 'TBOX T([2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + (TimestampSet('{2019-09-01, 2019-09-02}'), + 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (Period('[2019-09-01, 2019-09-02]'), + 'TBOX T([2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (PeriodSet('{[2019-09-01, 2019-09-02],[2019-09-03, 2019-09-05]}'), + 'TBOX T([2019-09-01 00:00:00+00, 2019-09-05 00:00:00+00])'), + ], + ids=['Timestamp', 'TimestampSet', 'Period', 'PeriodSet'] + ) + def test_from_time_constructor(self, time, expected): + tb = TBox.from_time(time) + assert isinstance(tb, TBox) + assert str(tb) == expected + + @pytest.mark.parametrize( + 'value, time, expected', + [ + (1, datetime(2019, 9, 1), + 'TBOXINT XT([1, 2),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + (1.5, datetime(2019, 9, 1), + 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + (intrange(1, 2, True, True), datetime(2019, 9, 1), + 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + (floatrange(1.5, 2.5, True, True), datetime(2019, 9, 1), + 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), + (1, Period('[2019-09-01, 2019-09-02]'), + 'TBOXINT XT([1, 2),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (1.5, Period('[2019-09-01, 2019-09-02]'), + 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (intrange(1, 2, True, True), Period('[2019-09-01, 2019-09-02]'), + 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + (floatrange(1.5, 2.5, True, True), Period('[2019-09-01, 2019-09-02]'), + 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), + ], + ids=['int-Timestamp', 'float-Timestamp', 'intrange-Timestamp', 'floatrange-Timestamp', + 'int-Period', 'float-Period', 'intrange-Period', 'floatrange-Period',] + ) + def test_from_value_time_constructor(self, value, time, expected): + tb = TBox.from_value_time(value, time) + assert isinstance(tb, TBox) + assert str(tb) == expected @pytest.mark.parametrize( 'tnumber, expected', From 084f24e603dfb96fbd3830ed0af46df37b110ca6 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Tue, 22 Aug 2023 19:55:06 +0200 Subject: [PATCH 008/101] Tests for temporal types --- pymeos/pymeos/main/tfloat.py | 11 +++++----- pymeos/pymeos/main/tint.py | 10 ++++----- pymeos/tests/main/tfloat_test.py | 16 +++++++------- pymeos/tests/main/tint_test.py | 37 +++++++++++++++++--------------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index dfa6bd7c..83ac82d4 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -639,11 +639,10 @@ def at(self, other: Union[int, float, List[int], List[float], elif isinstance(other, floatrange): result = tnumber_at_span(self._inner, floatrange_to_floatspan(other)) elif isinstance(other, list) and (isinstance(other[0], int) or isinstance(other[0], float)): - results = [tfloat_at_value(self._inner, float(other)) for value in other if other is not None] - result = temporal_merge_array(results, len(results)) - elif isinstance(other, list) and (isinstance(other[0], floatrange) or isinstance(other[0], intrange)): - results = [tnumber_at_span(self._inner, value) for value in other if other is not None] - result = temporal_merge_array(results, len(results)) + result = temporal_at_values(self._inner, floatset_make(other)) + # elif isinstance(other, list) and (isinstance(other[0], floatrange) or isinstance(other[0], intrange)): + # results = [tnumber_at_span(self._inner, value) for value in other if other is not None] + # result = temporal_merge_array(results, len(results)) else: return super().at(other) return Temporal._factory(result) @@ -670,7 +669,7 @@ def minus(self, other: Union[int, float, List[int], List[float], elif isinstance(other, floatrange): result = tnumber_minus_span(self._inner, floatrange_to_floatspan(other)) elif isinstance(other, list) and isinstance(other[0], float): - result = reduce(tfloat_minus_value, other, self._inner) + result = temporal_minus_values(self._inner, floatset_make(other)) else: return super().minus(other) return Temporal._factory(result) diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index 9f720103..355365c2 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -640,16 +640,14 @@ def at(self, other: Union[int, List[int], if isinstance(other, int): result = tint_at_value(self._inner, other) elif isinstance(other, list) and isinstance(other[0], int): - # result = tint_at_values(self._inner, other) - results = [tint_at_value(self._inner, value) for value in other if other is not None] - result = temporal_merge_array(results, len(results)) + result = temporal_at_values(self._inner, intset_make(other)) else: return super().at(other) return Temporal._factory(result) def minus(self, other: Union[int, List[int], - intrange, floatrange, List[intrange], List[floatrange], TBox, - datetime, TimestampSet, Period, PeriodSet]) -> Temporal: + intrange, floatrange, List[intrange], List[floatrange], TBox, + datetime, TimestampSet, Period, PeriodSet]) -> Temporal: """ Returns a new temporal int with the values of `self` restricted to the complement of the time or value `other`. @@ -667,7 +665,7 @@ def minus(self, other: Union[int, List[int], if isinstance(other, int): result = tint_minus_value(self._inner, other) elif isinstance(other, list) and isinstance(other[0], int): - result = temporal_minus_values(self._inner, intset_make(other, len(other))) + result = temporal_minus_values(self._inner, intset_make(other)) else: return super().minus(other) return Temporal._factory(result) diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index de1c98dc..be456dce 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -1435,21 +1435,21 @@ def test_at_time(self, temporal, restrictor, expected): (tfi, 1.5, TFloatInst('1.5@2019-09-01')), (tfi, 2.5, None), (tfi, floatrange(1.5, 1.5, True, True), TFloatInst('1.5@2019-09-01')), - # (tfi, [1.5,2.5], TFloatInst('1.5@2019-09-01')), + (tfi, [1.5,2.5], tfi), # (tfi, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], # TFloatInst('1.5@2019-09-01')), (tfds, 1.5, TFloatSeq('{1.5@2019-09-01}')), (tfds, 2.5, TFloatSeq('{2.5@2019-09-02}')), (tfds, floatrange(1.5, 1.5, True, True), TFloatInst('1.5@2019-09-01')), - # (tfds, [1.5,2.5], TFloatSeq('{1.5@2019-09-01}')), + (tfds, [1.5,2.5], tfds), # (tfds, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], # TFloatInst('1.5@2019-09-01')), (tfs, 1.5, TFloatSeq('[1.5@2019-09-01]')), (tfs, 2.5, TFloatSeq('[2.5@2019-09-02]')), (tfs, floatrange(1.5, 1.5, True, True), TFloatInst('1.5@2019-09-01')), - # (tfs, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), + (tfs, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02]}')), # (tfs, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], # TFloatInst('1.5@2019-09-01')), @@ -1457,18 +1457,18 @@ def test_at_time(self, temporal, restrictor, expected): (tfss, 2.5, TFloatSeqSet('{[2.5@2019-09-02]}')), (tfss, floatrange(1.5, 1.5, True, True), TFloatSeqSet('{[1.5@2019-09-01],[1.5@2019-09-03, 1.5@2019-09-05]}')), - # (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),[1.5@2019-09-03, 1.5@2019-09-05]}')) + (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')) # (tfss, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], # TFloatInst('1.5@2019-09-01')), ], ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', - # 'Instant-ValueList', 'Instant-RangeList', + 'Instant-ValueList', # 'Instant-RangeList', 'Discrete Sequence-1.5', 'Discrete Sequence-2.5', 'Discrete Sequence-Range', - # 'Discrete Sequence-ValueList', 'Discrete Sequence-RangeList' + 'Discrete Sequence-ValueList', # 'Discrete Sequence-RangeList' 'Sequence-1.5', 'Sequence-2.5', 'Sequence-Range', - # 'Sequence-ValueList', 'Sequence-RangeList', + 'Sequence-ValueList', # 'Sequence-RangeList', 'SequenceSet-1.5', 'SequenceSet-2.5', 'Sequence Set-Range', - # 'SequenceSet-ValueList', 'SequenceSet-RangeList' + 'SequenceSet-ValueList', # 'SequenceSet-RangeList' ] ) def test_at_values(self, temporal, restrictor, expected): diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index 1bec8a73..4e2e4ff5 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -1310,38 +1310,38 @@ def test_at_time(self, temporal, restrictor, expected): (tii, 1, TIntInst('1@2019-09-01')), (tii, 2, None), (tii, intrange(1, 1, True, True), TIntInst('1@2019-09-01')), - # (tii, [1,2], TIntInst('1@2019-09-01')), + (tii, [1,2], tii), # (tii, [intrange(1, 1, True, True), intrange(2, 2, True, True)], # TIntInst('1@2019-09-01')), (tids, 1, TIntSeq('{1@2019-09-01}')), (tids, 2, TIntSeq('{2@2019-09-02}')), (tids, intrange(1, 1, True, True), TIntSeq('{1@2019-09-01}')), - # (tids, [1,2], TIntSeq('{2@2019-09-02}')), + (tids, [1,2], tids), # (tids, [intrange(1, 1, True, True), tids), (tis, 1, TIntSeq('[1@2019-09-01, 1@2019-09-02)')), (tis, 2, TIntSeq('[2@2019-09-02]')), (tis, intrange(1, 1, True, True), TIntSeq('[1@2019-09-01, 1@2019-09-02)')), - # (tis, [1,2], TIntSeq('[2@2019-09-02]')), + (tis, [1,2], tis), # (tis, [intrange(1, 1, True, True), intrange(2, 2, True, True)], # TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), (tiss, 1, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), (tiss, 2, TIntSeqSet('{[2@2019-09-02]}')), (tiss, intrange(1, 1, True, True), TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), - # (tiss, [1,2], TIntSeqSet('{[2@2019-09-02]}')) + (tiss, [1,2], tiss) # (tiss, [intrange(1, 1, True, True), intrange(2, 2, True, True)], # tiss), ], ids=['Instant-1', 'Instant-2', 'Instant-Range', - # 'Instant-ValueList', 'Instant-RangeList', + 'Instant-ValueList', # 'Instant-RangeList', 'Discrete Sequence-1', 'Discrete Sequence-2', 'Discrete Sequence-Range', - # 'Discrete Sequence-ValueList', 'Discrete Sequence-RangeList', + 'Discrete Sequence-ValueList', # 'Discrete Sequence-RangeList', 'Sequence-1', 'Sequence-2', 'Sequence-Range', - # 'Sequence-ValueList', 'Sequence-RangeList', + 'Sequence-ValueList', # 'Sequence-RangeList', 'SequenceSet-1', 'SequenceSet-2', 'SequenceSet-Range', - # 'SequenceSet-ValueList', 'SequenceSet-RangeList' + 'SequenceSet-ValueList', # 'SequenceSet-RangeList' ] ) def test_at_values(self, temporal, restrictor, expected): @@ -1418,43 +1418,46 @@ def test_minus_time(self, temporal, restrictor, expected): (tii, 1, None), (tii, 2, TIntInst('1@2019-09-01')), (tii, intrange(1, 1, True, True), None), - # (tii, [1,2], None), + (tii, [1,2], None), # (tii, [intrange(1, 1, True, True), intrange(2, 2, True, True)], # None), (tids, 1, TIntSeq('{2@2019-09-02}')), (tids, 2, TIntSeq('{1@2019-09-01}')), (tids, intrange(1, 1, True, True), TIntSeq('{2@2019-09-02}')), - # (tids, [1,2], tids), + (tids, [1,2], None), # (tids, [intrange(1, 1, True, True), intrange(2, 2, True, True)], # None), (tis, 1, TIntSeqSet('{[2@2019-09-02]}')), (tis, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02)}')), (tis, intrange(1, 1, True, True), TIntSeqSet('{[2@2019-09-02]}')), - # (tis, [1,2], tis), + (tis, [1,2], None), # (tis, [intrange(1, 1, True, True), intrange(2, 2, True, True)], # None), (tiss, 1, TIntSeqSet('{[2@2019-09-02]}')), (tiss, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), (tis, intrange(1, 1, True, True), TIntSeqSet('{[2@2019-09-02]}')), - # (tiss, [1,2], tiss) + (tiss, [1,2], None) # (tiss, [intrange(1, 1, True, True), intrange(2, 2, True, True)], # None), ], ids=['Instant-1', 'Instant-2', 'Instant-Range', - # 'Instant-ValueList', 'Instant-RangeList', + 'Instant-ValueList', # 'Instant-RangeList', 'Discrete Sequence-1', 'Discrete Sequence-2', 'Discrete Sequence-Range', - # 'Discrete Sequence-ValueList', 'Discrete Sequence-RangeList', + 'Discrete Sequence-ValueList', # 'Discrete Sequence-RangeList', 'Sequence-1', 'Sequence-2', 'Sequence-Range', - # 'Sequence-ValueList', 'Sequence-RangeList', + 'Sequence-ValueList', # 'Sequence-RangeList', 'SequenceSet-1', 'SequenceSet-2', 'SequenceSet-Range', - # 'SequenceSet-ValueList', 'SequenceSet-RangeList', + 'SequenceSet-ValueList', # 'SequenceSet-RangeList', ] ) def test_minus_values(self, temporal, restrictor, expected): - assert temporal.minus(restrictor) == expected + if expected is None: + assert temporal.minus(restrictor) is None + else: + assert temporal.minus(restrictor) == expected @pytest.mark.parametrize( 'temporal, expected', From e75a5169f5709bfc809f7565a5d3685da4ee77f4 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 24 Aug 2023 10:18:58 +0200 Subject: [PATCH 009/101] Tests for temporal types --- pymeos/pymeos/main/ttext.py | 10 ++++------ pymeos/tests/main/ttext_test.py | 27 ++++++++++++++------------ pymeos_cffi/pymeos_cffi/__init__.py | 1 - pymeos_cffi/pymeos_cffi/builder/meos.h | 1 - pymeos_cffi/pymeos_cffi/functions.py | 6 ------ 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/pymeos/pymeos/main/ttext.py b/pymeos/pymeos/main/ttext.py index 331b29fa..1cd8831a 100644 --- a/pymeos/pymeos/main/ttext.py +++ b/pymeos/pymeos/main/ttext.py @@ -610,10 +610,8 @@ def at(self, other: Union[str, List[str], datetime, TimestampSet, Period, Period """ if isinstance(other, str): result = ttext_at_value(self._inner, other) - elif isinstance(other, list): - # result = ttext_at_values(self._inner, other) - results = [ttext_at_value(self._inner, value) for value in other] - result = temporal_merge_array(results, len(results)) + elif isinstance(other, list) and isinstance(other[0], str): + result = temporal_at_values(self._inner, textset_make(other)) else: return super().at(other) return Temporal._factory(result) @@ -635,8 +633,8 @@ def minus(self, other: Union[str, List[str], datetime, TimestampSet, Period, Per """ if isinstance(other, str): result = ttext_minus_value(self._inner, other) - elif isinstance(other, list): - result = reduce(ttext_minus_value, other, self._inner) + elif isinstance(other, list) and isinstance(other[0], str): + result = temporal_minus_values(self._inner, textset_make(other)) else: return super().minus(other) return Temporal._factory(result) diff --git a/pymeos/tests/main/ttext_test.py b/pymeos/tests/main/ttext_test.py index 20df2c0c..21782f20 100644 --- a/pymeos/tests/main/ttext_test.py +++ b/pymeos/tests/main/ttext_test.py @@ -1041,7 +1041,7 @@ class TestTTextRestrictors(TestTText): (tti, period_set, TTextInst('AAA@2019-09-01')), (tti, 'AAA', TTextInst('AAA@2019-09-01')), (tti, 'BBB', None), - # (tti, [AAA,BBB], None), + # (tti, ['AAA', 'BBB'], tti), (ttds, timestamp, TTextSeq('{AAA@2019-09-01}')), (ttds, timestamp_set, TTextSeq('{AAA@2019-09-01}')), @@ -1049,7 +1049,7 @@ class TestTTextRestrictors(TestTText): (ttds, period_set, TTextSeq('{AAA@2019-09-01, BBB@2019-09-02}')), (ttds, 'AAA', TTextSeq('{AAA@2019-09-01}')), (ttds, 'BBB', TTextSeq('{BBB@2019-09-02}')), - # (ttds, [AAA,BBB], TTextSeq('{BBB@2019-09-02}')), + # (ttds, ['AAA', 'BBB'], ttds), (tts, timestamp, TTextSeq('[AAA@2019-09-01]')), (tts, timestamp_set, TTextSeq('{AAA@2019-09-01}')), @@ -1057,7 +1057,7 @@ class TestTTextRestrictors(TestTText): (tts, period_set, TTextSeq('[AAA@2019-09-01, BBB@2019-09-02]')), (tts, 'AAA', TTextSeq('[AAA@2019-09-01, AAA@2019-09-02)')), (tts, 'BBB', TTextSeq('[BBB@2019-09-02]')), - # (tts, [AAA,BBB], TTextSeq('[BBB@2019-09-02]')), + # (tts, ['AAA', 'BBB'], tts), (ttss, timestamp, TTextSeqSet('[AAA@2019-09-01]')), (ttss, timestamp_set, TTextSeq('{AAA@2019-09-01, AAA@2019-09-03}')), @@ -1066,15 +1066,18 @@ class TestTTextRestrictors(TestTText): TTextSeqSet('{[AAA@2019-09-01, BBB@2019-09-02],[AAA@2019-09-03, AAA@2019-09-05]}')), (ttss, 'AAA', TTextSeqSet('{[AAA@2019-09-01, AAA@2019-09-02),[AAA@2019-09-03, AAA@2019-09-05]}')), (ttss, 'BBB', TTextSeqSet('{[BBB@2019-09-02]}')), - # (ttss, [AAA,BBB], TTextSeqSet('{[BBB@2019-09-02]}')) + # (ttss, ['AAA', 'BBB'], ttss) ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', 'Instant-AAA', 'Instant-BBB', # 'Instant-[AAA,BBB]', + 'Instant-PeriodSet', 'Instant-AAA', 'Instant-BBB', + # 'Instant-[AAA,BBB]', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', - 'Discrete Sequence-AAA', 'Discrete Sequence-BBB', # 'Discrete Sequence-[AAA,BBB]', + 'Discrete Sequence-AAA', 'Discrete Sequence-BBB', + # 'Discrete Sequence-[AAA,BBB]', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', 'Sequence-AAA', 'Sequence-BBB', # 'Sequence-[AAA,BBB]', + 'Sequence-PeriodSet', 'Sequence-AAA', 'Sequence-BBB', + # 'Sequence-[AAA,BBB]', 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', 'SequenceSet-PeriodSet', 'SequenceSet-AAA', 'SequenceSet-BBB', # 'SequenceSet-[AAA,BBB]' @@ -1119,7 +1122,7 @@ def test_at_min(self, temporal, expected): (tti, period_set, None), (tti, 'AAA', None), (tti, 'BBB', TTextInst('AAA@2019-09-01')), - # (tti, [AAA,BBB], None), + # (tti, ['AAA', 'BBB'], None), (ttds, timestamp, TTextSeq('{BBB@2019-09-02}')), (ttds, timestamp_set, TTextSeq('{BBB@2019-09-02}')), @@ -1127,7 +1130,7 @@ def test_at_min(self, temporal, expected): (ttds, period_set, None), (ttds, 'AAA', TTextSeq('{BBB@2019-09-02}')), (ttds, 'BBB', TTextSeq('{AAA@2019-09-01}')), - # (ttds, [AAA,BBB], TTextSeq('{BBB@2019-09-02}')), + # (ttds, ['AAA', 'BBB'], TTextSeq('{BBB@2019-09-02}')), (tts, timestamp, TTextSeqSet('{(AAA@2019-09-01, BBB@2019-09-02]}')), (tts, timestamp_set, TTextSeqSet('{(AAA@2019-09-01, BBB@2019-09-02]}')), @@ -1135,7 +1138,7 @@ def test_at_min(self, temporal, expected): (tts, period_set, None), (tts, 'AAA', TTextSeqSet('{[BBB@2019-09-02]}')), (tts, 'BBB', TTextSeqSet('{[AAA@2019-09-01, AAA@2019-09-02)}')), - # (tts, [AAA,BBB], TTextSeq('[BBB@2019-09-02]')), + # (tts, ['AAA', 'BBB'], TTextSeq('[BBB@2019-09-02]')), (ttss, timestamp, TTextSeqSet('{(AAA@2019-09-01, BBB@2019-09-02],[AAA@2019-09-03, AAA@2019-09-05]}')), @@ -1144,8 +1147,8 @@ def test_at_min(self, temporal, expected): (ttss, period, TTextSeqSet('{[AAA@2019-09-03, AAA@2019-09-05]}')), (ttss, period_set, None), (ttss, 'AAA', TTextSeqSet('{[BBB@2019-09-02]}')), - (ttss, 'BBB', TTextSeqSet('{[AAA@2019-09-01, AAA@2019-09-02),[AAA@2019-09-03, AAA@2019-09-05]}')) - # (ttss, [AAA,BBB], TTextSeqSet('{[BBB@2019-09-02]}')) + (ttss, 'BBB', TTextSeqSet('{[AAA@2019-09-01, AAA@2019-09-02),[AAA@2019-09-03, AAA@2019-09-05]}')), + # (ttss, ['AAA', 'BBB'], TTextSeqSet('{[BBB@2019-09-02]}')), ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', 'Instant-PeriodSet', 'Instant-AAA', 'Instant-BBB', # 'Instant-[AAA,BBB]', diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 0becca16..4a257ce6 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -131,7 +131,6 @@ 'spanset_copy', 'spanset_make', 'spanset_make_exp', - 'spanset_make_free', 'textset_make', 'timestampset_make', 'bigint_to_bigintset', diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index b17455a3..62ba20b8 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -857,7 +857,6 @@ extern Span *span_copy(const Span *s); extern SpanSet *spanset_copy(const SpanSet *ps); extern SpanSet *spanset_make(Span *spans, int count, bool normalize); extern SpanSet *spanset_make_exp(Span *spans, int count, int maxcount, bool normalize, bool ordered); -extern SpanSet *spanset_make_free(Span *spans, int count, bool normalize); extern Set *textset_make(const text **values, int count); extern Set *timestampset_make(const TimestampTz *values, int count); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index dbf93e94..ec08ee46 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -863,12 +863,6 @@ def spanset_make_exp(spans: 'Span *', count: int, maxcount: int, normalize: bool return result if result != _ffi.NULL else None -def spanset_make_free(spans: 'Span *', count: int, normalize: bool) -> 'SpanSet *': - spans_converted = _ffi.cast('Span *', spans) - result = _lib.spanset_make_free(spans_converted, count, normalize) - return result if result != _ffi.NULL else None - - def textset_make(values: 'List[const text]') -> 'Set *': values_converted = [_ffi.cast('const text *', x) for x in values] result = _lib.textset_make(values_converted, len(values)) From 92de4243d63f4a222a56fc38b30e8fa8706591f0 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 24 Aug 2023 17:04:01 +0200 Subject: [PATCH 010/101] Fix textset_make function --- pymeos_cffi/pymeos_cffi/__init__.py | 19 ++-- .../builder/build_pymeos_functions.py | 2 +- .../build_pymeos_functions_modifiers.py | 5 + pymeos_cffi/pymeos_cffi/builder/meos.h | 22 +++-- pymeos_cffi/pymeos_cffi/functions.py | 94 ++++++++++++++----- pymeos_cffi/test.py | 31 ------ 6 files changed, 107 insertions(+), 66 deletions(-) delete mode 100644 pymeos_cffi/test.py diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 4a257ce6..0d0b22fd 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -57,8 +57,6 @@ 'pg_interval_to_char', 'pg_to_timestamp', 'pg_to_date', - 'geography_from_hexewkb', - 'geometry_from_hexewkb', 'gserialized_as_ewkb', 'gserialized_as_ewkt', 'gserialized_as_geojson', @@ -131,6 +129,7 @@ 'spanset_copy', 'spanset_make', 'spanset_make_exp', + 'spanset_make_free', 'textset_make', 'timestampset_make', 'bigint_to_bigintset', @@ -833,14 +832,18 @@ 'temporal_ne', 'teq_bool_tbool', 'teq_float_tfloat', + 'teq_geo_tpoint', 'teq_int_tint', - 'teq_point_tpoint', + 'teq_point_tgeogpoint', + 'teq_point_tgeompoint', 'teq_tbool_bool', 'teq_temporal_temporal', 'teq_text_ttext', 'teq_tfloat_float', - 'teq_tpoint_point', + 'teq_tgeogpoint_point', + 'teq_tgeompoint_point', 'teq_tint_int', + 'teq_tpoint_geo', 'teq_ttext_text', 'tge_float_tfloat', 'tge_int_tint', @@ -872,14 +875,18 @@ 'tlt_ttext_text', 'tne_bool_tbool', 'tne_float_tfloat', + 'tne_geo_tpoint', 'tne_int_tint', - 'tne_point_tpoint', + 'tne_point_tgeogpoint', + 'tne_point_tgeompoint', 'tne_tbool_bool', 'tne_temporal_temporal', 'tne_text_ttext', 'tne_tfloat_float', - 'tne_tpoint_point', + 'tne_tgeogpoint_point', + 'tne_tgeompoint_point', 'tne_tint_int', + 'tne_tpoint_geo', 'tne_ttext_text', 'bearing_point_point', 'bearing_tpoint_point', diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index ccb88027..da55e0a7 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -182,7 +182,7 @@ def as_tsequenceset(temporal: 'Temporal *') -> 'TSequenceSet *': 'intset_make': array_parameter_modifier('values', 'count'), 'bigintset_make': array_parameter_modifier('values', 'count'), 'floatset_make': array_parameter_modifier('values', 'count'), - 'textset_make': array_parameter_modifier('values', 'count'), + 'textset_make': textset_make_modifier, 'geoset_make': array_length_remover_modifier('values', 'count'), } diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py index 211e8e1d..35156e15 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py @@ -26,6 +26,11 @@ def custom_array_modifier(function: str) -> str: return custom_array_modifier +def textset_make_modifier(function: str) -> str: + function = array_parameter_modifier('values', 'count')(function) + return function.replace("_ffi.cast('const text *', x)", "cstring2text(x)").replace("'List[const text]'", 'List[str]') + + def cstring2text_modifier(_: str) -> str: return """def cstring2text(cstring: str) -> 'text *': cstring_converted = cstring.encode('utf-8') diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 62ba20b8..709baf7a 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -482,6 +482,8 @@ extern LWPOINT *lwpoint_make(int32_t srid, int hasz, int hasm, const POINT4D *p) extern LWGEOM *lwgeom_from_gserialized(const GSERIALIZED *g); extern GSERIALIZED *gserialized_from_lwgeom(LWGEOM *geom, size_t *size); + + extern int32_t lwgeom_get_srid(const LWGEOM *geom); extern double lwpoint_get_x(const LWPOINT *point); @@ -771,8 +773,6 @@ extern DateADT pg_to_date(text *date_txt, text *fmt); * Functions for input/output and manipulation of PostGIS types *****************************************************************************/ -extern GSERIALIZED *geography_from_hexewkb(const char *wkt); -extern GSERIALIZED *geometry_from_hexewkb(const char *wkt); extern bytea *gserialized_as_ewkb(const GSERIALIZED *geom, char *type); extern char *gserialized_as_ewkt(const GSERIALIZED *geom, int precision); extern char *gserialized_as_geojson(const GSERIALIZED *geom, int option, int precision, char *srs); @@ -780,6 +780,7 @@ extern char *gserialized_as_hexewkb(const GSERIALIZED *geom, const char *type); extern char *gserialized_as_text(const GSERIALIZED *geom, int precision); extern GSERIALIZED *gserialized_from_ewkb(const bytea *bytea_wkb, int32 srid); extern GSERIALIZED *gserialized_from_geojson(const char *geojson); + extern GSERIALIZED *geometry_from_text(char *wkt, int srid); extern GSERIALIZED *geography_from_text(char *wkt, int srid); extern GSERIALIZED *pgis_geography_in(char *input, int32 geom_typmod); @@ -857,6 +858,7 @@ extern Span *span_copy(const Span *s); extern SpanSet *spanset_copy(const SpanSet *ps); extern SpanSet *spanset_make(Span *spans, int count, bool normalize); extern SpanSet *spanset_make_exp(Span *spans, int count, int maxcount, bool normalize, bool ordered); +extern SpanSet *spanset_make_free(Span *spans, int count, bool normalize); extern Set *textset_make(const text **values, int count); extern Set *timestampset_make(const TimestampTz *values, int count); @@ -1726,14 +1728,18 @@ extern bool temporal_lt(const Temporal *temp1, const Temporal *temp2); extern bool temporal_ne(const Temporal *temp1, const Temporal *temp2); extern Temporal *teq_bool_tbool(bool b, const Temporal *temp); extern Temporal *teq_float_tfloat(double d, const Temporal *temp); +extern Temporal *teq_geo_tpoint(const GSERIALIZED *geo, const Temporal *tpoint); extern Temporal *teq_int_tint(int i, const Temporal *temp); -extern Temporal *teq_point_tpoint(const GSERIALIZED *gs, const Temporal *temp); +extern Temporal *teq_point_tgeogpoint(const GSERIALIZED *gs, const Temporal *temp); +extern Temporal *teq_point_tgeompoint(const GSERIALIZED *gs, const Temporal *temp); extern Temporal *teq_tbool_bool(const Temporal *temp, bool b); extern Temporal *teq_temporal_temporal(const Temporal *temp1, const Temporal *temp2); extern Temporal *teq_text_ttext(const text *txt, const Temporal *temp); extern Temporal *teq_tfloat_float(const Temporal *temp, double d); -extern Temporal *teq_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); +extern Temporal *teq_tgeogpoint_point(const Temporal *temp, const GSERIALIZED *gs); +extern Temporal *teq_tgeompoint_point(const Temporal *temp, const GSERIALIZED *gs); extern Temporal *teq_tint_int(const Temporal *temp, int i); +extern Temporal *teq_tpoint_geo(const Temporal *tpoint, const GSERIALIZED *geo); extern Temporal *teq_ttext_text(const Temporal *temp, const text *txt); extern Temporal *tge_float_tfloat(double d, const Temporal *temp); extern Temporal *tge_int_tint(int i, const Temporal *temp); @@ -1765,14 +1771,18 @@ extern Temporal *tlt_tint_int(const Temporal *temp, int i); extern Temporal *tlt_ttext_text(const Temporal *temp, const text *txt); extern Temporal *tne_bool_tbool(bool b, const Temporal *temp); extern Temporal *tne_float_tfloat(double d, const Temporal *temp); +extern Temporal *tne_geo_tpoint(const GSERIALIZED *geo, const Temporal *tpoint); extern Temporal *tne_int_tint(int i, const Temporal *temp); -extern Temporal *tne_point_tpoint(const GSERIALIZED *gs, const Temporal *temp); +extern Temporal *tne_point_tgeogpoint(const GSERIALIZED *gs, const Temporal *temp); +extern Temporal *tne_point_tgeompoint(const GSERIALIZED *gs, const Temporal *temp); extern Temporal *tne_tbool_bool(const Temporal *temp, bool b); extern Temporal *tne_temporal_temporal(const Temporal *temp1, const Temporal *temp2); extern Temporal *tne_text_ttext(const text *txt, const Temporal *temp); extern Temporal *tne_tfloat_float(const Temporal *temp, double d); -extern Temporal *tne_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); +extern Temporal *tne_tgeogpoint_point(const Temporal *temp, const GSERIALIZED *gs); +extern Temporal *tne_tgeompoint_point(const Temporal *temp, const GSERIALIZED *gs); extern Temporal *tne_tint_int(const Temporal *temp, int i); +extern Temporal *tne_tpoint_geo(const Temporal *tpoint, const GSERIALIZED *geo); extern Temporal *tne_ttext_text(const Temporal *temp, const text *txt); /***************************************************************************** diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index ec08ee46..6f055e2c 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -370,18 +370,6 @@ def pg_to_date(date_txt: str, fmt: str) -> 'DateADT': return result if result != _ffi.NULL else None -def geography_from_hexewkb(wkt: str) -> 'GSERIALIZED *': - wkt_converted = wkt.encode('utf-8') - result = _lib.geography_from_hexewkb(wkt_converted) - return result if result != _ffi.NULL else None - - -def geometry_from_hexewkb(wkt: str) -> 'GSERIALIZED *': - wkt_converted = wkt.encode('utf-8') - result = _lib.geometry_from_hexewkb(wkt_converted) - return result if result != _ffi.NULL else None - - def gserialized_as_ewkb(geom: 'const GSERIALIZED *', type: str) -> 'bytea *': geom_converted = _ffi.cast('const GSERIALIZED *', geom) type_converted = type.encode('utf-8') @@ -863,8 +851,14 @@ def spanset_make_exp(spans: 'Span *', count: int, maxcount: int, normalize: bool return result if result != _ffi.NULL else None -def textset_make(values: 'List[const text]') -> 'Set *': - values_converted = [_ffi.cast('const text *', x) for x in values] +def spanset_make_free(spans: 'Span *', count: int, normalize: bool) -> 'SpanSet *': + spans_converted = _ffi.cast('Span *', spans) + result = _lib.spanset_make_free(spans_converted, count, normalize) + return result if result != _ffi.NULL else None + + +def textset_make(values: List[str]) -> 'Set *': + values_converted = [cstring2text(x) for x in values] result = _lib.textset_make(values_converted, len(values)) return result if result != _ffi.NULL else None @@ -5642,16 +5636,30 @@ def teq_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': return result if result != _ffi.NULL else None +def teq_geo_tpoint(geo: 'const GSERIALIZED *', tpoint: 'const Temporal *') -> 'Temporal *': + geo_converted = _ffi.cast('const GSERIALIZED *', geo) + tpoint_converted = _ffi.cast('const Temporal *', tpoint) + result = _lib.teq_geo_tpoint(geo_converted, tpoint_converted) + return result if result != _ffi.NULL else None + + def teq_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) result = _lib.teq_int_tint(i, temp_converted) return result if result != _ffi.NULL else None -def teq_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': +def teq_point_tgeogpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_point_tpoint(gs_converted, temp_converted) + result = _lib.teq_point_tgeogpoint(gs_converted, temp_converted) + return result if result != _ffi.NULL else None + + +def teq_point_tgeompoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.teq_point_tgeompoint(gs_converted, temp_converted) return result if result != _ffi.NULL else None @@ -5681,10 +5689,17 @@ def teq_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': return result if result != _ffi.NULL else None -def teq_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': +def teq_tgeogpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.teq_tgeogpoint_point(temp_converted, gs_converted) + return result if result != _ffi.NULL else None + + +def teq_tgeompoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.teq_tpoint_point(temp_converted, gs_converted) + result = _lib.teq_tgeompoint_point(temp_converted, gs_converted) return result if result != _ffi.NULL else None @@ -5694,6 +5709,13 @@ def teq_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': return result if result != _ffi.NULL else None +def teq_tpoint_geo(tpoint: 'const Temporal *', geo: 'const GSERIALIZED *') -> 'Temporal *': + tpoint_converted = _ffi.cast('const Temporal *', tpoint) + geo_converted = _ffi.cast('const GSERIALIZED *', geo) + result = _lib.teq_tpoint_geo(tpoint_converted, geo_converted) + return result if result != _ffi.NULL else None + + def teq_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) txt_converted = cstring2text(txt) @@ -5893,16 +5915,30 @@ def tne_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': return result if result != _ffi.NULL else None +def tne_geo_tpoint(geo: 'const GSERIALIZED *', tpoint: 'const Temporal *') -> 'Temporal *': + geo_converted = _ffi.cast('const GSERIALIZED *', geo) + tpoint_converted = _ffi.cast('const Temporal *', tpoint) + result = _lib.tne_geo_tpoint(geo_converted, tpoint_converted) + return result if result != _ffi.NULL else None + + def tne_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) result = _lib.tne_int_tint(i, temp_converted) return result if result != _ffi.NULL else None -def tne_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': +def tne_point_tgeogpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tne_point_tgeogpoint(gs_converted, temp_converted) + return result if result != _ffi.NULL else None + + +def tne_point_tgeompoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_point_tpoint(gs_converted, temp_converted) + result = _lib.tne_point_tgeompoint(gs_converted, temp_converted) return result if result != _ffi.NULL else None @@ -5932,10 +5968,17 @@ def tne_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': return result if result != _ffi.NULL else None -def tne_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': +def tne_tgeogpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.tne_tpoint_point(temp_converted, gs_converted) + result = _lib.tne_tgeogpoint_point(temp_converted, gs_converted) + return result if result != _ffi.NULL else None + + +def tne_tgeompoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.tne_tgeompoint_point(temp_converted, gs_converted) return result if result != _ffi.NULL else None @@ -5945,6 +5988,13 @@ def tne_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': return result if result != _ffi.NULL else None +def tne_tpoint_geo(tpoint: 'const Temporal *', geo: 'const GSERIALIZED *') -> 'Temporal *': + tpoint_converted = _ffi.cast('const Temporal *', tpoint) + geo_converted = _ffi.cast('const GSERIALIZED *', geo) + result = _lib.tne_tpoint_geo(tpoint_converted, geo_converted) + return result if result != _ffi.NULL else None + + def tne_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) txt_converted = cstring2text(txt) diff --git a/pymeos_cffi/test.py b/pymeos_cffi/test.py deleted file mode 100644 index 764d8093..00000000 --- a/pymeos_cffi/test.py +++ /dev/null @@ -1,31 +0,0 @@ -from time import time - -from spans import intrange, floatrange - -from pymeos_cffi import tgeompoint_in, meos_initialize, tpoint_out, tintinst_make, tint_in, temporal_merge, \ - temporal_merge_array, intrange_to_intspan, span_out, floatrange_to_floatspan - -meos_initialize('UTC') - -t1 = tint_in('1@2000-01-01') -t2 = tint_in('2@2000-01-02') -ta = [t1, t2] -times = 1000 -s = time() -for _ in range(times): - t3 = temporal_merge(t1, t2) -m = time() -for _ in range(times): - t3 = temporal_merge_array(ta, 2) -e = time() - -print(f'Merge time: {m - s}s') -print(f'Merge Array time: {e - m}s') - -ir = intrange(1, 4, lower_inc=False, upper_inc=True) -print(ir, ir.lower_inc, ir.upper_inc) -print(span_out(intrange_to_intspan(ir), -1)) - -fr = floatrange(1.0, 4.0, lower_inc=False, upper_inc=True) -print(fr, fr.lower_inc, fr.upper_inc) -print(span_out(floatrange_to_floatspan(fr), -1)) From 90245b2ce96ce37065ae541aefde1eeaf2aebc13 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 4 Sep 2023 18:11:30 +0200 Subject: [PATCH 011/101] Update to last MEOS changes --- pymeos/pymeos/main/tpoint.py | 80 ++++++++++++++-------------- pymeos/pyproject.toml | 2 +- pymeos/tests/main/tfloat_test.py | 2 +- pymeos/tests/main/tgeogpoint_test.py | 2 +- pymeos/tests/main/tgeompoint_test.py | 2 +- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index dd2bcac6..59e6678e 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -1209,10 +1209,10 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], base: Tempo A new :class:`TGeomPoint` object. MEOS Functions: - tgeompoint_from_base_temp + tpoint_from_base_temp """ gs = geometry_to_gserialized(value) - result = tgeompoint_from_base_temp(gs, base._inner) + result = tpoint_from_base_temp(gs, base._inner) return Temporal._factory(result) @staticmethod @@ -1245,18 +1245,18 @@ def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Time, A new :class:`TGeomPoint` object. MEOS Functions: - tgeompointinst_make, tgeompointseq_from_base_timestampset, - tgeompointseq_from_base_period, tgeompointseqset_from_base_periodset + tpointinst_make, tpointseq_from_base_timestampset, + tpointseq_from_base_period, tpointseqset_from_base_periodset """ gs = geometry_to_gserialized(value) if isinstance(base, datetime): - return TGeomPointInst(_inner=tgeompointinst_make(gs, datetime_to_timestamptz(base))) + return TGeomPointInst(_inner=tpointinst_make(gs, datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): - return TGeomPointSeq(_inner=tgeompointseq_from_base_timestampset(gs, base._inner)) + return TGeomPointSeq(_inner=tpointseq_from_base_timestampset(gs, base._inner)) elif isinstance(base, Period): - return TGeomPointSeq(_inner=tgeompointseq_from_base_period(gs, base._inner, interpolation)) + return TGeomPointSeq(_inner=tpointseq_from_base_period(gs, base._inner, interpolation)) elif isinstance(base, PeriodSet): - return TGeomPointSeqSet(_inner=tgeompointseqset_from_base_periodset(gs, base._inner, interpolation)) + return TGeomPointSeqSet(_inner=tpointseqset_from_base_periodset(gs, base._inner, interpolation)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Conversions ---------------------------------- @@ -1313,10 +1313,10 @@ def always_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: True if `self` is always equal to `value`, False otherwise. MEOS Functions: - tgeompoint_always_eq + tpoint_always_eq """ gs = geometry_to_gserialized(value) - return tgeompoint_always_eq(self._inner, gs) + return tpoint_always_eq(self._inner, gs) def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: """ @@ -1329,10 +1329,10 @@ def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool True if `self` is always different to `value`, False otherwise. MEOS Functions: - tgeompoint_ever_eq + tpoint_ever_eq """ gs = geometry_to_gserialized(value) - return not tgeompoint_ever_eq(self._inner, gs) + return not tpoint_ever_eq(self._inner, gs) def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: """ @@ -1345,10 +1345,10 @@ def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: True if `self` is ever equal to `value`, False otherwise. MEOS Functions: - tgeompoint_ever_eq + tpoint_ever_eq """ gs = geometry_to_gserialized(value) - return tgeompoint_ever_eq(self._inner, gs) + return tpoint_ever_eq(self._inner, gs) def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: """ @@ -1361,10 +1361,10 @@ def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: True if `self` is ever different to `value`, False otherwise. MEOS Functions: - tgeompoint_always_eq + tpoint_always_eq """ gs = geometry_to_gserialized(value) - return not tgeompoint_always_eq(self._inner, gs) + return not tpoint_always_eq(self._inner, gs) def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: """ @@ -1377,10 +1377,10 @@ def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: True if `self` is never equal to `value`, False otherwise. MEOS Functions: - tgeompoint_ever_eq + tpoint_ever_eq """ gs = geometry_to_gserialized(value) - return not tgeompoint_ever_eq(self._inner, gs) + return not tpoint_ever_eq(self._inner, gs) def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: """ @@ -1393,10 +1393,10 @@ def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: True if `self` is never different to `value`, False otherwise. MEOS Functions: - tgeompoint_always_eq + tpoint_always_eq """ gs = geometry_to_gserialized(value) - return tgeompoint_always_eq(self._inner, gs) + return tpoint_always_eq(self._inner, gs) # ------------------------- Temporal Comparisons -------------------------- def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: @@ -1487,10 +1487,10 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], base: Tempo A new :class:`TGeogPoint` object. MEOS Functions: - tgeogpoint_from_base_temp + tpoint_from_base_temp """ gs = geography_to_gserialized(value) - result = tgeogpoint_from_base_temp(gs, base._inner) + result = tpoint_from_base_temp(gs, base._inner) return Temporal._factory(result) @staticmethod @@ -1523,18 +1523,18 @@ def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Time, A new :class:`TGeogPoint` object. MEOS Functions: - tgeogpointinst_make, tgeogpointseq_from_base_timestampset, - tgeogpointseq_from_base_period, tgeogpointseqset_from_base_periodset + tpointinst_make, tpointseq_from_base_timestampset, + tpointseq_from_base_period, tpointseqset_from_base_periodset """ gs = geography_to_gserialized(value) if isinstance(base, datetime): - return TGeogPointInst(_inner=tgeogpointinst_make(gs, datetime_to_timestamptz(base))) + return TGeogPointInst(_inner=tpointinst_make(gs, datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): - return TGeogPointSeq(_inner=tgeogpointseq_from_base_timestampset(gs, base._inner)) + return TGeogPointSeq(_inner=tpointseq_from_base_timestampset(gs, base._inner)) elif isinstance(base, Period): - return TGeogPointSeq(_inner=tgeogpointseq_from_base_period(gs, base._inner, interpolation)) + return TGeogPointSeq(_inner=tpointseq_from_base_period(gs, base._inner, interpolation)) elif isinstance(base, PeriodSet): - return TGeogPointSeqSet(_inner=tgeogpointseqset_from_base_periodset(gs, base._inner, interpolation)) + return TGeogPointSeqSet(_inner=tpointseqset_from_base_periodset(gs, base._inner, interpolation)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Conversions ---------------------------------- @@ -1563,10 +1563,10 @@ def always_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: True if `self` is always equal to `value`, False otherwise. MEOS Functions: - tgeogpoint_always_eq + tpoint_always_eq """ gs = geography_to_gserialized(value) - return tgeogpoint_always_eq(self._inner, gs) + return tpoint_always_eq(self._inner, gs) def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: """ @@ -1579,10 +1579,10 @@ def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool True if `self` is always different to `value`, False otherwise. MEOS Functions: - tgeogpoint_ever_eq + tpoint_ever_eq """ gs = geography_to_gserialized(value) - return not tgeogpoint_ever_eq(self._inner, gs) + return not tpoint_ever_eq(self._inner, gs) def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: """ @@ -1595,10 +1595,10 @@ def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: True if `self` is ever equal to `value`, False otherwise. MEOS Functions: - tgeogpoint_ever_eq + tpoint_ever_eq """ gs = geography_to_gserialized(value) - return tgeogpoint_ever_eq(self._inner, gs) + return tpoint_ever_eq(self._inner, gs) def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: """ @@ -1611,10 +1611,10 @@ def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: True if `self` is ever different to `value`, False otherwise. MEOS Functions: - tgeogpoint_always_eq + tpoint_always_eq """ gs = geography_to_gserialized(value) - return not tgeogpoint_always_eq(self._inner, gs) + return not tpoint_always_eq(self._inner, gs) def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: """ @@ -1627,10 +1627,10 @@ def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: True if `self` is never equal to `value`, False otherwise. MEOS Functions: - tgeogpoint_ever_eq + tpoint_ever_eq """ gs = geography_to_gserialized(value) - return not tgeogpoint_ever_eq(self._inner, gs) + return not tpoint_ever_eq(self._inner, gs) def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: """ @@ -1643,10 +1643,10 @@ def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: True if `self` is never different to `value`, False otherwise. MEOS Functions: - tgeogpoint_always_eq + tpoint_always_eq """ gs = geography_to_gserialized(value) - return tgeogpoint_always_eq(self._inner, gs) + return tpoint_always_eq(self._inner, gs) # ------------------------- Temporal Comparisons -------------------------- def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: diff --git a/pymeos/pyproject.toml b/pymeos/pyproject.toml index c815a1d7..83ebd4d3 100644 --- a/pymeos/pyproject.toml +++ b/pymeos/pyproject.toml @@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta' [project] name = 'pymeos' -version = '1.1.3-alpha-2' +version = '1.1.3-alpha.4' authors = [ { name = 'Victor Divi', email = 'vdiviloper@gmail.com' }, { name = 'Zhicheng Luo', email = 'zhicheng.luo@ulb.be' }, diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index be456dce..6b153a40 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -1074,7 +1074,7 @@ def test_temporal_sample(self, tfloat, delta, expected): 'Sequence Set days', 'Sequence Set hours'] ) def test_stops(self, tfloat, delta, expected): - assert tfloat.stops(0.0, delta) == expected + assert tfloat.stops(0.1, delta) == expected @pytest.mark.parametrize( 'temporal, expected', diff --git a/pymeos/tests/main/tgeogpoint_test.py b/pymeos/tests/main/tgeogpoint_test.py index f160a6ab..f1fe04cb 100644 --- a/pymeos/tests/main/tgeogpoint_test.py +++ b/pymeos/tests/main/tgeogpoint_test.py @@ -1229,7 +1229,7 @@ def test_temporal_sample(self, tpoint, delta, expected): 'Sequence Set days', 'Sequence Set hours'] ) def test_stops(self, tpoint, delta, expected): - assert tpoint.stops(0.0, delta) == expected + assert tpoint.stops(0.1, delta) == expected @pytest.mark.parametrize( 'temporal, expected', diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index 55d0a1ca..21ce1590 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -1285,7 +1285,7 @@ def test_temporal_sample(self, tpoint, delta, expected): 'Sequence Set days', 'Sequence Set hours'] ) def test_stops(self, tpoint, delta, expected): - assert tpoint.stops(0.0, delta) == expected + assert tpoint.stops(0.1, delta) == expected @pytest.mark.parametrize( 'temporal, expected', From 0bf49c6919539adf8ace5c536be2133b5e1b0584 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 4 Sep 2023 18:15:07 +0200 Subject: [PATCH 012/101] Refactor expand method in stbox.py Removed STBox as a valid input type in the expand method within the stbox.py file. The same output should be obtained with the union method. --- pymeos/pymeos/boxes/stbox.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index b75855ba..c346eadd 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -540,7 +540,7 @@ def set_srid(self, value: int) -> STBox: return STBox(_inner=stbox_set_srid(self._inner, value)) # ------------------------- Transformations ------------------------------- - def expand(self, other: Union[int, float, timedelta, STBox]) -> STBox: + def expand(self, other: Union[int, float, timedelta]) -> STBox: """ Expands ``self`` with `other`. If `other` is a :class:`int` or a :class:`float`, the result is equal to ``self`` but with the spatial dimensions @@ -560,9 +560,6 @@ def expand(self, other: Union[int, float, timedelta, STBox]) -> STBox: result = stbox_expand_space(self._inner, float(other)) elif isinstance(other, timedelta): result = stbox_expand_time(self._inner, timedelta_to_interval(other)) - elif isinstance(other, STBox): - result = stbox_copy(self._inner) - stbox_expand(other._inner, result) else: raise TypeError(f'Operation not supported with type {other.__class__}') return STBox(_inner=result) From f04ff8578bc6b8f7a2bd4a76b934a92726d2be1c Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 4 Sep 2023 18:18:25 +0200 Subject: [PATCH 013/101] Refactor expand and union methods in TBox class The expand function now only accepts numeric and temporal types as arguments, thus excluding TBox type. This simplification attempts to make the code cleaner and easier to manage. Refactored the union method to merge boxes by default even if they do not intersect. Added an optional parameter 'strict' that when set true, preserves the old behaviour, i.e., fails if the boxes do not intersect. Fixed return type in round method Corrected the round method's return type. Previously it was wrongly marked as STBox, it's now correctly marked as TBox. --- pymeos/pymeos/boxes/tbox.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index a7b8ddff..dcc5a831 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -429,11 +429,10 @@ def tmax_inc(self) -> bool: return tbox_tmax_inc(self._inner) # ------------------------- Transformation -------------------------------- - def expand(self, other: Union[TBox, int, float, timedelta]) -> TBox: + def expand(self, other: Union[int, float, timedelta]) -> TBox: """ Returns the result of expanding ``self`` with the ``other``. Depending on the type of ``other``, the expansion - will be of the numeric dimension (:class:`float`), temporal (:class:`~datetime.timedelta`) or both - (:class:`TBox`). + will be of the numeric dimension (:class:`float`) or temporal (:class:`~datetime.timedelta`). Args: other: object used to expand ``self`` @@ -442,12 +441,9 @@ def expand(self, other: Union[TBox, int, float, timedelta]) -> TBox: A new :class:`TBox` instance. MEOS Functions: - tbox_copy, tbox_expand, tbox_expand_value, tbox_expand_time + tbox_expand_value, tbox_expand_time """ - if isinstance(other, TBox): - result = tbox_copy(self._inner) - tbox_expand(other._inner, result) - elif isinstance(other, int) or isinstance(other, float): + if isinstance(other, int) or isinstance(other, float): result = tbox_expand_value(self._inner, float(other)) elif isinstance(other, timedelta): result = tbox_expand_time(self._inner, timedelta_to_interval(other)) @@ -524,7 +520,7 @@ def shift_tscale(self, shift: Optional[timedelta] = None, ) return TBox(_inner=new_inner) - def round(self, maxdd : int = 0) -> STBox: + def round(self, maxdd : int = 0) -> TBox: """ Returns `self` rounded to the given number of decimal digits. @@ -544,10 +540,11 @@ def round(self, maxdd : int = 0) -> STBox: # ------------------------- Set Operations -------------------------------- def union(self, other: TBox, strict: Optional[bool] = True) -> TBox: """ - Returns the union of `self` with `other`. Fails if the union is not contiguous. + Returns the union of `self` with `other`. Args: other: temporal object to merge with + strict: Whether to fail if the boxes do not intersect. Returns: A :class:`TBox` instance. @@ -570,7 +567,7 @@ def __add__(self, other): MEOS Functions: union_tbox_tbox """ - return self.union(other) + return self.union(other, False) # TODO: Check returning None for empty intersection is the desired behaviour def intersection(self, other: TBox) -> Optional[TBox]: From b0970eeda467d5b2fac43b7883ab900cbe80c790 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 4 Sep 2023 18:19:21 +0200 Subject: [PATCH 014/101] Add Box class to tnumber imports. --- pymeos/pymeos/main/tnumber.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymeos/pymeos/main/tnumber.py b/pymeos/pymeos/main/tnumber.py index 3464f5f0..0246f903 100644 --- a/pymeos/pymeos/main/tnumber.py +++ b/pymeos/pymeos/main/tnumber.py @@ -9,7 +9,7 @@ from ..temporal import Temporal if TYPE_CHECKING: - from ..boxes import TBox + from ..boxes import Box, TBox from ..time import Time from .tint import TInt from .tfloat import TFloat From fc7885007762ec0a5fce84484be70d2c7a12ad4b Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 4 Sep 2023 18:21:00 +0200 Subject: [PATCH 015/101] Add TSequence and TBox imports to temporal.py --- pymeos/pymeos/temporal/temporal.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index 42e0b9a0..fbb1e3af 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -11,8 +11,9 @@ if TYPE_CHECKING: from .tinstant import TInstant + from .tsequence import TSequence from ..main import TBool - from ..boxes import Box + from ..boxes import Box, TBox TBase = TypeVar('TBase') TG = TypeVar('TG', bound='Temporal[Any]') TI = TypeVar('TI', bound='TInstant[Any]') From eb537defeb900d1a0f2030e5bca40d8db98e3dfd Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Tue, 5 Sep 2023 16:45:04 +0200 Subject: [PATCH 016/101] Synchronize with MEOS API --- pymeos/pymeos/main/tpoint.py | 26 +- pymeos/pymeos/temporal/temporal.py | 8 +- pymeos/pymeos/time/timestampset.py | 4 +- pymeos/tests/boxes/stbox_test.py | 10 +- pymeos/tests/main/tbool_test.py | 25 +- pymeos/tests/main/tfloat_test.py | 22 +- pymeos/tests/main/tgeogpoint_test.py | 48 +-- pymeos/tests/main/tgeompoint_test.py | 22 +- pymeos/tests/main/tint_test.py | 22 +- pymeos/tests/main/ttext_test.py | 22 +- pymeos_cffi/pymeos_cffi/__init__.py | 57 ++-- pymeos_cffi/pymeos_cffi/builder/meos.h | 81 +++-- pymeos_cffi/pymeos_cffi/functions.py | 442 +++++++++++++++---------- 13 files changed, 469 insertions(+), 320 deletions(-) diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 59e6678e..b43ddcab 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -227,9 +227,9 @@ def x(self) -> TFloat: A :class:`TFloat` with the x coordinate of the temporal point. MEOS Functions: - tpoint_get_coord + tpoint_get_x """ - result = tpoint_get_coord(self._inner, 0) + result = tpoint_get_x(self._inner) return Temporal._factory(result) def y(self) -> TFloat: @@ -240,9 +240,9 @@ def y(self) -> TFloat: A :class:`TFloat` with the y coordinate of the temporal point. MEOS Functions: - tpoint_get_coord + tpoint_get_y """ - result = tpoint_get_coord(self._inner, 1) + result = tpoint_get_y(self._inner) return Temporal._factory(result) def z(self) -> TFloat: @@ -253,9 +253,9 @@ def z(self) -> TFloat: A :class:`TFloat` with the z coordinate of the temporal point. MEOS Functions: - tpoint_get_coord + tpoint_get_z """ - result = tpoint_get_coord(self._inner, 2) + result = tpoint_get_z(self._inner) return Temporal._factory(result) def has_z(self) -> bool: @@ -935,14 +935,14 @@ def distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, STBox]) A new :class:`TFloat` indicating the temporal distance between the temporal point and `other`. MEOS Functions: - distance_tpoint_geo, distance_tpoint_tpoint + distance_tpoint_point, distance_tpoint_tpoint """ from ..boxes import STBox if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = distance_tpoint_geo(self._inner, gs) + result = distance_tpoint_point(self._inner, gs) elif isinstance(other, STBox): - result = distance_tpoint_geo(self._inner, stbox_to_geo(other._inner)) + result = distance_tpoint_point(self._inner, stbox_to_geo(other._inner)) elif isinstance(other, TPoint): result = distance_tpoint_tpoint(self._inner, other._inner) else: @@ -1268,9 +1268,9 @@ def to_geographic(self) -> TGeogPoint: A new :class:`TGeogPoint` object. MEOS Functions: - tgeompoint_tgeogpoint + tgeompoint_to_tgeogpoint """ - result = tgeompoint_tgeogpoint(self._inner, True) + result = tgeompoint_to_tgeogpoint(self._inner) return Temporal._factory(result) def to_shapely_geometry(self, precision: int = 15) -> shpb.BaseGeometry: @@ -1546,9 +1546,9 @@ def to_geometric(self) -> TGeomPoint: A new :class:`TGeomPoint` object. MEOS Functions: - tgeompoint_tgeogpoint + tgeogpoint_to_tgeompoint """ - result = tgeompoint_tgeogpoint(self._inner, False) + result = tgeogpoint_to_tgeompoint(self._inner) return Temporal._factory(result) # ------------------------- Ever and Always Comparisons ------------------- diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index fbb1e3af..54328586 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -592,24 +592,24 @@ def to_instant(self) -> TI: inst = temporal_to_tinstant(self._inner) return Temporal._factory(inst) - def to_sequence(self) -> TS: + def to_sequence(self, interpolation: TInterpolation) -> TS: """ Converts `self` into a :class:`TSequence`. MEOS Functions: temporal_to_sequence """ - seq = temporal_to_tsequence(self._inner) + seq = temporal_to_tsequence(self._inner, interpolation) return Temporal._factory(seq) - def to_sequenceset(self) -> TSS: + def to_sequenceset(self, interpolation: TInterpolation) -> TSS: """ Returns `self` as a :class:`TSequenceSet`. MEOS Functions: temporal_to_tsequenceset """ - ss = temporal_to_tsequenceset(self._inner) + ss = temporal_to_tsequenceset(self._inner, interpolation) return Temporal._factory(ss) def to_dataframe(self) -> DataFrame: diff --git a/pymeos/pymeos/time/timestampset.py b/pymeos/pymeos/time/timestampset.py index 3997035c..4e1a1dd0 100644 --- a/pymeos/pymeos/time/timestampset.py +++ b/pymeos/pymeos/time/timestampset.py @@ -101,9 +101,9 @@ def __str__(self): A new :class:`str` instance MEOS Functions: - set_out + timestampset_out """ - return set_out(self._inner, 15) + return timestampset_out(self._inner) def __repr__(self): """ diff --git a/pymeos/tests/boxes/stbox_test.py b/pymeos/tests/boxes/stbox_test.py index 5bd83410..0233d07c 100644 --- a/pymeos/tests/boxes/stbox_test.py +++ b/pymeos/tests/boxes/stbox_test.py @@ -487,25 +487,23 @@ def test_tmin_tmax_inc(self, stbox, expected): [ (stbx, 0), (stbz, 0), - (stbt, 0), (stbxt, 0), (stbzt, 0), (gstbx, 4326), (gstbz, 4326), - (gstbt, 4326), (gstbxt, 4326), (gstbzt, 4326) ], - ids=['STBox X', 'STBox Z', 'STBox T', 'STBox XT', 'STBox ZT', - 'Geodetic STBox X', 'Geodetic STBox Z', 'Geodetic STBox T', 'Geodetic STBox XT', 'Geodetic STBox ZT'] + ids=['STBox X', 'STBox Z', 'STBox XT', 'STBox ZT', + 'Geodetic STBox X', 'Geodetic STBox Z', 'Geodetic STBox XT', 'Geodetic STBox ZT'] ) def test_srid(self, stbox, expected): assert stbox.srid() == expected @pytest.mark.parametrize( 'stbox', - [stbx, stbz, stbt, stbxt, stbzt], - ids=['STBox X', 'STBox Z', 'STBox T', 'STBox XT', 'STBox ZT'] + [stbx, stbz, stbxt, stbzt], + ids=['STBox X', 'STBox Z', 'STBox XT', 'STBox ZT'] ) def test_set_srid(self, stbox): assert stbox.set_srid(5676).srid() == 5676 diff --git a/pymeos/tests/main/tbool_test.py b/pymeos/tests/main/tbool_test.py index dbff67da..38d39a1f 100644 --- a/pymeos/tests/main/tbool_test.py +++ b/pymeos/tests/main/tbool_test.py @@ -752,40 +752,43 @@ def test_to_instant(self, temporal, expected): assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TBoolInst('True@2019-09-01'), + (TBoolInst('True@2019-09-01'), TInterpolation.STEPWISE, TBoolSeq('[True@2019-09-01]')), - (TBoolSeq('{True@2019-09-01, False@2019-09-02}'), + (TBoolSeq('{True@2019-09-01, False@2019-09-02}'), TInterpolation.DISCRETE, TBoolSeq('{True@2019-09-01, False@2019-09-02}')), - (TBoolSeq('[True@2019-09-01, False@2019-09-02]'), + (TBoolSeq('[True@2019-09-01, False@2019-09-02]'), TInterpolation.STEPWISE, TBoolSeq('[True@2019-09-01, False@2019-09-02]')), - (TBoolSeqSet('{[True@2019-09-01, False@2019-09-02]}'), + (TBoolSeqSet('{[True@2019-09-01, False@2019-09-02]}'), TInterpolation.STEPWISE, TBoolSeq('[True@2019-09-01, False@2019-09-02]')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequence(self, temporal, expected): - temp = temporal.to_sequence() + def test_to_sequence(self, temporal, interpolation, expected): + temp = temporal.to_sequence(interpolation) assert isinstance(temp, TBoolSeq) assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TBoolInst('True@2019-09-01'), + (TBoolInst('True@2019-09-01'), TInterpolation.STEPWISE, TBoolSeqSet('{[True@2019-09-01]}')), (TBoolSeq('{True@2019-09-01, False@2019-09-02}'), + TInterpolation.STEPWISE, TBoolSeqSet('{[True@2019-09-01], [False@2019-09-02]}')), (TBoolSeq('[True@2019-09-01, False@2019-09-02]'), + TInterpolation.STEPWISE, TBoolSeqSet('{[True@2019-09-01, False@2019-09-02]}')), (TBoolSeqSet('{[True@2019-09-01, False@2019-09-02]}'), + TInterpolation.STEPWISE, TBoolSeqSet('{[True@2019-09-01, False@2019-09-02]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequenceset(self, temporal, expected): - temp = temporal.to_sequenceset() + def test_to_sequenceset(self, temporal, interpolation, expected): + temp = temporal.to_sequenceset(interpolation) assert isinstance(temp, TBoolSeqSet) assert temp == expected diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index 6b153a40..56bf76c2 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -876,40 +876,46 @@ def test_to_instant(self, temporal, expected): assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TFloatInst('1.5@2019-09-01'), + (TFloatInst('1.5@2019-09-01'), TInterpolation.LINEAR, TFloatSeq('[1.5@2019-09-01]')), (TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}'), + TInterpolation.DISCRETE, TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}')), (TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]'), + TInterpolation.LINEAR, TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]')), (TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}'), + TInterpolation.LINEAR, TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequence(self, temporal, expected): - temp = temporal.to_sequence() + def test_to_sequence(self, temporal, interpolation, expected): + temp = temporal.to_sequence(interpolation) assert isinstance(temp, TFloatSeq) assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TFloatInst('1.5@2019-09-01'), + (TFloatInst('1.5@2019-09-01'), TInterpolation.LINEAR, TFloatSeqSet('{[1.5@2019-09-01]}')), (TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}'), + TInterpolation.LINEAR, TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02]}')), (TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]'), + TInterpolation.LINEAR, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}')), (TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}'), + TInterpolation.LINEAR, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequenceset(self, temporal, expected): - temp = temporal.to_sequenceset() + def test_to_sequenceset(self, temporal, interpolation, expected): + temp = temporal.to_sequenceset(interpolation) assert isinstance(temp, TFloatSeqSet) assert temp == expected diff --git a/pymeos/tests/main/tgeogpoint_test.py b/pymeos/tests/main/tgeogpoint_test.py index f1fe04cb..2154b0aa 100644 --- a/pymeos/tests/main/tgeogpoint_test.py +++ b/pymeos/tests/main/tgeogpoint_test.py @@ -33,16 +33,20 @@ class TestTGeogPointConstructors(TestTGeogPoint): @pytest.mark.parametrize( 'source, type, interpolation', [ - (TFloatInst('1.5@2019-09-01'), TGeogPointInst, TInterpolation.NONE), - (TFloatSeq('{1.5@2019-09-01, 0.5@2019-09-02}'), TGeogPointSeq, TInterpolation.DISCRETE), - (TFloatSeq('[1.5@2019-09-01, 0.5@2019-09-02]'), TGeogPointSeq, TInterpolation.LINEAR), - (TFloatSeqSet('{[1.5@2019-09-01, 0.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}'), - TGeogPointSeqSet, TInterpolation.LINEAR) + (TGeogPointInst('Point(1.5 1.5)@2019-09-01'), TGeogPointInst, + TInterpolation.NONE), + (TGeogPointSeq('{Point(1.5 1.5)@2019-09-01, Point(2.5 2.5)@2019-09-02}'), + TGeogPointSeq, TInterpolation.DISCRETE), + (TGeogPointSeq('[Point(1.5 1.5)@2019-09-01, Point(2.5 2.5)@2019-09-02]'), + TGeogPointSeq, TInterpolation.LINEAR), + (TGeogPointSeqSet('{[Point(1.5 1.5)@2019-09-01, Point(2.5 2.5)@2019-09-02],' + '[Point(1.5 1.5)@2019-09-03, Point(1.5 1.5)@2019-09-05]}'), + TGeogPointSeqSet, TInterpolation.LINEAR) ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_from_base_constructor(self, source, type, interpolation): - tp = TGeogPoint.from_base_temporal(Point(1,1), source) + tp = TGeogPoint.from_base_temporal(shapely.set_srid(shapely.Point(1,1), 4326), source) assert isinstance(tp, type) assert tp.interpolation() == interpolation @@ -57,7 +61,7 @@ def test_from_base_constructor(self, source, type, interpolation): ids=['Instant', 'Sequence', 'Discrete Sequence', 'SequenceSet'] ) def test_from_base_time_constructor(self, source, type, interpolation): - tp = TGeogPoint.from_base_time(Point(1,1), source, interpolation) + tp = TGeogPoint.from_base_time(shapely.set_srid(shapely.Point(1,1), 4326), source, interpolation) assert isinstance(tp, type) assert tp.interpolation() == interpolation @@ -1011,10 +1015,10 @@ class TestTGeogPointConversions(TestTGeogPoint): @pytest.mark.parametrize( 'temporal, expected', [ - (tpi, TGeomPointInst('SRID=4326;Point(1 1)@2019-09-01')), - (tpds, TGeomPointSeq('SRID=4326;{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}')), - (tps, TGeomPointSeq('SRID=4326;[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), - (tpss, TGeomPointSeqSet('SRID=4326;{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],' + (tpi, TGeomPointInst('Point(1 1)@2019-09-01')), + (tpds, TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}')), + (tps, TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), + (tpss, TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],' '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] @@ -1054,40 +1058,46 @@ def test_to_instant(self, temporal, expected): assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TGeogPointInst('Point(1 1)@2019-09-01'), + (TGeogPointInst('Point(1 1)@2019-09-01'), TInterpolation.LINEAR, TGeogPointSeq('[Point(1 1)@2019-09-01]')), (TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}'), + TInterpolation.DISCRETE, TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}')), (TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]'), + TInterpolation.LINEAR, TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), (TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}'), + TInterpolation.LINEAR, TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequence(self, temporal, expected): - temp = temporal.to_sequence() + def test_to_sequence(self, temporal, interpolation, expected): + temp = temporal.to_sequence(interpolation) assert isinstance(temp, TGeogPointSeq) assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TGeogPointInst('Point(1 1)@2019-09-01'), + (TGeogPointInst('Point(1 1)@2019-09-01'), TInterpolation.LINEAR, TGeogPointSeqSet('{[Point(1 1)@2019-09-01]}')), (TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}'), + TInterpolation.LINEAR, TGeogPointSeqSet('{[Point(1 1)@2019-09-01], [Point(2 2)@2019-09-02]}')), (TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]'), + TInterpolation.LINEAR, TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}')), (TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}'), + TInterpolation.LINEAR, TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequenceset(self, temporal, expected): - temp = temporal.to_sequenceset() + def test_to_sequenceset(self, temporal, interpolation, expected): + temp = temporal.to_sequenceset(interpolation) assert isinstance(temp, TGeogPointSeqSet) assert temp == expected diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index 21ce1590..7e74da22 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -1110,40 +1110,46 @@ def test_to_instant(self, temporal, expected): assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TGeomPointInst('Point(1 1)@2019-09-01'), + (TGeomPointInst('Point(1 1)@2019-09-01'), TInterpolation.LINEAR, TGeomPointSeq('[Point(1 1)@2019-09-01]')), (TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}'), + TInterpolation.DISCRETE, TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}')), (TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]'), + TInterpolation.LINEAR, TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), (TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}'), + TInterpolation.LINEAR, TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequence(self, temporal, expected): - temp = temporal.to_sequence() + def test_to_sequence(self, temporal, interpolation, expected): + temp = temporal.to_sequence(interpolation) assert isinstance(temp, TGeomPointSeq) assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TGeomPointInst('Point(1 1)@2019-09-01'), + (TGeomPointInst('Point(1 1)@2019-09-01'), TInterpolation.LINEAR, TGeomPointSeqSet('{[Point(1 1)@2019-09-01]}')), (TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}'), + TInterpolation.LINEAR, TGeomPointSeqSet('{[Point(1 1)@2019-09-01], [Point(2 2)@2019-09-02]}')), (TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]'), + TInterpolation.LINEAR, TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}')), (TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}'), + TInterpolation.LINEAR, TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequenceset(self, temporal, expected): - temp = temporal.to_sequenceset() + def test_to_sequenceset(self, temporal, interpolation, expected): + temp = temporal.to_sequenceset(interpolation) assert isinstance(temp, TGeomPointSeqSet) assert temp == expected diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index 4e2e4ff5..abf01fce 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -847,40 +847,46 @@ def test_to_instant(self, temporal, expected): assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TIntInst('1@2019-09-01'), + (TIntInst('1@2019-09-01'), TInterpolation.STEPWISE, TIntSeq('[1@2019-09-01]')), (TIntSeq('{1@2019-09-01, 2@2019-09-02}'), + TInterpolation.DISCRETE, TIntSeq('{1@2019-09-01, 2@2019-09-02}')), (TIntSeq('[1@2019-09-01, 2@2019-09-02]'), + TInterpolation.STEPWISE, TIntSeq('[1@2019-09-01, 2@2019-09-02]')), (TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}'), + TInterpolation.STEPWISE, TIntSeq('[1@2019-09-01, 2@2019-09-02]')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequence(self, temporal, expected): - temp = temporal.to_sequence() + def test_to_sequence(self, temporal, interpolation, expected): + temp = temporal.to_sequence(interpolation) assert isinstance(temp, TIntSeq) assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TIntInst('1@2019-09-01'), + (TIntInst('1@2019-09-01'), TInterpolation.STEPWISE, TIntSeqSet('{[1@2019-09-01]}')), (TIntSeq('{1@2019-09-01, 2@2019-09-02}'), + TInterpolation.STEPWISE, TIntSeqSet('{[1@2019-09-01], [2@2019-09-02]}')), (TIntSeq('[1@2019-09-01, 2@2019-09-02]'), + TInterpolation.STEPWISE, TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), (TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}'), + TInterpolation.STEPWISE, TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequenceset(self, temporal, expected): - temp = temporal.to_sequenceset() + def test_to_sequenceset(self, temporal, interpolation, expected): + temp = temporal.to_sequenceset(interpolation) assert isinstance(temp, TIntSeqSet) assert temp == expected diff --git a/pymeos/tests/main/ttext_test.py b/pymeos/tests/main/ttext_test.py index 21782f20..af88debb 100644 --- a/pymeos/tests/main/ttext_test.py +++ b/pymeos/tests/main/ttext_test.py @@ -747,40 +747,46 @@ def test_to_instant(self, temporal, expected): assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TTextInst('AAA@2019-09-01'), + (TTextInst('AAA@2019-09-01'), TInterpolation.STEPWISE, TTextSeq('[AAA@2019-09-01]')), (TTextSeq('{AAA@2019-09-01, BBB@2019-09-02}'), + TInterpolation.DISCRETE, TTextSeq('{AAA@2019-09-01, BBB@2019-09-02}')), (TTextSeq('[AAA@2019-09-01, BBB@2019-09-02]'), + TInterpolation.STEPWISE, TTextSeq('[AAA@2019-09-01, BBB@2019-09-02]')), (TTextSeqSet('{[AAA@2019-09-01, BBB@2019-09-02]}'), + TInterpolation.STEPWISE, TTextSeq('[AAA@2019-09-01, BBB@2019-09-02]')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequence(self, temporal, expected): - temp = temporal.to_sequence() + def test_to_sequence(self, temporal, interpolation, expected): + temp = temporal.to_sequence(interpolation) assert isinstance(temp, TTextSeq) assert temp == expected @pytest.mark.parametrize( - 'temporal, expected', + 'temporal, interpolation, expected', [ - (TTextInst('AAA@2019-09-01'), + (TTextInst('AAA@2019-09-01'), TInterpolation.STEPWISE, TTextSeqSet('{[AAA@2019-09-01]}')), (TTextSeq('{AAA@2019-09-01, BBB@2019-09-02}'), + TInterpolation.STEPWISE, TTextSeqSet('{[AAA@2019-09-01], [BBB@2019-09-02]}')), (TTextSeq('[AAA@2019-09-01, BBB@2019-09-02]'), + TInterpolation.STEPWISE, TTextSeqSet('{[AAA@2019-09-01, BBB@2019-09-02]}')), (TTextSeqSet('{[AAA@2019-09-01, BBB@2019-09-02]}'), + TInterpolation.STEPWISE, TTextSeqSet('{[AAA@2019-09-01, BBB@2019-09-02]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_to_sequenceset(self, temporal, expected): - temp = temporal.to_sequenceset() + def test_to_sequenceset(self, temporal, interpolation, expected): + temp = temporal.to_sequenceset(interpolation) assert isinstance(temp, TTextSeqSet) assert temp == expected diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index de436d78..6dbeb318 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -68,6 +68,7 @@ 'pg_interval_make', 'pg_interval_mul', 'pg_interval_out', + 'pg_interval_to_char', 'pg_interval_pl', 'pg_time_in', 'pg_time_out', @@ -76,16 +77,17 @@ 'pg_timestamp_mi_interval', 'pg_timestamp_out', 'pg_timestamp_pl_interval', + 'pg_timestamp_to_char', 'pg_timestamptz_in', 'pg_timestamptz_out', - 'text2cstring', - 'pg_timestamp_to_char', 'pg_timestamptz_to_char', - 'pg_interval_to_char', - 'pg_to_timestamp', 'pg_to_date', + 'pg_to_timestamp', + 'text2cstring', 'geography_from_hexewkb', + 'geography_from_text', 'geometry_from_hexewkb', + 'geometry_from_text', 'gserialized_as_ewkb', 'gserialized_as_ewkt', 'gserialized_as_geojson', @@ -94,8 +96,6 @@ 'gserialized_from_ewkb', 'gserialized_from_geojson', 'gserialized_out', - 'geometry_from_text', - 'geography_from_text', 'pgis_geography_in', 'pgis_geometry_in', 'pgis_gserialized_same', @@ -112,10 +112,10 @@ 'floatspanset_in', 'floatspanset_out', 'geogset_in', - 'geoset_out', 'geomset_in', 'geoset_as_ewkt', 'geoset_as_text', + 'geoset_out', 'intset_in', 'intset_out', 'intspan_in', @@ -130,17 +130,14 @@ 'set_as_wkb', 'set_from_hexwkb', 'set_from_wkb', - 'set_out', - 'span_as_wkb', 'span_as_hexwkb', + 'span_as_wkb', 'span_from_hexwkb', 'span_from_wkb', - 'span_out', - 'spanset_as_wkb', 'spanset_as_hexwkb', + 'spanset_as_wkb', 'spanset_from_hexwkb', 'spanset_from_wkb', - 'spanset_out', 'textset_in', 'textset_out', 'timestampset_in', @@ -157,7 +154,6 @@ 'span_copy', 'spanset_copy', 'spanset_make', - 'spanset_make_exp', 'textset_make', 'timestampset_make', 'bigint_to_bigintset', @@ -218,7 +214,6 @@ 'periodset_upper', 'set_hash', 'set_hash_extended', - 'set_mem_size', 'set_num_values', 'set_span', 'span_hash', @@ -230,7 +225,6 @@ 'spanset_hash', 'spanset_hash_extended', 'spanset_lower_inc', - 'spanset_mem_size', 'spanset_num_spans', 'spanset_span', 'spanset_span_n', @@ -248,13 +242,17 @@ 'timestampset_timestamp_n', 'timestampset_values', 'floatset_round', + 'floatspan_intspan', 'floatspan_round', + 'floatspanset_intspanset', 'floatspanset_round', 'geoset_round', - 'period_tprecision', - 'periodset_tprecision', + 'intspan_floatspan', + 'intspanset_floatspanset', 'period_shift_tscale', + 'period_tprecision', 'periodset_shift_tscale', + 'periodset_tprecision', 'timestamp_tprecision', 'timestampset_shift_tscale', 'adjacent_bigintspan_bigint', @@ -363,6 +361,7 @@ 'intersection_floatset_float', 'intersection_floatspan_float', 'intersection_floatspanset_float', + 'intersection_geoset_geo', 'intersection_intset_int', 'intersection_intspan_int', 'intersection_intspanset_int', @@ -386,6 +385,8 @@ 'minus_floatset_float', 'minus_floatspan_float', 'minus_floatspanset_float', + 'minus_geo_geoset', + 'minus_geoset_geo', 'minus_int_intset', 'minus_int_intspan', 'minus_int_intspanset', @@ -403,6 +404,7 @@ 'minus_textset_text', 'minus_timestamp_period', 'minus_timestamp_periodset', + 'minus_timestamp_timestampset', 'minus_timestampset_timestamp', 'union_bigintset_bigint', 'union_bigintspan_bigint', @@ -410,6 +412,7 @@ 'union_floatset_float', 'union_floatspan_float', 'union_floatspanset_float', + 'union_geoset_geo', 'union_intset_int', 'union_intspan_int', 'union_intspanset_int', @@ -421,11 +424,18 @@ 'union_spanset_spanset', 'union_textset_text', 'union_timestampset_timestamp', + 'distance_bigintset_bigint', + 'distance_bigintspan_bigint', + 'distance_bigintspanset_bigint', + 'distance_floatset_float', 'distance_floatspan_float', + 'distance_floatspanset_float', + 'distance_intset_int', 'distance_intspan_int', - 'distance_set_set', + 'distance_intspanset_int', 'distance_period_timestamp', 'distance_periodset_timestamp', + 'distance_set_set', 'distance_span_span', 'distance_spanset_span', 'distance_spanset_spanset', @@ -643,9 +653,7 @@ 'tpointseq_from_base_timestampset', 'tpointseqset_from_base_periodset', 'tsequence_make', - 'tsequence_make_exp', 'tsequenceset_make', - 'tsequenceset_make_exp', 'tsequenceset_make_gaps', 'ttext_from_base_temp', 'ttextinst_make', @@ -804,7 +812,7 @@ 'distance_tfloat_float', 'distance_tint_int', 'distance_tnumber_tnumber', - 'distance_tpoint_geo', + 'distance_tpoint_point', 'distance_tpoint_tpoint', 'nad_stbox_geo', 'nad_stbox_stbox', @@ -908,7 +916,9 @@ 'tpoint_convex_hull', 'tpoint_cumulative_length', 'tpoint_direction', - 'tpoint_get_coord', + 'tpoint_get_x', + 'tpoint_get_y', + 'tpoint_get_z', 'tpoint_is_simple', 'tpoint_length', 'tpoint_speed', @@ -916,8 +926,9 @@ 'tpoint_stboxes', 'tpoint_trajectory', 'geo_expand_space', - 'tgeompoint_tgeogpoint', 'tpoint_expand_space', + 'tgeompoint_to_tgeogpoint', + 'tgeogpoint_to_tgeompoint', 'tpoint_round', 'tpoint_make_simple', 'tpoint_set_srid', diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index f63cdcb3..da956599 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -757,6 +757,7 @@ extern Interval *pg_interval_in(const char *str, int32 typmod); extern Interval *pg_interval_make(int32 years, int32 months, int32 weeks, int32 days, int32 hours, int32 mins, double secs); extern Interval *pg_interval_mul(const Interval *span, double factor); extern char *pg_interval_out(const Interval *span); +extern text *pg_interval_to_char(Interval *it, text *fmt); extern Interval *pg_interval_pl(const Interval *span1, const Interval *span2); extern TimeADT pg_time_in(const char *str, int32 typmod); extern char *pg_time_out(TimeADT time); @@ -765,21 +766,22 @@ extern Interval *pg_timestamp_mi(TimestampTz dt1, TimestampTz dt2); extern TimestampTz pg_timestamp_mi_interval(TimestampTz timestamp, const Interval *span); extern char *pg_timestamp_out(Timestamp dt); extern TimestampTz pg_timestamp_pl_interval(TimestampTz timestamp, const Interval *span); +extern text *pg_timestamp_to_char(Timestamp dt, text *fmt); extern TimestampTz pg_timestamptz_in(const char *str, int32 typmod); extern char *pg_timestamptz_out(TimestampTz dt); -extern char *text2cstring(const text *textptr); -extern text *pg_timestamp_to_char(Timestamp dt, text *fmt); extern text *pg_timestamptz_to_char(TimestampTz dt, text *fmt); -extern text *pg_interval_to_char(Interval *it, text *fmt); -extern TimestampTz pg_to_timestamp(text *date_txt, text *fmt); extern DateADT pg_to_date(text *date_txt, text *fmt); +extern TimestampTz pg_to_timestamp(text *date_txt, text *fmt); +extern char *text2cstring(const text *textptr); /***************************************************************************** * Functions for input/output and manipulation of PostGIS types *****************************************************************************/ extern GSERIALIZED *geography_from_hexewkb(const char *wkt); +extern GSERIALIZED *geography_from_text(char *wkt, int srid); extern GSERIALIZED *geometry_from_hexewkb(const char *wkt); +extern GSERIALIZED *geometry_from_text(char *wkt, int srid); extern bytea *gserialized_as_ewkb(const GSERIALIZED *gs, char *type); extern char *gserialized_as_ewkt(const GSERIALIZED *gs, int precision); extern char *gserialized_as_geojson(const GSERIALIZED *gs, int option, int precision, char *srs); @@ -788,11 +790,9 @@ extern char *gserialized_as_text(const GSERIALIZED *gs, int precision); extern GSERIALIZED *gserialized_from_ewkb(const bytea *bytea_wkb, int32 srid); extern GSERIALIZED *gserialized_from_geojson(const char *geojson); extern char *gserialized_out(const GSERIALIZED *gs); -extern GSERIALIZED *geometry_from_text(char *wkt, int srid); -extern GSERIALIZED *geography_from_text(char *wkt, int srid); extern GSERIALIZED *pgis_geography_in(char *input, int32 geom_typmod); extern GSERIALIZED *pgis_geometry_in(char *input, int32 geom_typmod); -extern bool pgis_gserialized_same(const GSERIALIZED *gs1, const GSERIALIZED *geom2); +extern bool pgis_gserialized_same(const GSERIALIZED *gs1, const GSERIALIZED *gs2); /***************************************************************************** * Functions for set and span types @@ -813,10 +813,10 @@ extern char *floatspan_out(const Span *s, int maxdd); extern SpanSet *floatspanset_in(const char *str); extern char *floatspanset_out(const SpanSet *ss, int maxdd); extern Set *geogset_in(const char *str); -extern char *geoset_out(const Set *set, int maxdd); extern Set *geomset_in(const char *str); extern char *geoset_as_ewkt(const Set *set, int maxdd); extern char *geoset_as_text(const Set *set, int maxdd); +extern char *geoset_out(const Set *set, int maxdd); extern Set *intset_in(const char *str); extern char *intset_out(const Set *set); extern Span *intspan_in(const char *str); @@ -831,17 +831,14 @@ extern char *set_as_hexwkb(const Set *s, uint8_t variant, size_t *size_out); extern uint8_t *set_as_wkb(const Set *s, uint8_t variant, size_t *size_out); extern Set *set_from_hexwkb(const char *hexwkb); extern Set *set_from_wkb(const uint8_t *wkb, size_t size); -extern char *set_out(const Set *s, int maxdd); -extern uint8_t *span_as_wkb(const Span *s, uint8_t variant, size_t *size_out); extern char *span_as_hexwkb(const Span *s, uint8_t variant, size_t *size_out); +extern uint8_t *span_as_wkb(const Span *s, uint8_t variant, size_t *size_out); extern Span *span_from_hexwkb(const char *hexwkb); extern Span *span_from_wkb(const uint8_t *wkb, size_t size); -extern char *span_out(const Span *s, int maxdd); -extern uint8_t *spanset_as_wkb(const SpanSet *ss, uint8_t variant, size_t *size_out); extern char *spanset_as_hexwkb(const SpanSet *ss, uint8_t variant, size_t *size_out); +extern uint8_t *spanset_as_wkb(const SpanSet *ss, uint8_t variant, size_t *size_out); extern SpanSet *spanset_from_hexwkb(const char *hexwkb); extern SpanSet *spanset_from_wkb(const uint8_t *wkb, size_t size); -extern char *spanset_out(const SpanSet *ss, int maxdd); extern Set *textset_in(const char *str); extern char *textset_out(const Set *set); extern Set *timestampset_in(const char *str); @@ -863,7 +860,6 @@ extern Set *set_copy(const Set *s); extern Span *span_copy(const Span *s); extern SpanSet *spanset_copy(const SpanSet *ps); extern SpanSet *spanset_make(Span *spans, int count, bool normalize); -extern SpanSet *spanset_make_exp(Span *spans, int count, int maxcount, bool normalize, bool ordered); extern Set *textset_make(const text **values, int count); extern Set *timestampset_make(const TimestampTz *values, int count); @@ -934,7 +930,6 @@ extern TimestampTz *periodset_timestamps(const SpanSet *ps, int *count); extern TimestampTz periodset_upper(const SpanSet *ps); extern uint32 set_hash(const Set *s); extern uint64 set_hash_extended(const Set *s, uint64 seed); -extern int set_mem_size(const Set *s); extern int set_num_values(const Set *s); extern Span *set_span(const Set *s); extern uint32 span_hash(const Span *s); @@ -946,7 +941,6 @@ extern Span *spanset_end_span(const SpanSet *ss); extern uint32 spanset_hash(const SpanSet *ps); extern uint64 spanset_hash_extended(const SpanSet *ps, uint64 seed); extern bool spanset_lower_inc(const SpanSet *ss); -extern int spanset_mem_size(const SpanSet *ss); extern int spanset_num_spans(const SpanSet *ss); extern Span *spanset_span(const SpanSet *ss); extern Span *spanset_span_n(const SpanSet *ss, int i); @@ -969,13 +963,17 @@ extern TimestampTz *timestampset_values(const Set *ts); extern Set *floatset_round(const Set *s, int maxdd); +extern Span *floatspan_intspan(const Span *s); extern Span *floatspan_round(const Span *s, int maxdd); +extern SpanSet *floatspanset_intspanset(const SpanSet *ss); extern SpanSet *floatspanset_round(const SpanSet *ss, int maxdd); extern Set *geoset_round(const Set *s, int maxdd); -extern Span *period_tprecision(const Span *s, const Interval *duration, TimestampTz torigin); -extern SpanSet *periodset_tprecision(const SpanSet *ss, const Interval *duration, TimestampTz torigin); +extern Span *intspan_floatspan(const Span *s); +extern SpanSet *intspanset_floatspanset(const SpanSet *ss); extern Span *period_shift_tscale(const Span *p, const Interval *shift, const Interval *duration); +extern Span *period_tprecision(const Span *s, const Interval *duration, TimestampTz torigin); extern SpanSet *periodset_shift_tscale(const SpanSet *ps, const Interval *shift, const Interval *duration); +extern SpanSet *periodset_tprecision(const SpanSet *ss, const Interval *duration, TimestampTz torigin); extern TimestampTz timestamp_tprecision(TimestampTz t, const Interval *duration, TimestampTz torigin); extern Set *timestampset_shift_tscale(const Set *ts, const Interval *shift, const Interval *duration); @@ -1101,6 +1099,7 @@ extern bool intersection_bigintspanset_bigint(const SpanSet *ss, int64 i, int64 extern bool intersection_floatset_float(const Set *s, double d, double *result); extern bool intersection_floatspan_float(const Span *s, double d, double *result); extern bool intersection_floatspanset_float(const SpanSet *ss, double d, double *result); +extern bool intersection_geoset_geo(const Set *s, const GSERIALIZED *gs, GSERIALIZED **result); extern bool intersection_intset_int(const Set *s, int i, int *result); extern bool intersection_intspan_int(const Span *s, int i, int *result); extern bool intersection_intspanset_int(const SpanSet *ss, int i, int *result); @@ -1124,6 +1123,8 @@ extern bool minus_float_floatspanset(double d, const SpanSet *ss, double *result extern Set *minus_floatset_float(const Set *s, double d); extern SpanSet *minus_floatspan_float(const Span *s, double d); extern SpanSet *minus_floatspanset_float(const SpanSet *ss, double d); +extern bool minus_geo_geoset(const GSERIALIZED *gs, const Set *s, GSERIALIZED **result); +extern Set *minus_geoset_geo(const Set *s, const GSERIALIZED *gs); extern bool minus_int_intset(int i, const Set *s, int *result); extern bool minus_int_intspan(int i, const Span *s, int *result); extern bool minus_int_intspanset(int i, const SpanSet *ss, int *result); @@ -1141,6 +1142,7 @@ extern bool minus_text_textset(const text *txt, const Set *s, text **result); extern Set *minus_textset_text(const Set *s, const text *txt); extern bool minus_timestamp_period(TimestampTz t, const Span *s, TimestampTz *result); extern bool minus_timestamp_periodset(TimestampTz t, const SpanSet *ss, TimestampTz *result); +extern bool minus_timestamp_timestampset(TimestampTz t, const Set *s, TimestampTz *result); extern Set *minus_timestampset_timestamp(const Set *s, TimestampTz t); extern Set *union_bigintset_bigint(const Set *s, int64 i); extern SpanSet *union_bigintspan_bigint(const Span *s, int64 i); @@ -1148,6 +1150,7 @@ extern SpanSet *union_bigintspanset_bigint(const SpanSet *ss, int64 i); extern Set *union_floatset_float(const Set *s, double d); extern SpanSet *union_floatspan_float(const Span *s, double d); extern SpanSet *union_floatspanset_float(const SpanSet *ss, double d); +extern Set *union_geoset_geo(const Set *s, const GSERIALIZED *gs); extern Set *union_intset_int(const Set *s, int i); extern SpanSet *union_intspan_int(const Span *s, int i); extern SpanSet *union_intspanset_int(const SpanSet *ss, int i); @@ -1157,22 +1160,29 @@ extern Set *union_set_set(const Set *s1, const Set *s2); extern SpanSet *union_span_span(const Span *s1, const Span *s2); extern SpanSet *union_spanset_span(const SpanSet *ss, const Span *s); extern SpanSet *union_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern Set *union_textset_text(const Set *s, text *txt); +extern Set *union_textset_text(const Set *s, const text *txt); extern Set *union_timestampset_timestamp(const Set *s, const TimestampTz t); +extern double distance_bigintset_bigint(const Set *s, int64 i); +extern double distance_bigintspan_bigint(const Span *s, int64 i); +extern double distance_bigintspanset_bigint(const SpanSet *ss, int64 i); +extern double distance_floatset_float(const Set *s, double d); extern double distance_floatspan_float(const Span *s, double d); +extern double distance_floatspanset_float(const SpanSet *ss, double d); +extern double distance_intset_int(const Set *s, int i); extern double distance_intspan_int(const Span *s, int i); +extern double distance_intspanset_int(const SpanSet *ss, int i); +extern double distance_period_timestamp(const Span *s, TimestampTz t); +extern double distance_periodset_timestamp(const SpanSet *ss, TimestampTz t); extern double distance_set_set(const Set *s1, const Set *s2); -extern double distance_period_timestamp(const Span *p, TimestampTz t); -extern double distance_periodset_timestamp(const SpanSet *ps, TimestampTz t); extern double distance_span_span(const Span *s1, const Span *s2); extern double distance_spanset_span(const SpanSet *ss, const Span *s); extern double distance_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern double distance_timestampset_timestamp(const Set *ts, TimestampTz t); +extern double distance_timestampset_timestamp(const Set *s, TimestampTz t); @@ -1461,9 +1471,7 @@ extern TSequence *tpointseq_from_base_period(const GSERIALIZED *gs, const Span * extern TSequence *tpointseq_from_base_timestampset(const GSERIALIZED *gs, const Set *ts); extern TSequenceSet *tpointseqset_from_base_periodset(const GSERIALIZED *gs, const SpanSet *ps, interpType interp); extern TSequence *tsequence_make(const TInstant **instants, int count, bool lower_inc, bool upper_inc, interpType interp, bool normalize); -extern TSequence *tsequence_make_exp(const TInstant **instants, int count, int maxcount, bool lower_inc, bool upper_inc, interpType interp, bool normalize); extern TSequenceSet *tsequenceset_make(const TSequence **sequences, int count, bool normalize); -extern TSequenceSet *tsequenceset_make_exp(const TSequence **sequences, int count, int maxcount, bool normalize); extern TSequenceSet *tsequenceset_make_gaps(const TInstant **instants, int count, interpType interp, Interval *maxt, double maxdist); extern Temporal *ttext_from_base_temp(const text *txt, const Temporal *temp); extern TInstant *ttextinst_make(const text *txt, TimestampTz t); @@ -1539,8 +1547,8 @@ extern Temporal *temporal_set_interp(const Temporal *temp, interpType interp); extern Temporal *temporal_shift(const Temporal *temp, const Interval *shift); extern Temporal *temporal_shift_tscale(const Temporal *temp, const Interval *shift, const Interval *duration); extern Temporal *temporal_to_tinstant(const Temporal *temp); -extern Temporal *temporal_to_tsequence(const Temporal *temp); -extern Temporal *temporal_to_tsequenceset(const Temporal *temp); +extern Temporal *temporal_to_tsequence(const Temporal *temp, interpType interp); +extern Temporal *temporal_to_tsequenceset(const Temporal *temp, interpType interp); extern Temporal *temporal_tprecision(const Temporal *temp, const Interval *duration, TimestampTz origin); extern Temporal *temporal_tsample(const Temporal *temp, const Interval *duration, TimestampTz origin); extern Temporal *temporal_tscale(const Temporal *temp, const Interval *duration); @@ -1667,7 +1675,7 @@ extern Temporal *ttext_lower(const Temporal *temp); extern Temporal *distance_tfloat_float(const Temporal *temp, double d); extern Temporal *distance_tint_int(const Temporal *temp, int i); extern Temporal *distance_tnumber_tnumber(const Temporal *temp1, const Temporal *temp2); -extern Temporal *distance_tpoint_geo(const Temporal *temp, const GSERIALIZED *geo); +extern Temporal *distance_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); extern Temporal *distance_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); extern double nad_stbox_geo(const STBox *box, const GSERIALIZED *gs); extern double nad_stbox_stbox(const STBox *box1, const STBox *box2); @@ -1780,7 +1788,7 @@ extern Temporal *tne_ttext_text(const Temporal *temp, const text *txt); -extern bool bearing_point_point(const GSERIALIZED *geo1, const GSERIALIZED *geo2, double *result); +extern bool bearing_point_point(const GSERIALIZED *gs1, const GSERIALIZED *gs2, double *result); extern Temporal *bearing_tpoint_point(const Temporal *temp, const GSERIALIZED *gs, bool invert); extern Temporal *bearing_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); extern Temporal *tpoint_angular_difference(const Temporal *temp); @@ -1788,7 +1796,9 @@ extern Temporal *tpoint_azimuth(const Temporal *temp); extern GSERIALIZED *tpoint_convex_hull(const Temporal *temp); extern Temporal *tpoint_cumulative_length(const Temporal *temp); extern bool tpoint_direction(const Temporal *temp, double *result); -extern Temporal *tpoint_get_coord(const Temporal *temp, int coord); +extern Temporal *tpoint_get_x(const Temporal *temp); +extern Temporal *tpoint_get_y(const Temporal *temp); +extern Temporal *tpoint_get_z(const Temporal *temp); extern bool tpoint_is_simple(const Temporal *temp); extern double tpoint_length(const Temporal *temp); extern Temporal *tpoint_speed(const Temporal *temp); @@ -1801,8 +1811,9 @@ extern GSERIALIZED *tpoint_trajectory(const Temporal *temp); extern STBox *geo_expand_space(const GSERIALIZED *gs, double d); -extern Temporal *tgeompoint_tgeogpoint(const Temporal *temp, bool oper); extern STBox *tpoint_expand_space(const Temporal *temp, double d); +extern Temporal *tgeompoint_to_tgeogpoint(const Temporal *temp); +extern Temporal *tgeogpoint_to_tgeompoint(const Temporal *temp); extern Temporal *tpoint_round(const Temporal *temp, int maxdd); extern Temporal **tpoint_make_simple(const Temporal *temp, int *count); extern Temporal *tpoint_set_srid(const Temporal *temp, int32 srid); @@ -1811,7 +1822,7 @@ extern Temporal *tpoint_set_srid(const Temporal *temp, int32 srid); -extern int econtains_geo_tpoint(const GSERIALIZED *geo, const Temporal *temp); +extern int econtains_geo_tpoint(const GSERIALIZED *gs, const Temporal *temp); extern int edisjoint_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); extern int edisjoint_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); extern int edwithin_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, double dist); @@ -1820,10 +1831,10 @@ extern int eintersects_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); extern int eintersects_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); extern int etouches_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); extern Temporal *tcontains_geo_tpoint(const GSERIALIZED *gs, const Temporal *temp, bool restr, bool atvalue); -extern Temporal *tdisjoint_tpoint_geo(const Temporal *temp, const GSERIALIZED *geo, bool restr, bool atvalue); +extern Temporal *tdisjoint_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, bool restr, bool atvalue); extern Temporal *tdwithin_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, double dist, bool restr, bool atvalue); extern Temporal *tdwithin_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2, double dist, bool restr, bool atvalue); -extern Temporal *tintersects_tpoint_geo(const Temporal *temp, const GSERIALIZED *geo, bool restr, bool atvalue); +extern Temporal *tintersects_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, bool restr, bool atvalue); extern Temporal *ttouches_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, bool restr, bool atvalue); @@ -1885,12 +1896,12 @@ extern double temporal_hausdorff_distance(const Temporal *temp1, const Temporal -Temporal *geo_to_tpoint(const GSERIALIZED *geo); +Temporal *geo_to_tpoint(const GSERIALIZED *gs); Temporal *temporal_simplify_min_dist(const Temporal *temp, double dist); Temporal *temporal_simplify_min_tdelta(const Temporal *temp, const Interval *mint); Temporal *temporal_simplify_dp(const Temporal *temp, double eps_dist, bool synchronized); Temporal *temporal_simplify_max_dist(const Temporal *temp, double eps_dist, bool synchronized); -bool tpoint_AsMVTGeom(const Temporal *temp, const STBox *bounds, int32_t extent, int32_t buffer, bool clip_geom, GSERIALIZED **geom, int64 **timesarr, int *count); +bool tpoint_AsMVTGeom(const Temporal *temp, const STBox *bounds, int32_t extent, int32_t buffer, bool clip_geom, GSERIALIZED **gsarr, int64 **timesarr, int *count); bool tpoint_to_geo_meas(const Temporal *tpoint, const Temporal *measure, bool segmentize, GSERIALIZED **result); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 9d79fa8e..ab0afc09 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -300,6 +300,15 @@ def pg_interval_out(span: 'const Interval *') -> str: return result if result != _ffi.NULL else None +def pg_interval_to_char(it: 'Interval *', fmt: str) -> str: + it_converted = _ffi.cast('Interval *', it) + fmt_converted = cstring2text(fmt) + result = _lib.pg_interval_to_char(it_converted, fmt_converted) + _check_error() + result = text2cstring(result) + return result if result != _ffi.NULL else None + + def pg_interval_pl(span1: 'const Interval *', span2: 'const Interval *') -> 'Interval *': span1_converted = _ffi.cast('const Interval *', span1) span2_converted = _ffi.cast('const Interval *', span2) @@ -364,6 +373,15 @@ def pg_timestamp_pl_interval(timestamp: int, span: 'const Interval *') -> 'Times return result if result != _ffi.NULL else None +def pg_timestamp_to_char(dt: int, fmt: str) -> str: + dt_converted = _ffi.cast('Timestamp', dt) + fmt_converted = cstring2text(fmt) + result = _lib.pg_timestamp_to_char(dt_converted, fmt_converted) + _check_error() + result = text2cstring(result) + return result if result != _ffi.NULL else None + + def pg_timestamptz_in(string: str, typmod: int) -> 'TimestampTz': string_converted = string.encode('utf-8') typmod_converted = _ffi.cast('int32', typmod) @@ -380,21 +398,6 @@ def pg_timestamptz_out(dt: int) -> str: return result if result != _ffi.NULL else None -def text2cstring(textptr: 'text *') -> str: - result = _lib.text2cstring(textptr) - result = _ffi.string(result).decode('utf-8') - return result - - -def pg_timestamp_to_char(dt: int, fmt: str) -> str: - dt_converted = _ffi.cast('Timestamp', dt) - fmt_converted = cstring2text(fmt) - result = _lib.pg_timestamp_to_char(dt_converted, fmt_converted) - _check_error() - result = text2cstring(result) - return result if result != _ffi.NULL else None - - def pg_timestamptz_to_char(dt: int, fmt: str) -> str: dt_converted = _ffi.cast('TimestampTz', dt) fmt_converted = cstring2text(fmt) @@ -404,12 +407,11 @@ def pg_timestamptz_to_char(dt: int, fmt: str) -> str: return result if result != _ffi.NULL else None -def pg_interval_to_char(it: 'Interval *', fmt: str) -> str: - it_converted = _ffi.cast('Interval *', it) +def pg_to_date(date_txt: str, fmt: str) -> 'DateADT': + date_txt_converted = cstring2text(date_txt) fmt_converted = cstring2text(fmt) - result = _lib.pg_interval_to_char(it_converted, fmt_converted) + result = _lib.pg_to_date(date_txt_converted, fmt_converted) _check_error() - result = text2cstring(result) return result if result != _ffi.NULL else None @@ -421,12 +423,10 @@ def pg_to_timestamp(date_txt: str, fmt: str) -> 'TimestampTz': return result if result != _ffi.NULL else None -def pg_to_date(date_txt: str, fmt: str) -> 'DateADT': - date_txt_converted = cstring2text(date_txt) - fmt_converted = cstring2text(fmt) - result = _lib.pg_to_date(date_txt_converted, fmt_converted) - _check_error() - return result if result != _ffi.NULL else None +def text2cstring(textptr: 'text *') -> str: + result = _lib.text2cstring(textptr) + result = _ffi.string(result).decode('utf-8') + return result def geography_from_hexewkb(wkt: str) -> 'GSERIALIZED *': @@ -436,6 +436,13 @@ def geography_from_hexewkb(wkt: str) -> 'GSERIALIZED *': return result if result != _ffi.NULL else None +def geography_from_text(wkt: str, srid: int) -> 'GSERIALIZED *': + wkt_converted = wkt.encode('utf-8') + result = _lib.geography_from_text(wkt_converted, srid) + _check_error() + return result if result != _ffi.NULL else None + + def geometry_from_hexewkb(wkt: str) -> 'GSERIALIZED *': wkt_converted = wkt.encode('utf-8') result = _lib.geometry_from_hexewkb(wkt_converted) @@ -443,6 +450,13 @@ def geometry_from_hexewkb(wkt: str) -> 'GSERIALIZED *': return result if result != _ffi.NULL else None +def geometry_from_text(wkt: str, srid: int) -> 'GSERIALIZED *': + wkt_converted = wkt.encode('utf-8') + result = _lib.geometry_from_text(wkt_converted, srid) + _check_error() + return result if result != _ffi.NULL else None + + def gserialized_as_ewkb(gs: 'const GSERIALIZED *', type: str) -> 'bytea *': gs_converted = _ffi.cast('const GSERIALIZED *', gs) type_converted = type.encode('utf-8') @@ -508,20 +522,6 @@ def gserialized_out(gs: 'const GSERIALIZED *') -> str: return result if result != _ffi.NULL else None -def geometry_from_text(wkt: str, srid: int) -> 'GSERIALIZED *': - wkt_converted = wkt.encode('utf-8') - result = _lib.geometry_from_text(wkt_converted, srid) - _check_error() - return result if result != _ffi.NULL else None - - -def geography_from_text(wkt: str, srid: int) -> 'GSERIALIZED *': - wkt_converted = wkt.encode('utf-8') - result = _lib.geography_from_text(wkt_converted, srid) - _check_error() - return result if result != _ffi.NULL else None - - def pgis_geography_in(input: str, geom_typmod: int) -> 'GSERIALIZED *': input_converted = input.encode('utf-8') geom_typmod_converted = _ffi.cast('int32', geom_typmod) @@ -538,10 +538,10 @@ def pgis_geometry_in(input: str, geom_typmod: int) -> 'GSERIALIZED *': return result if result != _ffi.NULL else None -def pgis_gserialized_same(gs1: 'const GSERIALIZED *', geom2: 'const GSERIALIZED *') -> 'bool': +def pgis_gserialized_same(gs1: 'const GSERIALIZED *', gs2: 'const GSERIALIZED *') -> 'bool': gs1_converted = _ffi.cast('const GSERIALIZED *', gs1) - geom2_converted = _ffi.cast('const GSERIALIZED *', geom2) - result = _lib.pgis_gserialized_same(gs1_converted, geom2_converted) + gs2_converted = _ffi.cast('const GSERIALIZED *', gs2) + result = _lib.pgis_gserialized_same(gs1_converted, gs2_converted) _check_error() return result if result != _ffi.NULL else None @@ -643,14 +643,6 @@ def geogset_in(string: str) -> 'Set *': return result if result != _ffi.NULL else None -def geoset_out(set: 'const Set *', maxdd: int) -> str: - set_converted = _ffi.cast('const Set *', set) - result = _lib.geoset_out(set_converted, maxdd) - _check_error() - result = _ffi.string(result).decode('utf-8') - return result if result != _ffi.NULL else None - - def geomset_in(string: str) -> 'Set *': string_converted = string.encode('utf-8') result = _lib.geomset_in(string_converted) @@ -674,6 +666,14 @@ def geoset_as_text(set: 'const Set *', maxdd: int) -> str: return result if result != _ffi.NULL else None +def geoset_out(set: 'const Set *', maxdd: int) -> str: + set_converted = _ffi.cast('const Set *', set) + result = _lib.geoset_out(set_converted, maxdd) + _check_error() + result = _ffi.string(result).decode('utf-8') + return result if result != _ffi.NULL else None + + def intset_in(string: str) -> 'Set *': string_converted = string.encode('utf-8') result = _lib.intset_in(string_converted) @@ -782,12 +782,14 @@ def set_from_wkb(wkb: bytes) -> 'Set *': return result if result != _ffi.NULL else None -def set_out(s: 'const Set *', maxdd: int) -> str: - s_converted = _ffi.cast('const Set *', s) - result = _lib.set_out(s_converted, maxdd) +def span_as_hexwkb(s: 'const Span *', variant: int) -> "Tuple[str, 'size_t *']": + s_converted = _ffi.cast('const Span *', s) + variant_converted = _ffi.cast('uint8_t', variant) + size_out = _ffi.new('size_t *') + result = _lib.span_as_hexwkb(s_converted, variant_converted, size_out) _check_error() result = _ffi.string(result).decode('utf-8') - return result if result != _ffi.NULL else None + return result if result != _ffi.NULL else None, size_out[0] def span_as_wkb(s: 'const Span *', variant: int) -> bytes: @@ -800,16 +802,6 @@ def span_as_wkb(s: 'const Span *', variant: int) -> bytes: return result_converted -def span_as_hexwkb(s: 'const Span *', variant: int) -> "Tuple[str, 'size_t *']": - s_converted = _ffi.cast('const Span *', s) - variant_converted = _ffi.cast('uint8_t', variant) - size_out = _ffi.new('size_t *') - result = _lib.span_as_hexwkb(s_converted, variant_converted, size_out) - _check_error() - result = _ffi.string(result).decode('utf-8') - return result if result != _ffi.NULL else None, size_out[0] - - def span_from_hexwkb(hexwkb: str) -> 'Span *': hexwkb_converted = hexwkb.encode('utf-8') result = _lib.span_from_hexwkb(hexwkb_converted) @@ -823,12 +815,14 @@ def span_from_wkb(wkb: bytes) -> 'Span *': return result if result != _ffi.NULL else None -def span_out(s: 'const Span *', maxdd: int) -> str: - s_converted = _ffi.cast('const Span *', s) - result = _lib.span_out(s_converted, maxdd) +def spanset_as_hexwkb(ss: 'const SpanSet *', variant: int) -> "Tuple[str, 'size_t *']": + ss_converted = _ffi.cast('const SpanSet *', ss) + variant_converted = _ffi.cast('uint8_t', variant) + size_out = _ffi.new('size_t *') + result = _lib.spanset_as_hexwkb(ss_converted, variant_converted, size_out) _check_error() result = _ffi.string(result).decode('utf-8') - return result if result != _ffi.NULL else None + return result if result != _ffi.NULL else None, size_out[0] def spanset_as_wkb(ss: 'const SpanSet *', variant: int) -> bytes: @@ -841,16 +835,6 @@ def spanset_as_wkb(ss: 'const SpanSet *', variant: int) -> bytes: return result_converted -def spanset_as_hexwkb(ss: 'const SpanSet *', variant: int) -> "Tuple[str, 'size_t *']": - ss_converted = _ffi.cast('const SpanSet *', ss) - variant_converted = _ffi.cast('uint8_t', variant) - size_out = _ffi.new('size_t *') - result = _lib.spanset_as_hexwkb(ss_converted, variant_converted, size_out) - _check_error() - result = _ffi.string(result).decode('utf-8') - return result if result != _ffi.NULL else None, size_out[0] - - def spanset_from_hexwkb(hexwkb: str) -> 'SpanSet *': hexwkb_converted = hexwkb.encode('utf-8') result = _lib.spanset_from_hexwkb(hexwkb_converted) @@ -864,14 +848,6 @@ def spanset_from_wkb(wkb: bytes) -> 'SpanSet *': return result if result != _ffi.NULL else None -def spanset_out(ss: 'const SpanSet *', maxdd: int) -> str: - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.spanset_out(ss_converted, maxdd) - _check_error() - result = _ffi.string(result).decode('utf-8') - return result if result != _ffi.NULL else None - - def textset_in(string: str) -> 'Set *': string_converted = string.encode('utf-8') result = _lib.textset_in(string_converted) @@ -986,13 +962,6 @@ def spanset_make(spans: 'List[Span *]', normalize: bool) -> 'SpanSet *': return result if result != _ffi.NULL else None -def spanset_make_exp(spans: 'Span *', count: int, maxcount: int, normalize: bool, ordered: bool) -> 'SpanSet *': - spans_converted = _ffi.cast('Span *', spans) - result = _lib.spanset_make_exp(spans_converted, count, maxcount, normalize, ordered) - _check_error() - return result if result != _ffi.NULL else None - - def textset_make(values: List[str]) -> 'Set *': values_converted = [cstring2text(x) for x in values] result = _lib.textset_make(values_converted, len(values)) @@ -1422,13 +1391,6 @@ def set_hash_extended(s: 'const Set *', seed: int) -> 'uint64': return result if result != _ffi.NULL else None -def set_mem_size(s: 'const Set *') -> 'int': - s_converted = _ffi.cast('const Set *', s) - result = _lib.set_mem_size(s_converted) - _check_error() - return result if result != _ffi.NULL else None - - def set_num_values(s: 'const Set *') -> 'int': s_converted = _ffi.cast('const Set *', s) result = _lib.set_num_values(s_converted) @@ -1508,13 +1470,6 @@ def spanset_lower_inc(ss: 'const SpanSet *') -> 'bool': return result if result != _ffi.NULL else None -def spanset_mem_size(ss: 'const SpanSet *') -> 'int': - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.spanset_mem_size(ss_converted) - _check_error() - return result if result != _ffi.NULL else None - - def spanset_num_spans(ss: 'const SpanSet *') -> 'int': ss_converted = _ffi.cast('const SpanSet *', ss) result = _lib.spanset_num_spans(ss_converted) @@ -1642,6 +1597,13 @@ def floatset_round(s: 'const Set *', maxdd: int) -> 'Set *': return result if result != _ffi.NULL else None +def floatspan_intspan(s: 'const Span *') -> 'Span *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.floatspan_intspan(s_converted) + _check_error() + return result if result != _ffi.NULL else None + + def floatspan_round(s: 'const Span *', maxdd: int) -> 'Span *': s_converted = _ffi.cast('const Span *', s) result = _lib.floatspan_round(s_converted, maxdd) @@ -1649,6 +1611,13 @@ def floatspan_round(s: 'const Span *', maxdd: int) -> 'Span *': return result if result != _ffi.NULL else None +def floatspanset_intspanset(ss: 'const SpanSet *') -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.floatspanset_intspanset(ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def floatspanset_round(ss: 'const SpanSet *', maxdd: int) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) result = _lib.floatspanset_round(ss_converted, maxdd) @@ -1663,20 +1632,16 @@ def geoset_round(s: 'const Set *', maxdd: int) -> 'Set *': return result if result != _ffi.NULL else None -def period_tprecision(s: 'const Span *', duration: 'const Interval *', torigin: int) -> 'Span *': +def intspan_floatspan(s: 'const Span *') -> 'Span *': s_converted = _ffi.cast('const Span *', s) - duration_converted = _ffi.cast('const Interval *', duration) - torigin_converted = _ffi.cast('TimestampTz', torigin) - result = _lib.period_tprecision(s_converted, duration_converted, torigin_converted) + result = _lib.intspan_floatspan(s_converted) _check_error() return result if result != _ffi.NULL else None -def periodset_tprecision(ss: 'const SpanSet *', duration: 'const Interval *', torigin: int) -> 'SpanSet *': +def intspanset_floatspanset(ss: 'const SpanSet *') -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - duration_converted = _ffi.cast('const Interval *', duration) - torigin_converted = _ffi.cast('TimestampTz', torigin) - result = _lib.periodset_tprecision(ss_converted, duration_converted, torigin_converted) + result = _lib.intspanset_floatspanset(ss_converted) _check_error() return result if result != _ffi.NULL else None @@ -1690,6 +1655,15 @@ def period_shift_tscale(p: 'const Span *', shift: "Optional['const Interval *']" return result if result != _ffi.NULL else None +def period_tprecision(s: 'const Span *', duration: 'const Interval *', torigin: int) -> 'Span *': + s_converted = _ffi.cast('const Span *', s) + duration_converted = _ffi.cast('const Interval *', duration) + torigin_converted = _ffi.cast('TimestampTz', torigin) + result = _lib.period_tprecision(s_converted, duration_converted, torigin_converted) + _check_error() + return result if result != _ffi.NULL else None + + def periodset_shift_tscale(ps: 'const SpanSet *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'SpanSet *': ps_converted = _ffi.cast('const SpanSet *', ps) shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL @@ -1699,6 +1673,15 @@ def periodset_shift_tscale(ps: 'const SpanSet *', shift: "Optional['const Interv return result if result != _ffi.NULL else None +def periodset_tprecision(ss: 'const SpanSet *', duration: 'const Interval *', torigin: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + duration_converted = _ffi.cast('const Interval *', duration) + torigin_converted = _ffi.cast('TimestampTz', torigin) + result = _lib.periodset_tprecision(ss_converted, duration_converted, torigin_converted) + _check_error() + return result if result != _ffi.NULL else None + + def timestamp_tprecision(t: int, duration: 'const Interval *', torigin: int) -> 'TimestampTz': t_converted = _ffi.cast('TimestampTz', t) duration_converted = _ffi.cast('const Interval *', duration) @@ -2548,6 +2531,17 @@ def intersection_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double' return None +def intersection_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'GSERIALIZED **': + s_converted = _ffi.cast('const Set *', s) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.intersection_geoset_geo(s_converted, gs_converted, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + def intersection_intset_int(s: 'const Set *', i: int) -> 'int': s_converted = _ffi.cast('const Set *', s) out_result = _ffi.new('int *') @@ -2762,6 +2756,25 @@ def minus_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': return result if result != _ffi.NULL else None +def minus_geo_geoset(gs: 'const GSERIALIZED *', s: 'const Set *') -> 'GSERIALIZED **': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.minus_geo_geoset(gs_converted, s_converted, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def minus_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.minus_geoset_geo(s_converted, gs_converted) + _check_error() + return result if result != _ffi.NULL else None + + def minus_int_intset(i: int, s: 'const Set *') -> 'int': s_converted = _ffi.cast('const Set *', s) out_result = _ffi.new('int *') @@ -2910,6 +2923,17 @@ def minus_timestamp_periodset(t: int, ss: 'const SpanSet *') -> int: return None +def minus_timestamp_timestampset(t: int, s: 'const Set *') -> int: + t_converted = _ffi.cast('TimestampTz', t) + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('TimestampTz *') + result = _lib.minus_timestamp_timestampset(t_converted, s_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + def minus_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('TimestampTz', t) @@ -2963,6 +2987,14 @@ def union_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': return result if result != _ffi.NULL else None +def union_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.union_geoset_geo(s_converted, gs_converted) + _check_error() + return result if result != _ffi.NULL else None + + def union_intset_int(s: 'const Set *', i: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) result = _lib.union_intset_int(s_converted, i) @@ -3048,6 +3080,37 @@ def union_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': return result if result != _ffi.NULL else None +def distance_bigintset_bigint(s: 'const Set *', i: int) -> 'double': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.distance_bigintset_bigint(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def distance_bigintspan_bigint(s: 'const Span *', i: int) -> 'double': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.distance_bigintspan_bigint(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def distance_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.distance_bigintspanset_bigint(ss_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def distance_floatset_float(s: 'const Set *', d: float) -> 'double': + s_converted = _ffi.cast('const Set *', s) + result = _lib.distance_floatset_float(s_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + def distance_floatspan_float(s: 'const Span *', d: float) -> 'double': s_converted = _ffi.cast('const Span *', s) result = _lib.distance_floatspan_float(s_converted, d) @@ -3055,6 +3118,20 @@ def distance_floatspan_float(s: 'const Span *', d: float) -> 'double': return result if result != _ffi.NULL else None +def distance_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.distance_floatspanset_float(ss_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + +def distance_intset_int(s: 'const Set *', i: int) -> 'double': + s_converted = _ffi.cast('const Set *', s) + result = _lib.distance_intset_int(s_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + def distance_intspan_int(s: 'const Span *', i: int) -> 'double': s_converted = _ffi.cast('const Span *', s) result = _lib.distance_intspan_int(s_converted, i) @@ -3062,26 +3139,33 @@ def distance_intspan_int(s: 'const Span *', i: int) -> 'double': return result if result != _ffi.NULL else None -def distance_set_set(s1: 'const Set *', s2: 'const Set *') -> 'double': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.distance_set_set(s1_converted, s2_converted) +def distance_intspanset_int(ss: 'const SpanSet *', i: int) -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.distance_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def distance_period_timestamp(p: 'const Span *', t: int) -> 'double': - p_converted = _ffi.cast('const Span *', p) +def distance_period_timestamp(s: 'const Span *', t: int) -> 'double': + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.distance_period_timestamp(p_converted, t_converted) + result = _lib.distance_period_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def distance_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'double': - ps_converted = _ffi.cast('const SpanSet *', ps) +def distance_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.distance_periodset_timestamp(ps_converted, t_converted) + result = _lib.distance_periodset_timestamp(ss_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def distance_set_set(s1: 'const Set *', s2: 'const Set *') -> 'double': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.distance_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None @@ -3110,10 +3194,10 @@ def distance_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> return result if result != _ffi.NULL else None -def distance_timestampset_timestamp(ts: 'const Set *', t: int) -> 'double': - ts_converted = _ffi.cast('const Set *', ts) +def distance_timestampset_timestamp(s: 'const Set *', t: int) -> 'double': + s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.distance_timestampset_timestamp(ts_converted, t_converted) + result = _lib.distance_timestampset_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None @@ -4804,14 +4888,6 @@ def tsequence_make(instants: 'const TInstant **', count: int, lower_inc: bool, u return result if result != _ffi.NULL else None -def tsequence_make_exp(instants: 'const TInstant **', count: int, maxcount: int, lower_inc: bool, upper_inc: bool, interp: 'interpType', normalize: bool) -> 'TSequence *': - instants_converted = [_ffi.cast('const TInstant *', x) for x in instants] - interp_converted = _ffi.cast('interpType', interp) - result = _lib.tsequence_make_exp(instants_converted, count, maxcount, lower_inc, upper_inc, interp_converted, normalize) - _check_error() - return result if result != _ffi.NULL else None - - def tsequenceset_make(sequences: 'const TSequence **', count: int, normalize: bool) -> 'TSequenceSet *': sequences_converted = [_ffi.cast('const TSequence *', x) for x in sequences] result = _lib.tsequenceset_make(sequences_converted, count, normalize) @@ -4819,13 +4895,6 @@ def tsequenceset_make(sequences: 'const TSequence **', count: int, normalize: bo return result if result != _ffi.NULL else None -def tsequenceset_make_exp(sequences: 'const TSequence **', count: int, maxcount: int, normalize: bool) -> 'TSequenceSet *': - sequences_converted = [_ffi.cast('const TSequence *', x) for x in sequences] - result = _lib.tsequenceset_make_exp(sequences_converted, count, maxcount, normalize) - _check_error() - return result if result != _ffi.NULL else None - - def tsequenceset_make_gaps(instants: 'const TInstant **', count: int, interp: 'interpType', maxt: 'Interval *', maxdist: float) -> 'TSequenceSet *': instants_converted = [_ffi.cast('const TInstant *', x) for x in instants] interp_converted = _ffi.cast('interpType', interp) @@ -5276,16 +5345,18 @@ def temporal_to_tinstant(temp: 'const Temporal *') -> 'Temporal *': return result if result != _ffi.NULL else None -def temporal_to_tsequence(temp: 'const Temporal *') -> 'Temporal *': +def temporal_to_tsequence(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.temporal_to_tsequence(temp_converted) + interp_converted = _ffi.cast('interpType', interp) + result = _lib.temporal_to_tsequence(temp_converted, interp_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_to_tsequenceset(temp: 'const Temporal *') -> 'Temporal *': +def temporal_to_tsequenceset(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.temporal_to_tsequenceset(temp_converted) + interp_converted = _ffi.cast('interpType', interp) + result = _lib.temporal_to_tsequenceset(temp_converted, interp_converted) _check_error() return result if result != _ffi.NULL else None @@ -6034,10 +6105,10 @@ def distance_tnumber_tnumber(temp1: 'const Temporal *', temp2: 'const Temporal * return result if result != _ffi.NULL else None -def distance_tpoint_geo(temp: 'const Temporal *', geo: 'const GSERIALIZED *') -> 'Temporal *': +def distance_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - geo_converted = _ffi.cast('const GSERIALIZED *', geo) - result = _lib.distance_tpoint_geo(temp_converted, geo_converted) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.distance_tpoint_point(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None @@ -6764,11 +6835,11 @@ def tne_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': return result if result != _ffi.NULL else None -def bearing_point_point(geo1: 'const GSERIALIZED *', geo2: 'const GSERIALIZED *') -> 'double': - geo1_converted = _ffi.cast('const GSERIALIZED *', geo1) - geo2_converted = _ffi.cast('const GSERIALIZED *', geo2) +def bearing_point_point(gs1: 'const GSERIALIZED *', gs2: 'const GSERIALIZED *') -> 'double': + gs1_converted = _ffi.cast('const GSERIALIZED *', gs1) + gs2_converted = _ffi.cast('const GSERIALIZED *', gs2) out_result = _ffi.new('double *') - result = _lib.bearing_point_point(geo1_converted, geo2_converted, out_result) + result = _lib.bearing_point_point(gs1_converted, gs2_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None @@ -6829,9 +6900,23 @@ def tpoint_direction(temp: 'const Temporal *') -> 'double': return None -def tpoint_get_coord(temp: 'const Temporal *', coord: int) -> 'Temporal *': +def tpoint_get_x(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tpoint_get_coord(temp_converted, coord) + result = _lib.tpoint_get_x(temp_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tpoint_get_y(temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tpoint_get_y(temp_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tpoint_get_z(temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tpoint_get_z(temp_converted) _check_error() return result if result != _ffi.NULL else None @@ -6886,16 +6971,23 @@ def geo_expand_space(gs: 'const GSERIALIZED *', d: float) -> 'STBox *': return result if result != _ffi.NULL else None -def tgeompoint_tgeogpoint(temp: 'const Temporal *', oper: bool) -> 'Temporal *': +def tpoint_expand_space(temp: 'const Temporal *', d: float) -> 'STBox *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgeompoint_tgeogpoint(temp_converted, oper) + result = _lib.tpoint_expand_space(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def tpoint_expand_space(temp: 'const Temporal *', d: float) -> 'STBox *': +def tgeompoint_to_tgeogpoint(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tpoint_expand_space(temp_converted, d) + result = _lib.tgeompoint_to_tgeogpoint(temp_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tgeogpoint_to_tgeompoint(temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tgeogpoint_to_tgeompoint(temp_converted) _check_error() return result if result != _ffi.NULL else None @@ -6923,10 +7015,10 @@ def tpoint_set_srid(temp: 'const Temporal *', srid: int) -> 'Temporal *': return result if result != _ffi.NULL else None -def econtains_geo_tpoint(geo: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'int': - geo_converted = _ffi.cast('const GSERIALIZED *', geo) +def econtains_geo_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'int': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.econtains_geo_tpoint(geo_converted, temp_converted) + result = _lib.econtains_geo_tpoint(gs_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None @@ -6995,10 +7087,10 @@ def tcontains_geo_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *', re return result if result != _ffi.NULL else None -def tdisjoint_tpoint_geo(temp: 'const Temporal *', geo: 'const GSERIALIZED *', restr: bool, atvalue: bool) -> 'Temporal *': +def tdisjoint_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *', restr: bool, atvalue: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - geo_converted = _ffi.cast('const GSERIALIZED *', geo) - result = _lib.tdisjoint_tpoint_geo(temp_converted, geo_converted, restr, atvalue) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.tdisjoint_tpoint_geo(temp_converted, gs_converted, restr, atvalue) _check_error() return result if result != _ffi.NULL else None @@ -7019,10 +7111,10 @@ def tdwithin_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *', return result if result != _ffi.NULL else None -def tintersects_tpoint_geo(temp: 'const Temporal *', geo: 'const GSERIALIZED *', restr: bool, atvalue: bool) -> 'Temporal *': +def tintersects_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *', restr: bool, atvalue: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - geo_converted = _ffi.cast('const GSERIALIZED *', geo) - result = _lib.tintersects_tpoint_geo(temp_converted, geo_converted, restr, atvalue) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.tintersects_tpoint_geo(temp_converted, gs_converted, restr, atvalue) _check_error() return result if result != _ffi.NULL else None @@ -7363,9 +7455,9 @@ def temporal_hausdorff_distance(temp1: 'const Temporal *', temp2: 'const Tempora return result if result != _ffi.NULL else None -def geo_to_tpoint(geo: 'const GSERIALIZED *') -> 'Temporal *': - geo_converted = _ffi.cast('const GSERIALIZED *', geo) - result = _lib.geo_to_tpoint(geo_converted) +def geo_to_tpoint(gs: 'const GSERIALIZED *') -> 'Temporal *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.geo_to_tpoint(gs_converted) _check_error() return result if result != _ffi.NULL else None @@ -7399,15 +7491,15 @@ def temporal_simplify_max_dist(temp: 'const Temporal *', eps_dist: float, synchr return result if result != _ffi.NULL else None -def tpoint_AsMVTGeom(temp: 'const Temporal *', bounds: 'const STBox *', extent: 'int32_t', buffer: 'int32_t', clip_geom: bool, geom: 'GSERIALIZED **', timesarr: 'int64 **') -> "Tuple['bool', 'int']": +def tpoint_AsMVTGeom(temp: 'const Temporal *', bounds: 'const STBox *', extent: 'int32_t', buffer: 'int32_t', clip_geom: bool, gsarr: 'GSERIALIZED **', timesarr: 'int64 **') -> "Tuple['bool', 'int']": temp_converted = _ffi.cast('const Temporal *', temp) bounds_converted = _ffi.cast('const STBox *', bounds) extent_converted = _ffi.cast('int32_t', extent) buffer_converted = _ffi.cast('int32_t', buffer) - geom_converted = [_ffi.cast('GSERIALIZED *', x) for x in geom] + gsarr_converted = [_ffi.cast('GSERIALIZED *', x) for x in gsarr] timesarr_converted = [_ffi.cast('int64 *', x) for x in timesarr] count = _ffi.new('int *') - result = _lib.tpoint_AsMVTGeom(temp_converted, bounds_converted, extent_converted, buffer_converted, clip_geom, geom_converted, timesarr_converted, count) + result = _lib.tpoint_AsMVTGeom(temp_converted, bounds_converted, extent_converted, buffer_converted, clip_geom, gsarr_converted, timesarr_converted, count) _check_error() return result if result != _ffi.NULL else None, count[0] From 4a60f0c17bd414d55cff63b2ece35d913b361c2f Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 7 Sep 2023 17:24:44 +0200 Subject: [PATCH 017/101] Synchronize with MEOS API --- pymeos/pymeos/boxes/stbox.py | 34 ++++++++---- pymeos/pymeos/boxes/tbox.py | 10 ++-- pymeos/tests/boxes/stbox_test.py | 40 ++++++++------ pymeos/tests/boxes/tbox_test.py | 23 +++----- pymeos/tests/main/ttext_test.py | 55 ++++++++++--------- pymeos_cffi/pymeos_cffi/__init__.py | 2 + .../builder/build_pymeos_functions.py | 6 +- pymeos_cffi/pymeos_cffi/builder/meos.h | 2 + pymeos_cffi/pymeos_cffi/functions.py | 18 ++++++ 9 files changed, 113 insertions(+), 77 deletions(-) diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index c346eadd..f08ade92 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -232,7 +232,8 @@ def from_tpoint(temporal: TPoint) -> STBox: def from_expanding_bounding_box(value: Union[Geometry, TPoint, STBox], expansion: float, geodetic: Optional[bool] = False) -> STBox: """ - Returns a `STBox` from a `Geometry`, `TPoint` or `STBox` instance, expanding its bounding box by the given amount. + Returns a `STBox` from a `Geometry`, `TPoint` or `STBox` instance, + expanding its bounding box by the given amount. Args: value: A `Geometry`, `TPoint` or `STBox` instance. @@ -540,6 +541,19 @@ def set_srid(self, value: int) -> STBox: return STBox(_inner=stbox_set_srid(self._inner, value)) # ------------------------- Transformations ------------------------------- + def get_space(self) -> STBox: + """ + Get the spatial dimension of ``self``, removing the temporal dimension if any + + Returns: + A new :class:`STBox` instance. + + MEOS Functions: + stbox_get_space + """ + result = stbox_get_space(self._inner) + return STBox(_inner=result) + def expand(self, other: Union[int, float, timedelta]) -> STBox: """ Expands ``self`` with `other`. @@ -575,10 +589,10 @@ def shift(self, delta: timedelta) -> STBox: A new :class:`STBox` instance MEOS Functions: - periodshift_tscale + stbox_shift_tscale See Also: - :meth:`Period.shift` + :meth:`STBox.shift` """ return self.shift_tscale(shift=delta) @@ -596,7 +610,7 @@ def tscale(self, duration: timedelta) -> STBox: period_shift_tscale See Also: - :meth:`Period.tscale` + :meth:`STBox.tscale` """ return self.shift_tscale(duration=duration) @@ -613,22 +627,20 @@ def shift_tscale(self, shift: Optional[timedelta] = None, A new :class:`STBox` instance MEOS Functions: - period_shift_tscale + stbox_shift_tscale See Also: :meth:`Period.shift_tscale` """ assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' - new_inner = stbox_copy(self._inner) - new_period = get_address(new_inner.period) - period_shift_tscale( - new_period, + result = stbox_shift_tscale( + self._inner, timedelta_to_interval(shift) if shift else None, timedelta_to_interval(duration) if duration else None ) - return STBox(_inner=new_inner) + return STBox(_inner=result) - def round(self, maxdd : int = 0) -> STBox: + def round(self, maxdd : Optional[int] = 0) -> STBox: """ Returns `self` rounded to the given number of decimal digits. diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index dcc5a831..8ca209df 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -511,14 +511,12 @@ def shift_tscale(self, shift: Optional[timedelta] = None, :meth:`Period.shift_tscale` """ assert shift is not None or duration is not None, 'shift and duration deltas must not be both None' - new_inner = tbox_copy(self._inner) - new_period = get_address(new_inner.period) - period_shift_tscale( - new_period, + result = tbox_shift_tscale( + self._inner, timedelta_to_interval(shift) if shift else None, - timedelta_to_interval(duration) if duration else None, + timedelta_to_interval(duration) if duration else None ) - return TBox(_inner=new_inner) + return TBox(_inner=result) def round(self, maxdd : int = 0) -> TBox: """ diff --git a/pymeos/tests/boxes/stbox_test.py b/pymeos/tests/boxes/stbox_test.py index 0233d07c..65121871 100644 --- a/pymeos/tests/boxes/stbox_test.py +++ b/pymeos/tests/boxes/stbox_test.py @@ -172,7 +172,7 @@ def test_from_tpoint_expand_space_constructor(self, tpoint, expected): ], ids=['STBox X', 'STBox Z', 'STBox XT', 'STBox ZT'] ) - def test_from_geo_expand_space_constructor(self, stbox, expected): + def test_from_stbox_expand_space_constructor(self, stbox, expected): stb = STBox.from_expanding_bounding_box(stbox, 1) assert isinstance(stb, STBox) assert str(stb) == expected @@ -516,6 +516,21 @@ class TestSTBoxTransformations(TestSTBox): stbxt = STBox('STBOX XT(((1,1),(2,2)),[2019-09-01,2019-09-02])') stbzt = STBox('STBOX ZT(((1,1,1),(2,2,2)),[2019-09-01,2019-09-02])') + @pytest.mark.parametrize( + 'stbox, expected', + [ + (stbx, STBox('STBOX X((1,1),(2,2))')), + (stbz, STBox('STBOX Z((1,1,1),(2,2,2))')), + (stbxt, STBox('STBOX X((1,1),(2,2))')), + (stbzt, STBox('STBOX Z((1,1,1),(2,2,2))')), + ], + ids=['STBox X', 'STBox Z', 'STBox XT', 'STBox ZT'] + ) + def test_get_space(self, stbox, expected): + stb = stbox.get_space() + assert isinstance(stb, STBox) + assert stb == expected + @pytest.mark.parametrize( 'stbox, expected', [ @@ -545,46 +560,37 @@ def test_expand_time(self, stbox, expected): assert isinstance(stb, STBox) assert stb == expected - ###################################### - # THIS TEST DOES NOT WORK CORRECTLY - ###################################### @pytest.mark.parametrize( 'stbox, delta, expected', [(stbt, timedelta(days=4), - STBox('STBOX T([2019-09-01,2019-09-02])')), + STBox('STBOX T([2019-09-05,2019-09-06])')), (stbt, timedelta(days=-4), - STBox('STBOX T([2019-09-01,2019-09-02])')), + STBox('STBOX T([2019-08-28,2019-08-29])')), (stbt, timedelta(hours=2), - STBox('STBOX T([2019-09-01,2019-09-02])')), + STBox('STBOX T([2019-09-01 02:00:00,2019-09-02 02:00:00])')), (stbt, timedelta(hours=-2), - STBox('STBOX T([2019-09-01,2019-09-02])')), + STBox('STBOX T([2019-08-31 22:00:00,2019-09-01 22:00:00])')), ], ids=['positive days', 'negative days', 'positive hours', 'negative hours'] ) def test_shift(self, stbox, delta, expected): assert stbox.shift(delta) == expected - ###################################### - # THIS TEST DOES NOT WORK CORRECTLY - ###################################### @pytest.mark.parametrize( 'stbox, delta, expected', [(stbt, timedelta(days=4), - STBox('STBOX T([2019-09-01,2019-09-02])')), + STBox('STBOX T([2019-09-01,2019-09-05])')), (stbt, timedelta(hours=2), - STBox('STBOX T([2019-09-01,2019-09-02])')), + STBox('STBOX T([2019-09-01,2019-09-01 02:00:00])')), ], ids=['positive days', 'positive hours'] ) def test_tscale(self, stbox, delta, expected): assert stbox.tscale(delta) == expected - ###################################### - # THIS TEST DOES NOT WORK CORRECTLY - ###################################### def test_shift_tscale(self): assert self.stbt.shift_tscale(timedelta(days=4), timedelta(hours=4)) == \ - STBox('STBOX T([2019-09-01,2019-09-02])') + STBox('STBOX T([2019-09-05,2019-09-05 04:00:00])') @pytest.mark.parametrize( 'stbox, expected', diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index fa10a2b4..182d7ea1 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -377,46 +377,37 @@ def test_expand_time(self, tbox, expected): assert isinstance(tb, TBox) assert tb == expected - ##################################### - ## THIS TEST DOES NOT WORK CORRECTLY - ##################################### @pytest.mark.parametrize( 'tbox, delta, expected', [(tbt, timedelta(days=4), - TBox('TBOX T([2019-09-01,2019-09-02])')), + TBox('TBOX T([2019-09-05,2019-09-06])')), (tbt, timedelta(days=-4), - TBox('TBOX T([2019-09-01,2019-09-02])')), + TBox('TBOX T([2019-08-28,2019-08-29])')), (tbt, timedelta(hours=2), - TBox('TBOX T([2019-09-01,2019-09-02])')), + TBox('TBOX T([2019-09-01 02:00:00,2019-09-02 02:00:00])')), (tbt, timedelta(hours=-2), - TBox('TBOX T([2019-09-01,2019-09-02])')), + TBox('TBOX T([2019-08-31 22:00:00,2019-09-01 22:00:00])')), ], ids=['positive days', 'negative days', 'positive hours', 'negative hours'] ) def test_shift(self, tbox, delta, expected): assert tbox.shift(delta) == expected - ##################################### - ## THIS TEST DOES NOT WORK CORRECTLY - ##################################### @pytest.mark.parametrize( 'tbox, delta, expected', [(tbt, timedelta(days=4), - TBox('TBOX T([2019-09-01,2019-09-02])')), + TBox('TBOX T([2019-09-01,2019-09-05])')), (tbt, timedelta(hours=2), - TBox('TBOX T([2019-09-01,2019-09-02])')), + TBox('TBOX T([2019-09-01,2019-09-01 02:00:00])')), ], ids=['positive days', 'positive hours'] ) def test_tscale(self, tbox, delta, expected): assert tbox.tscale(delta) == expected - ##################################### - ## THIS TEST DOES NOT WORK CORRECTLY - ##################################### def test_shift_tscale(self): assert self.tbt.shift_tscale(timedelta(days=4), timedelta(hours=4)) == \ - TBox('TBOX T([2019-09-01,2019-09-02])') + TBox('TBOX T([2019-09-05,2019-09-05 04:00:00])') @pytest.mark.parametrize( 'tbox, expected', diff --git a/pymeos/tests/main/ttext_test.py b/pymeos/tests/main/ttext_test.py index af88debb..3bba2f95 100644 --- a/pymeos/tests/main/ttext_test.py +++ b/pymeos/tests/main/ttext_test.py @@ -1047,7 +1047,7 @@ class TestTTextRestrictors(TestTText): (tti, period_set, TTextInst('AAA@2019-09-01')), (tti, 'AAA', TTextInst('AAA@2019-09-01')), (tti, 'BBB', None), - # (tti, ['AAA', 'BBB'], tti), + (tti, ['AAA', 'BBB'], tti), (ttds, timestamp, TTextSeq('{AAA@2019-09-01}')), (ttds, timestamp_set, TTextSeq('{AAA@2019-09-01}')), @@ -1055,7 +1055,7 @@ class TestTTextRestrictors(TestTText): (ttds, period_set, TTextSeq('{AAA@2019-09-01, BBB@2019-09-02}')), (ttds, 'AAA', TTextSeq('{AAA@2019-09-01}')), (ttds, 'BBB', TTextSeq('{BBB@2019-09-02}')), - # (ttds, ['AAA', 'BBB'], ttds), + (ttds, ['AAA', 'BBB'], ttds), (tts, timestamp, TTextSeq('[AAA@2019-09-01]')), (tts, timestamp_set, TTextSeq('{AAA@2019-09-01}')), @@ -1063,7 +1063,7 @@ class TestTTextRestrictors(TestTText): (tts, period_set, TTextSeq('[AAA@2019-09-01, BBB@2019-09-02]')), (tts, 'AAA', TTextSeq('[AAA@2019-09-01, AAA@2019-09-02)')), (tts, 'BBB', TTextSeq('[BBB@2019-09-02]')), - # (tts, ['AAA', 'BBB'], tts), + (tts, ['AAA', 'BBB'], tts), (ttss, timestamp, TTextSeqSet('[AAA@2019-09-01]')), (ttss, timestamp_set, TTextSeq('{AAA@2019-09-01, AAA@2019-09-03}')), @@ -1072,21 +1072,21 @@ class TestTTextRestrictors(TestTText): TTextSeqSet('{[AAA@2019-09-01, BBB@2019-09-02],[AAA@2019-09-03, AAA@2019-09-05]}')), (ttss, 'AAA', TTextSeqSet('{[AAA@2019-09-01, AAA@2019-09-02),[AAA@2019-09-03, AAA@2019-09-05]}')), (ttss, 'BBB', TTextSeqSet('{[BBB@2019-09-02]}')), - # (ttss, ['AAA', 'BBB'], ttss) + (ttss, ['AAA', 'BBB'], ttss) ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', 'Instant-PeriodSet', 'Instant-AAA', 'Instant-BBB', - # 'Instant-[AAA,BBB]', + 'Instant-[AAA,BBB]', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', 'Discrete Sequence-AAA', 'Discrete Sequence-BBB', - # 'Discrete Sequence-[AAA,BBB]', + 'Discrete Sequence-[AAA,BBB]', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', 'Sequence-PeriodSet', 'Sequence-AAA', 'Sequence-BBB', - # 'Sequence-[AAA,BBB]', + 'Sequence-[AAA,BBB]', 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', 'SequenceSet-PeriodSet', 'SequenceSet-AAA', 'SequenceSet-BBB', - # 'SequenceSet-[AAA,BBB]' + 'SequenceSet-[AAA,BBB]' ] ) def test_at(self, temporal, restrictor, expected): @@ -1128,7 +1128,7 @@ def test_at_min(self, temporal, expected): (tti, period_set, None), (tti, 'AAA', None), (tti, 'BBB', TTextInst('AAA@2019-09-01')), - # (tti, ['AAA', 'BBB'], None), + (tti, ['AAA', 'BBB'], None), (ttds, timestamp, TTextSeq('{BBB@2019-09-02}')), (ttds, timestamp_set, TTextSeq('{BBB@2019-09-02}')), @@ -1136,7 +1136,7 @@ def test_at_min(self, temporal, expected): (ttds, period_set, None), (ttds, 'AAA', TTextSeq('{BBB@2019-09-02}')), (ttds, 'BBB', TTextSeq('{AAA@2019-09-01}')), - # (ttds, ['AAA', 'BBB'], TTextSeq('{BBB@2019-09-02}')), + (ttds, ['AAA', 'BBB'], None), (tts, timestamp, TTextSeqSet('{(AAA@2019-09-01, BBB@2019-09-02]}')), (tts, timestamp_set, TTextSeqSet('{(AAA@2019-09-01, BBB@2019-09-02]}')), @@ -1144,7 +1144,7 @@ def test_at_min(self, temporal, expected): (tts, period_set, None), (tts, 'AAA', TTextSeqSet('{[BBB@2019-09-02]}')), (tts, 'BBB', TTextSeqSet('{[AAA@2019-09-01, AAA@2019-09-02)}')), - # (tts, ['AAA', 'BBB'], TTextSeq('[BBB@2019-09-02]')), + (tts, ['AAA', 'BBB'], None), (ttss, timestamp, TTextSeqSet('{(AAA@2019-09-01, BBB@2019-09-02],[AAA@2019-09-03, AAA@2019-09-05]}')), @@ -1154,22 +1154,27 @@ def test_at_min(self, temporal, expected): (ttss, period_set, None), (ttss, 'AAA', TTextSeqSet('{[BBB@2019-09-02]}')), (ttss, 'BBB', TTextSeqSet('{[AAA@2019-09-01, AAA@2019-09-02),[AAA@2019-09-03, AAA@2019-09-05]}')), - # (ttss, ['AAA', 'BBB'], TTextSeqSet('{[BBB@2019-09-02]}')), + (ttss, ['AAA', 'BBB'], None), ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', 'Instant-AAA', 'Instant-BBB', # 'Instant-[AAA,BBB]', + 'Instant-PeriodSet', 'Instant-AAA', 'Instant-BBB', 'Instant-[AAA,BBB]', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', - 'Discrete Sequence-AAA', 'Discrete Sequence-BBB', # 'Discrete Sequence-[AAA,BBB]', + 'Discrete Sequence-AAA', 'Discrete Sequence-BBB', + 'Discrete Sequence-[AAA,BBB]', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', 'Sequence-AAA', 'Sequence-BBB', # 'Sequence-[AAA,BBB]', + 'Sequence-PeriodSet', 'Sequence-AAA', 'Sequence-BBB', + 'Sequence-[AAA,BBB]', 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', 'SequenceSet-PeriodSet', 'SequenceSet-AAA', 'SequenceSet-BBB', - # 'SequenceSet-[AAA,BBB]' + 'SequenceSet-[AAA,BBB]' ] ) def test_minus(self, temporal, restrictor, expected): - assert temporal.minus(restrictor) == expected + if expected is None: + assert temporal.minus(restrictor) is None + else: + assert temporal.minus(restrictor) == expected @pytest.mark.parametrize( 'temporal, expected', @@ -1206,7 +1211,7 @@ def test_minus_max(self, temporal, expected): (tti, period_set), (tti, 'AAA'), (tti, 'BBB'), - # (tti, ['AAA','BBB']), + (tti, ['AAA','BBB']), (ttds, timestamp), (ttds, timestamp_set), @@ -1214,7 +1219,7 @@ def test_minus_max(self, temporal, expected): (ttds, period_set), (ttds, 'AAA'), (ttds, 'BBB'), - # (ttds, ['AAA','BBB']), + (ttds, ['AAA','BBB']), (tts, timestamp), (tts, timestamp_set), @@ -1222,7 +1227,7 @@ def test_minus_max(self, temporal, expected): (tts, period_set), (tts, 'AAA'), (tts, 'BBB'), - # (tts, ['AAA','BBB']), + (tts, ['AAA','BBB']), (ttss, timestamp), (ttss, timestamp_set), @@ -1230,18 +1235,18 @@ def test_minus_max(self, temporal, expected): (ttss, period_set), (ttss, 'AAA'), (ttss, 'BBB'), - # (ttss, ['AAA','BBB']), + (ttss, ['AAA','BBB']), ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', 'Instant-AAA', 'Instant-BBB', # 'Instant-[AAA,BBB]', + 'Instant-PeriodSet', 'Instant-AAA', 'Instant-BBB', 'Instant-[AAA,BBB]', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', - 'Discrete Sequence-AAA', 'Discrete Sequence-BBB', # 'Discrete Sequence-[AAA,BBB]', + 'Discrete Sequence-AAA', 'Discrete Sequence-BBB', 'Discrete Sequence-[AAA,BBB]', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', 'Sequence-AAA', 'Sequence-BBB', # 'Sequence-[AAA,BBB]', + 'Sequence-PeriodSet', 'Sequence-AAA', 'Sequence-BBB', 'Sequence-[AAA,BBB]', 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', 'SequenceSet-PeriodSet', 'SequenceSet-AAA', 'SequenceSet-BBB', - # 'SequenceSet-[AAA,BBB]' + 'SequenceSet-[AAA,BBB]' ] ) def test_at_minus(self, temporal, restrictor): diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 6dbeb318..f9272ef5 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -554,9 +554,11 @@ 'stbox_get_space', 'stbox_round', 'stbox_set_srid', + 'stbox_shift_tscale', 'tbox_expand_value', 'tbox_expand_time', 'tbox_round', + 'tbox_shift_tscale', 'contains_tbox_tbox', 'contained_tbox_tbox', 'overlaps_tbox_tbox', diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index 2d8ca1ce..a912bc78 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -117,8 +117,6 @@ def __init__(self, ctype: str, ptype: str, conversion: Optional[str]) -> None: ('gserialized_as_geojson', 'srs'), ('period_shift_tscale', 'shift'), ('period_shift_tscale', 'duration'), - ('period_shift_tscale', 'delta'), - ('period_shift_tscale', 'scale'), ('timestampset_shift_tscale', 'shift'), ('timestampset_shift_tscale', 'duration'), ('periodset_shift_tscale', 'shift'), @@ -129,12 +127,16 @@ def __init__(self, ctype: str, ptype: str, conversion: Optional[str]) -> None: ('tbox_make', 'p'), ('tbox_make', 's'), ('stbox_make', 'p'), + ('stbox_shift_tscale', 'shift'), + ('stbox_shift_tscale', 'duration'), ('temporal_tcount_transfn', 'state'), ('temporal_extent_transfn', 'p'), ('tnumber_extent_transfn', 'box'), ('tpoint_extent_transfn', 'box'), ('tbool_tand_transfn', 'state'), ('tbool_tor_transfn', 'state'), + ('tbox_shift_tscale', 'shift'), + ('tbox_shift_tscale', 'duration'), ('tint_tmin_transfn', 'state'), ('tfloat_tmin_transfn', 'state'), ('tint_tmax_transfn', 'state'), diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index da956599..9058cc3a 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -1335,9 +1335,11 @@ extern STBox *stbox_expand_time(const STBox *box, const Interval *interval); extern STBox *stbox_get_space(const STBox *box); extern STBox *stbox_round(const STBox *box, int maxdd); extern STBox *stbox_set_srid(const STBox *box, int32 srid); +extern STBox *stbox_shift_tscale(const STBox *box, const Interval *shift, const Interval *duration); extern TBox *tbox_expand_value(const TBox *box, const double d); extern TBox *tbox_expand_time(const TBox *box, const Interval *interval); extern TBox *tbox_round(const TBox *box, int maxdd); +extern TBox *tbox_shift_tscale(const TBox *box, const Interval *shift, const Interval *duration); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index ab0afc09..42b17c2c 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -4108,6 +4108,15 @@ def stbox_set_srid(box: 'const STBox *', srid: int) -> 'STBox *': return result if result != _ffi.NULL else None +def stbox_shift_tscale(box: 'const STBox *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'STBox *': + box_converted = _ffi.cast('const STBox *', box) + shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL + duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL + result = _lib.stbox_shift_tscale(box_converted, shift_converted, duration_converted) + _check_error() + return result if result != _ffi.NULL else None + + def tbox_expand_value(box: 'const TBox *', d: 'const double') -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) d_converted = _ffi.cast('const double', d) @@ -4131,6 +4140,15 @@ def tbox_round(box: 'const TBox *', maxdd: int) -> 'TBox *': return result if result != _ffi.NULL else None +def tbox_shift_tscale(box: 'const TBox *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'TBox *': + box_converted = _ffi.cast('const TBox *', box) + shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL + duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL + result = _lib.tbox_shift_tscale(box_converted, shift_converted, duration_converted) + _check_error() + return result if result != _ffi.NULL else None + + def contains_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'bool': box1_converted = _ffi.cast('const TBox *', box1) box2_converted = _ffi.cast('const TBox *', box2) From e83adc2846b85eb9c04f505ed36e01403d0cf106 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Fri, 8 Sep 2023 07:49:12 +0200 Subject: [PATCH 018/101] Add base classes for PyMEOS collections. --- pymeos/pymeos/collections/__init__.py | 0 pymeos/pymeos/collections/base/__init__.py | 0 pymeos/pymeos/collections/base/set.py | 19 + pymeos/pymeos/collections/base/span.py | 690 ++++++++++++++ pymeos/pymeos/collections/base/spanset.py | 19 + pymeos/pymeos/collections/time/__init__.py | 0 pymeos/pymeos/collections/time/period.py | 870 ++++++++++++++++++ pymeos/pymeos/collections/time/periodset.py | 7 + pymeos/pymeos/collections/time/time.py | 16 + .../pymeos/collections/time/timestampset.py | 7 + 10 files changed, 1628 insertions(+) create mode 100644 pymeos/pymeos/collections/__init__.py create mode 100644 pymeos/pymeos/collections/base/__init__.py create mode 100644 pymeos/pymeos/collections/base/set.py create mode 100644 pymeos/pymeos/collections/base/span.py create mode 100644 pymeos/pymeos/collections/base/spanset.py create mode 100644 pymeos/pymeos/collections/time/__init__.py create mode 100644 pymeos/pymeos/collections/time/period.py create mode 100644 pymeos/pymeos/collections/time/periodset.py create mode 100644 pymeos/pymeos/collections/time/time.py create mode 100644 pymeos/pymeos/collections/time/timestampset.py diff --git a/pymeos/pymeos/collections/__init__.py b/pymeos/pymeos/collections/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymeos/pymeos/collections/base/__init__.py b/pymeos/pymeos/collections/base/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py new file mode 100644 index 00000000..35850a65 --- /dev/null +++ b/pymeos/pymeos/collections/base/set.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from datetime import datetime, timedelta +from typing import Generic, TypeVar, Type, Callable, Any +from typing import Optional, Union, overload, get_args + +from pymeos_cffi import * + +T = TypeVar('T') +Self = TypeVar('Self', bound='Span[Any]') + + +class Set(Generic[T], ABC): + """ + Base class for all set classes. + """ + + __slots__ = ['_inner'] diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py new file mode 100644 index 00000000..175416b2 --- /dev/null +++ b/pymeos/pymeos/collections/base/span.py @@ -0,0 +1,690 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from datetime import datetime, timedelta +from typing import Generic, TypeVar, Type, Callable, Any, TYPE_CHECKING +from typing import Optional, Union, overload, get_args + +from pymeos_cffi import * + +if TYPE_CHECKING: + from .set import Set + from .spanset import SpanSet + +T = TypeVar('T') +Self = TypeVar('Self', bound='Span[Any]') + + +class Span(Generic[T], ABC): + """ + Base class for all span classes. + """ + + __slots__ = ['_inner'] + + _parse_function: Callable[[str], 'CData'] = None + _parse_value_function: Callable[[Union[str, T]], Any] = None + _make_function: Callable[[Any, Any, bool, bool], 'CData'] = None + + # ------------------------- Constructors ---------------------------------- + def __init__(self, string: Optional[str] = None, *, + lower: Optional[Union[str, T]] = None, + upper: Optional[Union[str, T]] = None, + lower_inc: bool = True, + upper_inc: bool = False, + _inner=None): + super().__init__() + assert (_inner is not None) or ((string is not None) != (lower is not None and upper is not None)), \ + "Either string must be not None or both lower and upper must be not" + if _inner is not None: + self._inner = _inner + elif string is not None: + self._inner = self.__class__._parse_function(string) + else: + lower_converted = self.__class__._parse_value_function(lower) + upper_converted = self.__class__._parse_value_function(upper) + self._inner = self.__class__._make_function(lower_converted, upper_converted, lower_inc, upper_inc) + + def __copy__(self: Self) -> Self: + """ + Return a copy of ``self``. + + Returns: + A new :class:`Span` instance + + MEOS Functions: + span_copy + """ + inner_copy = span_copy(self._inner) + return self._class__(_inner=inner_copy) + + @classmethod + def from_wkb(cls: Type[Self], wkb: bytes) -> Self: + """ + Returns a `Period` from its WKB representation. + + Args: + wkb: The WKB string. + + Returns: + A new :class:`Period` instance + + MEOS Functions: + span_from_wkb + """ + result = span_from_wkb(wkb) + return cls(_inner=result) + + @classmethod + def from_hexwkb(cls: Type[Self], hexwkb: str) -> Self: + """ + Returns a `Period` from its WKB representation in hex-encoded ASCII. + + Args: + hexwkb: WKB representation in hex-encoded ASCII + + Returns: + A new :class:`Period` instance + + MEOS Functions: + span_from_hexwkb + """ + result = span_from_hexwkb(hexwkb) + return cls(_inner=result) + + # ------------------------- Output ---------------------------------------- + @abstractmethod + def __str__(self) -> str: + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + """ + raise NotImplementedError() + + def __repr__(self): + """ + Return the string representation of ``self``. + + Returns: + A new :class:`str` instance + """ + return (f'{self.__class__.__name__}' + f'({self})') + + def as_wkb(self) -> bytes: + """ + Returns the WKB representation of ``self``. + + Returns: + A :class:`str` object with the WKB representation of ``self``. + + MEOS Functions: + span_as_wkb + """ + return span_as_wkb(self._inner, 4) + + def as_hexwkb(self) -> str: + """ + Returns the WKB representation of ``self`` in hex-encoded ASCII. + + Returns: + A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. + + MEOS Functions: + span_as_hexwkb + """ + return span_as_hexwkb(self._inner, -1)[0] + + # ------------------------- Conversions ----------------------------------- + @abstractmethod + def to_spanset(self) -> SpanSet: + """ + Returns a period set containing ``self``. + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + span_to_spanset + """ + return span_to_spanset(self._inner) + + # ------------------------- Accessors ------------------------------------- + def lower(self) -> T: + """ + Returns the lower bound of a period + + Returns: + The lower bound of the period as a :class:`datetime.datetime` + + MEOS Functions: + period_lower + """ + return period_lower(self._inner) + + def upper(self) -> T: + """ + Returns the upper bound of a period + + Returns: + The upper bound of the period as a :class:`datetime.datetime` + + MEOS Functions: + period_upper + """ + return timestamptz_to_datetime(period_upper(self._inner)) + + def lower_inc(self) -> bool: + """ + Returns whether the lower bound of the period is inclusive or not + + Returns: + True if the lower bound of the period is inclusive and False otherwise + + MEOS Functions: + span_lower_inc + """ + return span_lower_inc(self._inner) + + def upper_inc(self) -> bool: + """ + Returns whether the upper bound of the period is inclusive or not + + Returns: + True if the upper bound of the period is inclusive and False otherwise + + MEOS Functions: + span_upper_inc + """ + return span_upper_inc(self._inner) + + def width(self) -> float: + """ + Returns the duration of the period. + + Returns: + Returns a `float` representing the duration of the period in seconds + + MEOS Functions: + span_width + """ + return span_width(self._inner) + + def __hash__(self) -> int: + """ + Return the hash representation of ``self``. + + Returns: + A new :class:`int` instance + + MEOS Functions: + span_hash + """ + return span_hash(self._inner) + + # ------------------------- Topological Operations ------------------------ + @abstractmethod + def is_adjacent(self: Self, other) -> bool: + """ + Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them + contains it. + + Args: + other: object to compare with + + Returns: + True if adjacent, False otherwise + + MEOS Functions: + adjacent_span_span, adjacent_span_spanset, + """ + if isinstance(other, Set): + return adjacent_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Span): + return adjacent_span_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return adjacent_spanset_span(other._inner, self._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_contained_in(self, container) -> bool: + """ + Returns whether ``self`` is contained in ``container``. + + Args: + container: temporal object to compare with + + Returns: + True if contained, False otherwise + + MEOS Functions: + contained_span_span, contained_span_spanset, contained_period_temporal + """ + if isinstance(container, Span): + return contained_span_span(self._inner, container._inner) + elif isinstance(container, SpanSet): + return contained_span_spanset(self._inner, container._inner) + else: + raise TypeError(f'Operation not supported with type {container.__class__}') + + def contains(self, content) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_span_span, contains_span_spanset, contains_period_timestamp, + contains_period_timestampset, contains_period_temporal + """ + if isinstance(content, Set): + return contains_span_span(self._inner, set_span(content._inner)) + elif isinstance(content, Span): + return contains_span_span(self._inner, content._inner) + elif isinstance(content, SpanSet): + return contains_span_spanset(self._inner, content._inner) + else: + raise TypeError(f'Operation not supported with type {content.__class__}') + + def __contains__(self, item): + """ + Return whether ``self`` contains ``item``. + + Args: + item: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_span_span, contains_span_spanset, contains_period_timestamp, + contains_period_timestampset, contains_period_temporal + """ + return self.contains(item) + + def overlaps(self, other) -> bool: + """ + Returns whether ``self`` overlaps ``other``. That is, both share at least an element. + + Args: + other: temporal object to compare with + + Returns: + True if overlaps, False otherwise + + MEOS Functions: + overlaps_span_span, overlaps_span_spanset + """ + if isinstance(other, Set): + return overlaps_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Span): + return overlaps_span_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return overlaps_spanset_span(other._inner, self._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_same(self, other) -> bool: + """ + Returns whether ``self`` and the bounding period of ``other`` is the same. + + Args: + other: temporal object to compare with + + Returns: + True if equal, False otherwise + + MEOS Functions: + same_period_temporal + """ + if isinstance(other, Set): + return span_eq(self._inner, set_span(other._inner)) + elif isinstance(other, Span): + return span_eq(self._inner, other._inner) + elif isinstance(other, SpanSet): + return span_eq(self._inner, spanset_span(other._inner)) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Position Operations --------------------------- + def is_before(self, other) -> bool: + """ + Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + left_span_span, left_span_spanset + """ + if isinstance(other, Set): + return left_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Span): + return left_span_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return left_span_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_over_or_before(self, other) -> bool: + """ + Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or + at the same time). + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overleft_span_span, overleft_span_spanset + """ + if isinstance(other, Set): + return overleft_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Span): + return overleft_span_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return overleft_span_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_after(self, other) -> bool: + """ + Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. + + Args: + other: temporal object to compare with + + Returns: + True if after, False otherwise + + MEOS Functions: + right_span_span, right_span_spanset + """ + if isinstance(other, Set): + return right_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Span): + return right_span_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return right_span_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_over_or_after(self, other) -> bool: + """ + Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts + (or at the same time). + + Args: + other: temporal object to compare with + + Returns: + True if overlapping or after, False otherwise + + MEOS Functions: + overright_span_span, overright_span_spanset, overafter_period_timestamp, + overafter_period_timestampset, overafter_period_temporal + """ + if isinstance(other, Set): + return overright_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Span): + return overright_span_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return overright_span_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Distance Operations --------------------------- + def distance(self, other) -> timedelta: + """ + Returns the temporal distance between ``self`` and ``other``. + + Args: + other: temporal object to compare with + + Returns: + A :class:`datetime.timedelta` instance + + MEOS Functions: + distance_span_span, distance_spanset_span, distance_period_timestamp + """ + if isinstance(other, Set): + return timedelta(seconds=distance_span_span(self._inner, set_span(other._inner))) + elif isinstance(other, Span): + return timedelta(seconds=distance_span_span(self._inner, other._inner)) + elif isinstance(other, SpanSet): + return timedelta(seconds=distance_spanset_span(other._inner, self._inner)) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Set Operations -------------------------------- + def intersection(self, other): + """ + Returns the temporal intersection of ``self`` and ``other``. + + Args: + other: temporal object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_span_span, intersection_spanset_span, intersection_period_timestamp + """ + if isinstance(other, Set): + result = intersection_spanset_span(set_to_spanset(other._inner), self._inner) + return result if result is not None else None + elif isinstance(other, Span): + result = intersection_span_span(self._inner, other._inner) + return result if result is not None else None + elif isinstance(other, SpanSet): + result = intersection_spanset_span(other._inner, self._inner) + return result if result is not None else None + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __mul__(self, other): + """ + Returns the temporal intersection of ``self`` and ``other``. + + Args: + other: temporal object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_span_span, intersection_spanset_span, intersection_period_timestamp + """ + return self.intersection(other) + + @abstractmethod + def minus(self, other): + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + minus_period_timestamp, minus_span_spanset, minus_span_span + """ + if isinstance(other, Set): + result = minus_span_spanset(self._inner, set_to_spanset(other._inner)) + return result if result is not None else None + elif isinstance(other, Span): + result = minus_span_span(self._inner, other._inner) + return result if result is not None else None + elif isinstance(other, SpanSet): + result = minus_span_spanset(self._inner, other._inner) + return result if result is not None else None + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __sub__(self, other): + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + minus_period_timestamp, minus_span_spanset, minus_span_span + """ + return self.minus(other) + + def union(self, other): + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + union_period_timestamp, union_spanset_span, union_span_span + """ + if isinstance(other, Set): + return union_spanset_span(set_to_spanset(other._inner), self._inner) + elif isinstance(other, Span): + return union_span_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return union_spanset_span(other._inner, self._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __add__(self, other): + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + union_period_timestamp, union_spanset_span, union_span_span + """ + return self.union(other) + + # ------------------------- Comparisons ----------------------------------- + def __eq__(self, other): + """ + Return whether ``self`` and ``other`` are equal. + + Args: + other: temporal object to compare with + + Returns: + True if equal, False otherwise + + MEOS Functions: + span_eq + """ + if isinstance(other, self.__class__): + return span_eq(self._inner, other._inner) + return False + + def __ne__(self, other): + """ + Return whether ``self`` and ``other`` are not equal. + + Args: + other: temporal object to compare with + + Returns: + True if not equal, False otherwise + + MEOS Functions: + span_neq + """ + if isinstance(other, self.__class__): + return span_ne(self._inner, other._inner) + return True + + def __lt__(self, other): + """ + Return whether ``self`` is less than ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if less than, False otherwise + + MEOS Functions: + span_lt + """ + if isinstance(other, self.__class__): + return span_lt(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __le__(self, other): + """ + Return whether ``self`` is less than or equal to ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if less than or equal, False otherwise + + MEOS Functions: + span_le + """ + if isinstance(other, self.__class__): + return span_le(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __gt__(self, other): + """ + Return whether ``self`` is greater than ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if greater than, False otherwise + + MEOS Functions: + span_gt + """ + if isinstance(other, self.__class__): + return span_gt(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __ge__(self, other): + """ + Return whether ``self`` is greater than or equal to ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if greater than or equal, False otherwise + + MEOS Functions: + span_ge + """ + if isinstance(other, self.__class__): + return span_ge(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') diff --git a/pymeos/pymeos/collections/base/spanset.py b/pymeos/pymeos/collections/base/spanset.py new file mode 100644 index 00000000..19759b53 --- /dev/null +++ b/pymeos/pymeos/collections/base/spanset.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from datetime import datetime, timedelta +from typing import Generic, TypeVar, Type, Callable, Any +from typing import Optional, Union, overload, get_args + +from pymeos_cffi import * + +T = TypeVar('T') +Self = TypeVar('Self', bound='Span[Any]') + + +class SpanSet(Generic[T], ABC): + """ + Base class for all spanset classes. + """ + + __slots__ = ['_inner'] diff --git a/pymeos/pymeos/collections/time/__init__.py b/pymeos/pymeos/collections/time/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymeos/pymeos/collections/time/period.py b/pymeos/pymeos/collections/time/period.py new file mode 100644 index 00000000..3d8ceb52 --- /dev/null +++ b/pymeos/pymeos/collections/time/period.py @@ -0,0 +1,870 @@ +from __future__ import annotations + +from datetime import datetime, timedelta +from typing import Optional, Union, overload, TYPE_CHECKING, get_args + +from dateutil.parser import parse +from pymeos_cffi import * + +from ..base.span import Span +from ..base.spanset import SpanSet + +if TYPE_CHECKING: + from ...temporal import Temporal + from ...boxes import Box + from .periodset import PeriodSet + from .time import Time + + +class Period(Span[datetime]): + """ + Class for representing sets of contiguous timestamps between a lower and + an upper bound. The bounds may be inclusive or not. + + ``Period`` objects can be created with a single argument of type string + as in MobilityDB. + + >>> Period('(2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01)') + + Another possibility is to provide the ``lower`` and ``upper`` named parameters (of type str or datetime), and + optionally indicate whether the bounds are inclusive or exclusive (by default, the lower bound is inclusive and the + upper is exclusive): + + >>> Period(lower='2019-09-08 00:00:00+01', upper='2019-09-10 00:00:00+01') + >>> Period(lower='2019-09-08 00:00:00+01', upper='2019-09-10 00:00:00+01', lower_inc=False, upper_inc=True) + >>> Period(lower=parse('2019-09-08 00:00:00+01'), upper=parse('2019-09-10 00:00:00+01'), upper_inc=True) + """ + + __slots__ = ['_inner'] + + _parse_function = period_in + _parse_value_function = lambda x: pg_timestamptz_in(x, -1) if isinstance(x, str) else datetime_to_timestamptz(x) + _make_function = period_make + + # ------------------------- Constructors ---------------------------------- + + # ------------------------- Output ---------------------------------------- + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + period_out + """ + return period_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + + def to_spanset(self) -> PeriodSet: + """ + Returns a period set containing ``self``. + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + span_to_spanset + """ + from .periodset import PeriodSet + return PeriodSet(_inner=super().to_spanset()) + + def to_periodset(self) -> PeriodSet: + """ + Returns a period set containing ``self``. + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + span_to_spanset + """ + return self.to_spanset() + + # ------------------------- Accessors ------------------------------------- + def lower(self) -> datetime: + """ + Returns the lower bound of a period + + Returns: + The lower bound of the period as a :class:`datetime.datetime` + + MEOS Functions: + period_lower + """ + + return timestamptz_to_datetime(super().lower()) + + def upper(self) -> datetime: + """ + Returns the upper bound of a period + + Returns: + The upper bound of the period as a :class:`datetime.datetime` + + MEOS Functions: + period_upper + """ + return timestamptz_to_datetime(super().upper()) + + def duration(self) -> timedelta: + """ + Returns the duration of the period. + + Returns: + A :class:`datetime.timedelta` instance representing the duration of the period + + MEOS Functions: + period_duration + """ + return interval_to_timedelta(period_duration(self._inner)) + + def duration_in_seconds(self) -> float: + """ + Returns the duration of the period. + + Returns: + Returns a `float` representing the duration of the period in seconds + + MEOS Functions: + span_width + """ + return self.width() + + # ------------------------- Transformations ------------------------------- + def shift(self, delta: timedelta) -> Period: + """ + Returns a new period that is the result of shifting ``self`` by ``delta`` + + Examples: + >>> Period('[2000-01-01, 2000-01-10]').shift(timedelta(days=2)) + >>> 'Period([2000-01-03 00:00:00+01, 2000-01-12 00:00:00+01])' + + Args: + delta: :class:`datetime.timedelta` instance to shift + + Returns: + A new :class:`Period` instance + + MEOS Functions: + period_shift_tscale + """ + return self.shift_tscale(shift=delta) + + def tscale(self, duration: timedelta) -> Period: + """ + Returns a new period that starts as ``self`` but has duration ``duration`` + + Examples: + >>> Period('[2000-01-01, 2000-01-10]').tscale(timedelta(days=2)) + >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' + + Args: + duration: :class:`datetime.timedelta` instance representing the duration of the new period + + Returns: + A new :class:`Period` instance + + MEOS Functions: + period_shift_tscale + """ + return self.shift_tscale(duration=duration) + + def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> Period: + """ + Returns a new period that starts at ``self`` shifted by ``shift`` and has duration ``duration`` + + Examples: + >>> Period('[2000-01-01, 2000-01-10]').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) + >>> 'Period([2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01])' + + Args: + shift: :class:`datetime.timedelta` instance to shift + duration: :class:`datetime.timedelta` instance representing the duration of the new period + + Returns: + A new :class:`Period` instance + + MEOS Functions: + period_shift_tscale + """ + assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' + modified = period_shift_tscale( + self._inner, + timedelta_to_interval(shift) if shift else None, + timedelta_to_interval(duration) if duration else None, + ) + return Period(_inner=modified) + + # ------------------------- Topological Operations ------------------------ + def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them + contains it. + + Examples: + >>> Period('[2012-01-01, 2012-01-02)').is_adjacent(Period('[2012-01-02, 2012-01-03]')) + >>> True + >>> Period('[2012-01-01, 2012-01-02]').is_adjacent(Period('[2012-01-02, 2012-01-03]')) + >>> False # Both contain bound + >>> Period('[2012-01-01, 2012-01-02)').is_adjacent(Period('(2012-01-02, 2012-01-03]')) + >>> False # Neither contain bound + + Args: + other: temporal object to compare with + + Returns: + True if adjacent, False otherwise + + MEOS Functions: + adjacent_span_span, adjacent_span_spanset, adjacent_period_timestamp, + adjacent_period_timestampset, adjacent_period_temporal + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return adjacent_period_timestamp(self._inner, datetime_to_timestamptz(other)) + elif isinstance(other, Temporal): + return adjacent_span_span(self._inner, temporal_to_period(other._inner)) + elif isinstance(other, get_args(Box)): + return adjacent_span_span(self._inner, other.to_period()._inner) + else: + return super().is_adjacent(other) + + def is_contained_in(self, container: Union[Period, PeriodSet, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is temporally contained in ``container``. + + Examples: + >>> Period('[2012-01-02, 2012-01-03]').is_contained_in(Period('[2012-01-01, 2012-01-04]')) + >>> True + >>> Period('(2012-01-01, 2012-01-02)').is_contained_in(Period('[2012-01-01, 2012-01-02]')) + >>> True + >>> Period('[2012-01-01, 2012-01-02]').is_contained_in(Period('(2012-01-01, 2012-01-02)')) + >>> False + + Args: + container: temporal object to compare with + + Returns: + True if contained, False otherwise + + MEOS Functions: + contained_span_span, contained_span_spanset, contained_period_temporal + """ + from .periodset import PeriodSet + from ..temporal import Temporal + from ..boxes import Box + if isinstance(container, Period): + return contained_span_span(self._inner, container._inner) + elif isinstance(container, PeriodSet): + return contained_span_spanset(self._inner, container._inner) + elif isinstance(container, Temporal): + return contained_span_span(self._inner, temporal_to_period(container._inner)) + elif isinstance(container, Box): + return contained_span_span(self._inner, container.to_period()._inner) + else: + raise TypeError(f'Operation not supported with type {container.__class__}') + + def contains(self, content: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` temporally contains ``content``. + + Examples: + >>> Period('[2012-01-01, 2012-01-04]').contains(Period('[2012-01-02, 2012-01-03]')) + >>> True + >>> Period('[2012-01-01, 2012-01-02]').contains(Period('(2012-01-01, 2012-01-02)')) + >>> True + >>> Period('(2012-01-01, 2012-01-02)').contains(Period('[2012-01-01, 2012-01-02]')) + >>> False + + Args: + content: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_span_span, contains_span_spanset, contains_period_timestamp, + contains_period_timestampset, contains_period_temporal + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + from ..temporal import Temporal + from ..boxes import Box + if isinstance(content, Period): + return contains_span_span(self._inner, content._inner) + elif isinstance(content, PeriodSet): + return contains_span_spanset(self._inner, content._inner) + elif isinstance(content, datetime): + return contains_period_timestamp(self._inner, datetime_to_timestamptz(content)) + elif isinstance(content, TimestampSet): + return contains_span_span(self._inner, set_span(content._inner)) + elif isinstance(content, Temporal): + return contains_span_span(self._inner, temporal_to_period(content._inner)) + elif isinstance(content, get_args(Box)): + return contains_span_span(self._inner, content.to_period()._inner) + else: + raise TypeError(f'Operation not supported with type {content.__class__}') + + def __contains__(self, item): + """ + Return whether ``self`` temporally contains ``item``. + + Examples: + >>> Period('[2012-01-02, 2012-01-03]') in Period('[2012-01-01, 2012-01-04]') + >>> True + >>> Period('(2012-01-01, 2012-01-02)') in Period('[2012-01-01, 2012-01-02]') + >>> True + >>> Period('[2012-01-01, 2012-01-02]') in Period('(2012-01-01, 2012-01-02)') + >>> False + + Args: + item: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_span_span, contains_span_spanset, contains_period_timestamp, + contains_period_timestampset, contains_period_temporal + """ + return self.contains(item) + + def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant + + Examples: + >>> Period('[2012-01-01, 2012-01-02]').overlaps(Period('[2012-01-02, 2012-01-03]')) + >>> True + >>> Period('[2012-01-01, 2012-01-02)').overlaps(Period('[2012-01-02, 2012-01-03]')) + >>> False + >>> Period('[2012-01-01, 2012-01-02)').overlaps(Period('(2012-01-02, 2012-01-03]')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if overlaps, False otherwise + + MEOS Functions: + overlaps_span_span, overlaps_span_spanset, overlaps_period_timestampset, + overlaps_period_temporal + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + from ..temporal import Temporal + from ..boxes import Box + if isinstance(other, Period): + return overlaps_span_span(self._inner, other._inner) + elif isinstance(other, PeriodSet): + return overlaps_spanset_span(other._inner, self._inner) + elif isinstance(other, datetime): + return overlaps_span_span(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) + elif isinstance(other, TimestampSet): + return overlaps_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Temporal): + return overlaps_span_span(self._inner, temporal_to_period(other._inner)) + elif isinstance(other, get_args(Box)): + return overlaps_span_span(self._inner, other.to_period()._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_same(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` and the bounding period of ``other`` is the same. + + Args: + other: temporal object to compare with + + Returns: + True if equal, False otherwise + + MEOS Functions: + same_period_temporal + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + from ..temporal import Temporal + from ..boxes import Box + if isinstance(other, Temporal): + return span_eq(self._inner, temporal_to_period(other._inner)) + elif isinstance(other, get_args(Box)): + return span_eq(self._inner, other.to_period()._inner) + elif isinstance(other, Period): + return span_eq(self._inner, other._inner) + elif isinstance(other, PeriodSet): + return span_eq(self._inner, spanset_span(other._inner)) + elif isinstance(other, datetime): + return span_eq(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) + elif isinstance(other, TimestampSet): + return span_eq(self._inner, set_span(other._inner)) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Position Operations --------------------------- + def is_before(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + + Examples: + >>> Period('[2012-01-01, 2012-01-02)').is_before(Period('[2012-01-02, 2012-01-03]')) + >>> True + >>> Period('[2012-01-01, 2012-01-02)').is_before(Period('(2012-01-02, 2012-01-03]')) + >>> True + >>> Period('[2012-01-01, 2012-01-02]').is_before(Period('[2012-01-02, 2012-01-03]')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + left_span_span, left_span_spanset, before_period_timestamp, + before_period_timestampset, before_period_temporal + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + from ..temporal import Temporal + from ..boxes import Box + if isinstance(other, Period): + return left_span_span(self._inner, other._inner) + elif isinstance(other, PeriodSet): + return left_span_spanset(self._inner, other._inner) + elif isinstance(other, datetime): + return overafter_timestamp_period(datetime_to_timestamptz(other), self._inner) + if isinstance(other, TimestampSet): + return left_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Temporal): + return left_span_span(self._inner, temporal_to_period(other._inner)) + elif isinstance(other, get_args(Box)): + return left_span_span(self._inner, other.to_period()._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or + at the same time). + + Examples: + >>> Period('[2012-01-01, 2012-01-02)').is_over_or_before(Period('[2012-01-02, 2012-01-03]')) + >>> True + >>> Period('[2012-01-01, 2012-01-02]').is_over_or_before(Period('[2012-01-02, 2012-01-03]')) + >>> True + >>> Period('[2012-01-03, 2012-01-05]').is_over_or_before(Period('[2012-01-01, 2012-01-04]')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overleft_span_span, overleft_span_spanset, overbefore_period_timestamp, + overbefore_period_timestampset, overbefore_period_temporal + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + from ..temporal import Temporal + from ..boxes import Box + if isinstance(other, Period): + return overleft_span_span(self._inner, other._inner) + elif isinstance(other, PeriodSet): + return overleft_span_spanset(self._inner, other._inner) + elif isinstance(other, datetime): + return overbefore_period_timestamp(self._inner, datetime_to_timestamptz(other)) + if isinstance(other, TimestampSet): + return overleft_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Temporal): + return overleft_span_span(self._inner, temporal_to_period(other._inner)) + elif isinstance(other, get_args(Box)): + return overleft_span_span(self._inner, other.to_period()._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_after(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. + + Examples: + >>> Period('[2012-01-02, 2012-01-03]').is_after(Period('[2012-01-01, 2012-01-02)')) + >>> True + >>> Period('(2012-01-02, 2012-01-03]').is_after(Period('[2012-01-01, 2012-01-02)')) + >>> True + >>> Period('[2012-01-02, 2012-01-03]').is_after(Period('[2012-01-01, 2012-01-02]')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if after, False otherwise + + MEOS Functions: + right_span_span, right_span_spanset, after_period_timestamp, + after_period_timestampset, after_period_temporal + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + from ..temporal import Temporal + from ..boxes import Box + if isinstance(other, Period): + return right_span_span(self._inner, other._inner) + elif isinstance(other, PeriodSet): + return right_span_spanset(self._inner, other._inner) + elif isinstance(other, datetime): + return overbefore_timestamp_period(datetime_to_timestamptz(other), self._inner) + if isinstance(other, TimestampSet): + return right_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Temporal): + return right_span_span(self._inner, temporal_to_period(other._inner)) + elif isinstance(other, get_args(Box)): + return right_span_span(self._inner, other.to_period()._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts + (or at the same time). + + Examples: + >>> Period('[2012-01-02, 2012-01-03]').is_over_or_after(Period('[2012-01-01, 2012-01-02)')) + >>> True + >>> Period('[2012-01-02, 2012-01-03]').is_over_or_after(Period('[2012-01-01, 2012-01-02]')) + >>> True + >>> Period('[2012-01-02, 2012-01-03]').is_over_or_after(Period('[2012-01-01, 2012-01-03]')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if overlapping or after, False otherwise + + MEOS Functions: + overright_span_span, overright_span_spanset, overafter_period_timestamp, + overafter_period_timestampset, overafter_period_temporal + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + from ..temporal import Temporal + from ..boxes import Box + if isinstance(other, Period): + return overright_span_span(self._inner, other._inner) + elif isinstance(other, PeriodSet): + return overright_span_spanset(self._inner, other._inner) + elif isinstance(other, datetime): + return overafter_period_timestamp(self._inner, datetime_to_timestamptz(other)) + if isinstance(other, TimestampSet): + return overright_span_span(self._inner, set_span(other._inner)) + elif isinstance(other, Temporal): + return overright_span_span(self._inner, temporal_to_period(other._inner)) + elif isinstance(other, get_args(Box)): + return overright_span_span(self._inner, other.to_period()._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Distance Operations --------------------------- + def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: + """ + Returns the temporal distance between ``self`` and ``other``. + + Args: + other: temporal object to compare with + + Returns: + A :class:`datetime.timedelta` instance + + MEOS Functions: + distance_span_span, distance_spanset_span, distance_period_timestamp + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + from ..temporal import Temporal + from ..boxes import Box + if isinstance(other, Temporal): + return timedelta(seconds=distance_span_span(self._inner, temporal_to_period(other._inner))) + elif isinstance(other, Period): + return timedelta(seconds=distance_span_span(self._inner, other._inner)) + elif isinstance(other, PeriodSet): + return timedelta(seconds=distance_spanset_span(other._inner, self._inner)) + elif isinstance(other, datetime): + return timedelta(seconds=distance_period_timestamp(self._inner, datetime_to_timestamptz(other))) + elif isinstance(other, TimestampSet): + return timedelta(seconds=distance_span_span(self._inner, set_span(other._inner))) + elif isinstance(other, get_args(Box)): + return timedelta(seconds=distance_span_span(self._inner, other.to_period()._inner)) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Set Operations -------------------------------- + @overload + def intersection(self, other: datetime) -> Optional[datetime]: + ... + + @overload + def intersection(self, other: Period) -> Optional[Period]: + ... + + @overload + def intersection(self, other: Union[TimestampSet, PeriodSet]) -> Optional[PeriodSet]: + ... + + def intersection(self, other: Time) -> Optional[Time]: + """ + Returns the temporal intersection of ``self`` and ``other``. + + Args: + other: temporal object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_span_span, intersection_spanset_span, intersection_period_timestamp + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + if isinstance(other, datetime): + result = intersection_period_timestamp(self._inner, datetime_to_timestamptz(other)) + return timestamptz_to_datetime(result) if result is not None else None + elif isinstance(other, TimestampSet): + result = intersection_spanset_span(set_to_spanset(other._inner), self._inner) + return TimestampSet(_inner=result) if result is not None else None + elif isinstance(other, Period): + result = intersection_span_span(self._inner, other._inner) + return Period(_inner=result) if result is not None else None + elif isinstance(other, PeriodSet): + result = intersection_spanset_span(other._inner, self._inner) + return PeriodSet(_inner=result) if result is not None else None + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __mul__(self, other): + """ + Returns the temporal intersection of ``self`` and ``other``. + + Args: + other: temporal object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_span_span, intersection_spanset_span, intersection_period_timestamp + """ + return self.intersection(other) + + def minus(self, other: Time) -> PeriodSet: + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + minus_period_timestamp, minus_span_spanset, minus_span_span + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + if isinstance(other, datetime): + result = minus_period_timestamp(self._inner, datetime_to_timestamptz(other)) + return PeriodSet(_inner=result) if result is not None else None + elif isinstance(other, TimestampSet): + result = minus_span_spanset(self._inner, set_to_spanset(other._inner)) + return PeriodSet(_inner=result) if result is not None else None + elif isinstance(other, Period): + result = minus_span_span(self._inner, other._inner) + return PeriodSet(_inner=result) if result is not None else None + elif isinstance(other, PeriodSet): + result = minus_span_spanset(self._inner, other._inner) + return PeriodSet(_inner=result) if result is not None else None + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __sub__(self, other): + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + minus_period_timestamp, minus_span_spanset, minus_span_span + """ + return self.minus(other) + + def union(self, other: Time) -> PeriodSet: + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + union_period_timestamp, union_spanset_span, union_span_span + """ + from .periodset import PeriodSet + from .timestampset import TimestampSet + if isinstance(other, datetime): + return PeriodSet(_inner=union_period_timestamp(self._inner, datetime_to_timestamptz(other))) + elif isinstance(other, TimestampSet): + return PeriodSet(_inner=union_spanset_span(set_to_spanset(other._inner), self._inner)) + if isinstance(other, Period): + return PeriodSet(_inner=union_span_span(self._inner, other._inner)) + elif isinstance(other, PeriodSet): + return PeriodSet(_inner=union_spanset_span(other._inner, self._inner)) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __add__(self, other): + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + union_period_timestamp, union_spanset_span, union_span_span + """ + return self.union(other) + + # ------------------------- Comparisons ----------------------------------- + def __eq__(self, other): + """ + Return whether ``self`` and ``other`` are equal. + + Args: + other: temporal object to compare with + + Returns: + True if equal, False otherwise + + MEOS Functions: + span_eq + """ + if isinstance(other, self.__class__): + return span_eq(self._inner, other._inner) + return False + + def __ne__(self, other): + """ + Return whether ``self`` and ``other`` are not equal. + + Args: + other: temporal object to compare with + + Returns: + True if not equal, False otherwise + + MEOS Functions: + span_neq + """ + if isinstance(other, self.__class__): + return span_ne(self._inner, other._inner) + return True + + def __lt__(self, other): + """ + Return whether ``self`` is less than ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if less than, False otherwise + + MEOS Functions: + span_lt + """ + if isinstance(other, self.__class__): + return span_lt(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __le__(self, other): + """ + Return whether ``self`` is less than or equal to ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if less than or equal, False otherwise + + MEOS Functions: + span_le + """ + if isinstance(other, self.__class__): + return span_le(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __gt__(self, other): + """ + Return whether ``self`` is greater than ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if greater than, False otherwise + + MEOS Functions: + span_gt + """ + if isinstance(other, self.__class__): + return span_gt(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __ge__(self, other): + """ + Return whether ``self`` is greater than or equal to ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if greater than or equal, False otherwise + + MEOS Functions: + span_ge + """ + if isinstance(other, self.__class__): + return span_ge(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Plot Operations ------------------------------- + def plot(self, *args, **kwargs): + from ..plotters import TimePlotter + return TimePlotter.plot_period(self, *args, **kwargs) + + # ------------------------- Database Operations --------------------------- + @staticmethod + def read_from_cursor(value, _=None): + """ + Reads a :class:`Period` from a database cursor. Used when automatically loading objects from the database. + Users should use the class constructor instead. + """ + if not value: + return None + return Period(string=value) diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py new file mode 100644 index 00000000..4552962a --- /dev/null +++ b/pymeos/pymeos/collections/time/periodset.py @@ -0,0 +1,7 @@ +from datetime import datetime + +from ..base.spanset import SpanSet + + +class PeriodSet(SpanSet[datetime]): + pass diff --git a/pymeos/pymeos/collections/time/time.py b/pymeos/pymeos/collections/time/time.py new file mode 100644 index 00000000..336a665d --- /dev/null +++ b/pymeos/pymeos/collections/time/time.py @@ -0,0 +1,16 @@ +from typing import Union + +from .period import Period +from .periodset import PeriodSet +from .timestampset import TimestampSet +from datetime import datetime + +Time = Union[datetime, TimestampSet, Period, PeriodSet] +""" +Union type that includes all Time types in PyMEOS: + +- :class:`~datetime.datetime` for timestamps +- :class:`~pymeos.time.timestampset.TimestampSet` for sets of timestamps +- :class:`~pymeos.time.period.Period` for periods of time +- :class:`~pymeos.time.periodset.PeriodSet` for sets of periods of time +""" \ No newline at end of file diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py new file mode 100644 index 00000000..07ef893f --- /dev/null +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -0,0 +1,7 @@ +from datetime import datetime + +from ..base.set import Set + + +class TimestampSet(Set[datetime]): + pass From 784ce7545d0d7568548f6523915fb28f53767f84 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Sat, 9 Sep 2023 00:51:01 +0200 Subject: [PATCH 019/101] Finish Span and Period abstraction --- pymeos/pymeos/__init__.py | 3 +- pymeos/pymeos/collections/__init__.py | 7 + pymeos/pymeos/collections/base/__init__.py | 5 + pymeos/pymeos/collections/base/span.py | 53 ++- pymeos/pymeos/collections/time/__init__.py | 6 + pymeos/pymeos/collections/time/period.py | 376 ++++----------------- 6 files changed, 124 insertions(+), 326 deletions(-) diff --git a/pymeos/pymeos/__init__.py b/pymeos/pymeos/__init__.py index 02043095..98b1a345 100644 --- a/pymeos/pymeos/__init__.py +++ b/pymeos/pymeos/__init__.py @@ -3,7 +3,8 @@ from .main import * from .meos_init import * from .temporal import * -from .time import * +from .time import PeriodSet, TimestampSet, Time +from .collections import Period __version__ = '1.1.3a1' __all__ = [ diff --git a/pymeos/pymeos/collections/__init__.py b/pymeos/pymeos/collections/__init__.py index e69de29b..9ba1196e 100644 --- a/pymeos/pymeos/collections/__init__.py +++ b/pymeos/pymeos/collections/__init__.py @@ -0,0 +1,7 @@ +from .base import * +from .time import * + +__all__ = [ + 'Set', 'Span', 'SpanSet', + 'Time', 'TimestampSet', 'Period', 'PeriodSet', +] diff --git a/pymeos/pymeos/collections/base/__init__.py b/pymeos/pymeos/collections/base/__init__.py index e69de29b..29db124f 100644 --- a/pymeos/pymeos/collections/base/__init__.py +++ b/pymeos/pymeos/collections/base/__init__.py @@ -0,0 +1,5 @@ +from .set import Set +from .span import Span +from .spanset import SpanSet + +__all__ = ["Set", "Span", "SpanSet"] diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index 175416b2..c94cca0a 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -1,9 +1,9 @@ from __future__ import annotations from abc import ABC, abstractmethod -from datetime import datetime, timedelta +from datetime import timedelta from typing import Generic, TypeVar, Type, Callable, Any, TYPE_CHECKING -from typing import Optional, Union, overload, get_args +from typing import Optional, Union from pymeos_cffi import * @@ -56,7 +56,7 @@ def __copy__(self: Self) -> Self: span_copy """ inner_copy = span_copy(self._inner) - return self._class__(_inner=inner_copy) + return self.__class__(_inner=inner_copy) @classmethod def from_wkb(cls: Type[Self], wkb: bytes) -> Self: @@ -174,7 +174,7 @@ def upper(self) -> T: MEOS Functions: period_upper """ - return timestamptz_to_datetime(period_upper(self._inner)) + return period_upper(self._inner) def lower_inc(self) -> bool: """ @@ -240,6 +240,8 @@ def is_adjacent(self: Self, other) -> bool: MEOS Functions: adjacent_span_span, adjacent_span_spanset, """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): return adjacent_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Span): @@ -262,6 +264,7 @@ def is_contained_in(self, container) -> bool: MEOS Functions: contained_span_span, contained_span_spanset, contained_period_temporal """ + from .spanset import SpanSet if isinstance(container, Span): return contained_span_span(self._inner, container._inner) elif isinstance(container, SpanSet): @@ -283,6 +286,8 @@ def contains(self, content) -> bool: contains_span_span, contains_span_spanset, contains_period_timestamp, contains_period_timestampset, contains_period_temporal """ + from .set import Set + from .spanset import SpanSet if isinstance(content, Set): return contains_span_span(self._inner, set_span(content._inner)) elif isinstance(content, Span): @@ -321,6 +326,8 @@ def overlaps(self, other) -> bool: MEOS Functions: overlaps_span_span, overlaps_span_spanset """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): return overlaps_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Span): @@ -343,6 +350,8 @@ def is_same(self, other) -> bool: MEOS Functions: same_period_temporal """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): return span_eq(self._inner, set_span(other._inner)) elif isinstance(other, Span): @@ -366,6 +375,8 @@ def is_before(self, other) -> bool: MEOS Functions: left_span_span, left_span_spanset """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): return left_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Span): @@ -389,6 +400,8 @@ def is_over_or_before(self, other) -> bool: MEOS Functions: overleft_span_span, overleft_span_spanset """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): return overleft_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Span): @@ -411,6 +424,8 @@ def is_after(self, other) -> bool: MEOS Functions: right_span_span, right_span_spanset """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): return right_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Span): @@ -435,6 +450,8 @@ def is_over_or_after(self, other) -> bool: overright_span_span, overright_span_spanset, overafter_period_timestamp, overafter_period_timestampset, overafter_period_temporal """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): return overright_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Span): @@ -458,6 +475,8 @@ def distance(self, other) -> timedelta: MEOS Functions: distance_span_span, distance_spanset_span, distance_period_timestamp """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): return timedelta(seconds=distance_span_span(self._inner, set_span(other._inner))) elif isinstance(other, Span): @@ -468,6 +487,7 @@ def distance(self, other) -> timedelta: raise TypeError(f'Operation not supported with type {other.__class__}') # ------------------------- Set Operations -------------------------------- + @abstractmethod def intersection(self, other): """ Returns the temporal intersection of ``self`` and ``other``. @@ -481,15 +501,14 @@ def intersection(self, other): MEOS Functions: intersection_span_span, intersection_spanset_span, intersection_period_timestamp """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): - result = intersection_spanset_span(set_to_spanset(other._inner), self._inner) - return result if result is not None else None + return intersection_spanset_span(set_to_spanset(other._inner), self._inner) elif isinstance(other, Span): - result = intersection_span_span(self._inner, other._inner) - return result if result is not None else None + return intersection_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): - result = intersection_spanset_span(other._inner, self._inner) - return result if result is not None else None + return intersection_spanset_span(other._inner, self._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -522,15 +541,14 @@ def minus(self, other): MEOS Functions: minus_period_timestamp, minus_span_spanset, minus_span_span """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): - result = minus_span_spanset(self._inner, set_to_spanset(other._inner)) - return result if result is not None else None + return minus_span_spanset(self._inner, set_to_spanset(other._inner)) elif isinstance(other, Span): - result = minus_span_span(self._inner, other._inner) - return result if result is not None else None + return minus_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): - result = minus_span_spanset(self._inner, other._inner) - return result if result is not None else None + return minus_span_spanset(self._inner, other._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -549,6 +567,7 @@ def __sub__(self, other): """ return self.minus(other) + @abstractmethod def union(self, other): """ Returns the temporal union of ``self`` and ``other``. @@ -562,6 +581,8 @@ def union(self, other): MEOS Functions: union_period_timestamp, union_spanset_span, union_span_span """ + from .set import Set + from .spanset import SpanSet if isinstance(other, Set): return union_spanset_span(set_to_spanset(other._inner), self._inner) elif isinstance(other, Span): diff --git a/pymeos/pymeos/collections/time/__init__.py b/pymeos/pymeos/collections/time/__init__.py index e69de29b..83534d48 100644 --- a/pymeos/pymeos/collections/time/__init__.py +++ b/pymeos/pymeos/collections/time/__init__.py @@ -0,0 +1,6 @@ +from .timestampset import TimestampSet +from .period import Period +from .periodset import PeriodSet +from .time import Time + +__all__ = ['Time', 'TimestampSet', 'Period', 'PeriodSet'] diff --git a/pymeos/pymeos/collections/time/period.py b/pymeos/pymeos/collections/time/period.py index 3d8ceb52..0fd2774c 100644 --- a/pymeos/pymeos/collections/time/period.py +++ b/pymeos/pymeos/collections/time/period.py @@ -14,6 +14,7 @@ from ...boxes import Box from .periodset import PeriodSet from .time import Time + from .timestampset import TimestampSet class Period(Span[datetime]): @@ -254,19 +255,14 @@ def is_contained_in(self, container: Union[Period, PeriodSet, Box, Temporal]) -> MEOS Functions: contained_span_span, contained_span_spanset, contained_period_temporal """ - from .periodset import PeriodSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(container, Period): - return contained_span_span(self._inner, container._inner) - elif isinstance(container, PeriodSet): - return contained_span_spanset(self._inner, container._inner) - elif isinstance(container, Temporal): - return contained_span_span(self._inner, temporal_to_period(container._inner)) + from ...temporal import Temporal + from ...boxes import Box + if isinstance(container, Temporal): + return self.is_contained_in(container.period()) elif isinstance(container, Box): - return contained_span_span(self._inner, container.to_period()._inner) + return self.is_contained_in(container.to_period()) else: - raise TypeError(f'Operation not supported with type {container.__class__}') + super().is_contained_in(container) def contains(self, content: Union[Time, Box, Temporal]) -> bool: """ @@ -290,48 +286,16 @@ def contains(self, content: Union[Time, Box, Temporal]) -> bool: contains_span_span, contains_span_spanset, contains_period_timestamp, contains_period_timestampset, contains_period_temporal """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(content, Period): - return contains_span_span(self._inner, content._inner) - elif isinstance(content, PeriodSet): - return contains_span_spanset(self._inner, content._inner) - elif isinstance(content, datetime): + from ...temporal import Temporal + from ...boxes import Box + if isinstance(content, datetime): return contains_period_timestamp(self._inner, datetime_to_timestamptz(content)) - elif isinstance(content, TimestampSet): - return contains_span_span(self._inner, set_span(content._inner)) elif isinstance(content, Temporal): - return contains_span_span(self._inner, temporal_to_period(content._inner)) + return self.contains(content.period()) elif isinstance(content, get_args(Box)): - return contains_span_span(self._inner, content.to_period()._inner) + return self.contains(content.to_period()) else: - raise TypeError(f'Operation not supported with type {content.__class__}') - - def __contains__(self, item): - """ - Return whether ``self`` temporally contains ``item``. - - Examples: - >>> Period('[2012-01-02, 2012-01-03]') in Period('[2012-01-01, 2012-01-04]') - >>> True - >>> Period('(2012-01-01, 2012-01-02)') in Period('[2012-01-01, 2012-01-02]') - >>> True - >>> Period('[2012-01-01, 2012-01-02]') in Period('(2012-01-01, 2012-01-02)') - >>> False - - Args: - item: temporal object to compare with - - Returns: - True if contains, False otherwise - - MEOS Functions: - contains_span_span, contains_span_spanset, contains_period_timestamp, - contains_period_timestampset, contains_period_temporal - """ - return self.contains(item) + super().contains(content) def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: """ @@ -355,24 +319,16 @@ def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: overlaps_span_span, overlaps_span_spanset, overlaps_period_timestampset, overlaps_period_temporal """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return overlaps_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return overlaps_spanset_span(other._inner, self._inner) - elif isinstance(other, datetime): + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): return overlaps_span_span(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return overlaps_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return overlaps_span_span(self._inner, temporal_to_period(other._inner)) + return self.overlaps(other.period()) elif isinstance(other, get_args(Box)): - return overlaps_span_span(self._inner, other.to_period()._inner) + return self.overlaps(other.to_period()) else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().overlaps(other) def is_same(self, other: Union[Time, Box, Temporal]) -> bool: """ @@ -387,24 +343,16 @@ def is_same(self, other: Union[Time, Box, Temporal]) -> bool: MEOS Functions: same_period_temporal """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box + from ...temporal import Temporal + from ...boxes import Box if isinstance(other, Temporal): - return span_eq(self._inner, temporal_to_period(other._inner)) + return self.is_same(other.period()) elif isinstance(other, get_args(Box)): - return span_eq(self._inner, other.to_period()._inner) - elif isinstance(other, Period): - return span_eq(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return span_eq(self._inner, spanset_span(other._inner)) + return self.is_same(other.to_period()) elif isinstance(other, datetime): return span_eq(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return span_eq(self._inner, set_span(other._inner)) else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().is_same(other) # ------------------------- Position Operations --------------------------- def is_before(self, other: Union[Time, Box, Temporal]) -> bool: @@ -429,24 +377,16 @@ def is_before(self, other: Union[Time, Box, Temporal]) -> bool: left_span_span, left_span_spanset, before_period_timestamp, before_period_timestampset, before_period_temporal """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return left_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return left_span_spanset(self._inner, other._inner) - elif isinstance(other, datetime): + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): return overafter_timestamp_period(datetime_to_timestamptz(other), self._inner) - if isinstance(other, TimestampSet): - return left_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return left_span_span(self._inner, temporal_to_period(other._inner)) + return self.is_before(other.period()) elif isinstance(other, get_args(Box)): - return left_span_span(self._inner, other.to_period()._inner) + return self.is_before(other.to_period()) else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().is_before(other) def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: """ @@ -471,24 +411,16 @@ def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: overleft_span_span, overleft_span_spanset, overbefore_period_timestamp, overbefore_period_timestampset, overbefore_period_temporal """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return overleft_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return overleft_span_spanset(self._inner, other._inner) - elif isinstance(other, datetime): + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): return overbefore_period_timestamp(self._inner, datetime_to_timestamptz(other)) - if isinstance(other, TimestampSet): - return overleft_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return overleft_span_span(self._inner, temporal_to_period(other._inner)) + return self.is_over_or_before(other.period()) elif isinstance(other, get_args(Box)): - return overleft_span_span(self._inner, other.to_period()._inner) + return self.is_over_or_before(other.to_period()) else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().is_over_or_before(other) def is_after(self, other: Union[Time, Box, Temporal]) -> bool: """ @@ -512,24 +444,16 @@ def is_after(self, other: Union[Time, Box, Temporal]) -> bool: right_span_span, right_span_spanset, after_period_timestamp, after_period_timestampset, after_period_temporal """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return right_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return right_span_spanset(self._inner, other._inner) - elif isinstance(other, datetime): + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): return overbefore_timestamp_period(datetime_to_timestamptz(other), self._inner) - if isinstance(other, TimestampSet): - return right_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return right_span_span(self._inner, temporal_to_period(other._inner)) + return self.is_after(other.period()) elif isinstance(other, get_args(Box)): - return right_span_span(self._inner, other.to_period()._inner) + return self.is_after(other.to_period()) else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().is_after(other) def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: """ @@ -554,24 +478,16 @@ def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: overright_span_span, overright_span_spanset, overafter_period_timestamp, overafter_period_timestampset, overafter_period_temporal """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return overright_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return overright_span_spanset(self._inner, other._inner) - elif isinstance(other, datetime): + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): return overafter_period_timestamp(self._inner, datetime_to_timestamptz(other)) - if isinstance(other, TimestampSet): - return overright_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return overright_span_span(self._inner, temporal_to_period(other._inner)) + return self.is_over_or_after(other.period()) elif isinstance(other, get_args(Box)): - return overright_span_span(self._inner, other.to_period()._inner) + return self.is_over_or_after(other.to_period()) else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().is_over_or_after(other) # ------------------------- Distance Operations --------------------------- def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: @@ -587,24 +503,16 @@ def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: MEOS Functions: distance_span_span, distance_spanset_span, distance_period_timestamp """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box + from ...temporal import Temporal + from ...boxes import Box if isinstance(other, Temporal): - return timedelta(seconds=distance_span_span(self._inner, temporal_to_period(other._inner))) - elif isinstance(other, Period): - return timedelta(seconds=distance_span_span(self._inner, other._inner)) - elif isinstance(other, PeriodSet): - return timedelta(seconds=distance_spanset_span(other._inner, self._inner)) + return self.distance(other.period()) elif isinstance(other, datetime): return timedelta(seconds=distance_period_timestamp(self._inner, datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return timedelta(seconds=distance_span_span(self._inner, set_span(other._inner))) elif isinstance(other, get_args(Box)): - return timedelta(seconds=distance_span_span(self._inner, other.to_period()._inner)) + return self.distance(other.to_period()) else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().distance(other) # ------------------------- Set Operations -------------------------------- @overload @@ -638,32 +546,17 @@ def intersection(self, other: Time) -> Optional[Time]: result = intersection_period_timestamp(self._inner, datetime_to_timestamptz(other)) return timestamptz_to_datetime(result) if result is not None else None elif isinstance(other, TimestampSet): - result = intersection_spanset_span(set_to_spanset(other._inner), self._inner) + result = super().intersection(other) return TimestampSet(_inner=result) if result is not None else None elif isinstance(other, Period): - result = intersection_span_span(self._inner, other._inner) + result = super().intersection(other) return Period(_inner=result) if result is not None else None elif isinstance(other, PeriodSet): - result = intersection_spanset_span(other._inner, self._inner) + result = super().intersection(other) return PeriodSet(_inner=result) if result is not None else None else: raise TypeError(f'Operation not supported with type {other.__class__}') - def __mul__(self, other): - """ - Returns the temporal intersection of ``self`` and ``other``. - - Args: - other: temporal object to intersect with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - intersection_span_span, intersection_spanset_span, intersection_period_timestamp - """ - return self.intersection(other) - def minus(self, other: Time) -> PeriodSet: """ Returns the temporal difference of ``self`` and ``other``. @@ -681,33 +574,15 @@ def minus(self, other: Time) -> PeriodSet: from .timestampset import TimestampSet if isinstance(other, datetime): result = minus_period_timestamp(self._inner, datetime_to_timestamptz(other)) - return PeriodSet(_inner=result) if result is not None else None elif isinstance(other, TimestampSet): - result = minus_span_spanset(self._inner, set_to_spanset(other._inner)) - return PeriodSet(_inner=result) if result is not None else None + result = super().minus(other) elif isinstance(other, Period): - result = minus_span_span(self._inner, other._inner) - return PeriodSet(_inner=result) if result is not None else None + result = super().minus(other) elif isinstance(other, PeriodSet): - result = minus_span_spanset(self._inner, other._inner) - return PeriodSet(_inner=result) if result is not None else None + result = super().minus(other) else: raise TypeError(f'Operation not supported with type {other.__class__}') - - def __sub__(self, other): - """ - Returns the temporal difference of ``self`` and ``other``. - - Args: - other: temporal object to diff with - - Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - minus_period_timestamp, minus_span_spanset, minus_span_span - """ - return self.minus(other) + return PeriodSet(_inner=result) if result is not None else None def union(self, other: Time) -> PeriodSet: """ @@ -725,137 +600,20 @@ def union(self, other: Time) -> PeriodSet: from .periodset import PeriodSet from .timestampset import TimestampSet if isinstance(other, datetime): - return PeriodSet(_inner=union_period_timestamp(self._inner, datetime_to_timestamptz(other))) + result = union_period_timestamp(self._inner, datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): - return PeriodSet(_inner=union_spanset_span(set_to_spanset(other._inner), self._inner)) - if isinstance(other, Period): - return PeriodSet(_inner=union_span_span(self._inner, other._inner)) + result = super().union(other) + elif isinstance(other, Period): + result = super().union(other) elif isinstance(other, PeriodSet): - return PeriodSet(_inner=union_spanset_span(other._inner, self._inner)) + result = super().union(other) else: raise TypeError(f'Operation not supported with type {other.__class__}') - - def __add__(self, other): - """ - Returns the temporal union of ``self`` and ``other``. - - Args: - other: temporal object to merge with - - Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - union_period_timestamp, union_spanset_span, union_span_span - """ - return self.union(other) - - # ------------------------- Comparisons ----------------------------------- - def __eq__(self, other): - """ - Return whether ``self`` and ``other`` are equal. - - Args: - other: temporal object to compare with - - Returns: - True if equal, False otherwise - - MEOS Functions: - span_eq - """ - if isinstance(other, self.__class__): - return span_eq(self._inner, other._inner) - return False - - def __ne__(self, other): - """ - Return whether ``self`` and ``other`` are not equal. - - Args: - other: temporal object to compare with - - Returns: - True if not equal, False otherwise - - MEOS Functions: - span_neq - """ - if isinstance(other, self.__class__): - return span_ne(self._inner, other._inner) - return True - - def __lt__(self, other): - """ - Return whether ``self`` is less than ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if less than, False otherwise - - MEOS Functions: - span_lt - """ - if isinstance(other, self.__class__): - return span_lt(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __le__(self, other): - """ - Return whether ``self`` is less than or equal to ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if less than or equal, False otherwise - - MEOS Functions: - span_le - """ - if isinstance(other, self.__class__): - return span_le(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __gt__(self, other): - """ - Return whether ``self`` is greater than ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if greater than, False otherwise - - MEOS Functions: - span_gt - """ - if isinstance(other, self.__class__): - return span_gt(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __ge__(self, other): - """ - Return whether ``self`` is greater than or equal to ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if greater than or equal, False otherwise - - MEOS Functions: - span_ge - """ - if isinstance(other, self.__class__): - return span_ge(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') + return PeriodSet(_inner=result) if result is not None else None # ------------------------- Plot Operations ------------------------------- def plot(self, *args, **kwargs): - from ..plotters import TimePlotter + from ...plotters import TimePlotter return TimePlotter.plot_period(self, *args, **kwargs) # ------------------------- Database Operations --------------------------- From ebc30d695395daa98b5218a46dd8d1dab1b7f9fc Mon Sep 17 00:00:00 2001 From: Diviloper Date: Sat, 9 Sep 2023 19:43:15 +0200 Subject: [PATCH 020/101] Add Set and TimestampSet --- pymeos/pymeos/collections/base/set.py | 688 ++++++++++++++++- pymeos/pymeos/collections/base/span.py | 55 +- pymeos/pymeos/collections/time/period.py | 58 +- .../pymeos/collections/time/timestampset.py | 692 +++++++++++++++++- pymeos/tests/time/period_test.py | 8 +- pymeos_cffi/pymeos_cffi/__init__.py | 2 + .../builder/build_pymeos_functions.py | 2 +- .../build_pymeos_functions_modifiers.py | 4 +- pymeos_cffi/pymeos_cffi/builder/meos.h | 2 + pymeos_cffi/pymeos_cffi/functions.py | 22 +- 10 files changed, 1462 insertions(+), 71 deletions(-) diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py index 35850a65..74b7506b 100644 --- a/pymeos/pymeos/collections/base/set.py +++ b/pymeos/pymeos/collections/base/set.py @@ -2,13 +2,17 @@ from abc import ABC, abstractmethod from datetime import datetime, timedelta -from typing import Generic, TypeVar, Type, Callable, Any -from typing import Optional, Union, overload, get_args +from typing import Generic, TypeVar, Type, Callable, Any, TYPE_CHECKING, Iterable +from typing import Optional, Union, overload, get_args, List from pymeos_cffi import * +if TYPE_CHECKING: + from .spanset import SpanSet + from .span import Span + T = TypeVar('T') -Self = TypeVar('Self', bound='Span[Any]') +Self = TypeVar('Self', bound='Set[Any]') class Set(Generic[T], ABC): @@ -17,3 +21,681 @@ class Set(Generic[T], ABC): """ __slots__ = ['_inner'] + + _parse_function: Callable[[str], 'CData'] = None + _parse_value_function: Callable[[Union[str, T]], Any] = None + _make_function: Callable[[Iterable[Any]], 'CData'] = None + + # ------------------------- Constructors ---------------------------------- + def __init__(self, string: Optional[str] = None, *, timestamp_list: Optional[List[Union[str, datetime]]] = None, + _inner=None): + super().__init__() + assert (_inner is not None) or ((string is not None) != (timestamp_list is not None)), \ + "Either string must be not None or timestamp_list must be not" + if _inner is not None: + self._inner = _inner + elif string is not None: + self._inner = timestampset_in(string) + else: + times = [self.__class__._parse_value_function(ts) for ts in timestamp_list] + self._inner = self.__class__._make_function(times) + + def __copy__(self: Self) -> Self: + """ + Return a copy of ``self``. + + Returns: + A new :class:`Period` instance + + MEOS Functions: + set_copy + """ + inner_copy = set_copy(self._inner) + return self.__class__(_inner=inner_copy) + + @classmethod + def from_wkb(cls: Type[Self], wkb: bytes) -> Self: + """ + Returns a `TimestampSet` from its WKB representation. + Args: + wkb: WKB representation + + Returns: + A new :class:`TimestampSet` instance + + MEOS Functions: + set_from_wkb + """ + return cls(_inner=set_from_wkb(wkb)) + + @classmethod + def from_hexwkb(cls: Type[Self], hexwkb: str) -> Self: + """ + Returns a `TimestampSet` from its WKB representation in hex-encoded ASCII. + Args: + hexwkb: WKB representation in hex-encoded ASCII + + Returns: + A new :class:`TimestampSet` instance + + MEOS Functions: + set_from_hexwkb + """ + return cls(_inner=(set_from_hexwkb(hexwkb))) + + # ------------------------- Output ---------------------------------------- + @abstractmethod + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + timestampset_out + """ + raise NotImplementedError() + + def __repr__(self): + """ + Return the string representation of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + set_out + """ + return (f'{self.__class__.__name__}' + f'({self})') + + def as_wkb(self) -> bytes: + """ + Returns the WKB representation of ``self``. + Returns: + A :class:`str` object with the WKB representation of ``self``. + + MEOS Functions: + set_as_wkb + """ + return set_as_wkb(self._inner, 4) + + def as_hexwkb(self) -> str: + """ + Returns the WKB representation of ``self`` in hex-encoded ASCII. + Returns: + A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. + + MEOS Functions: + set_as_hexwkb + """ + return set_as_hexwkb(self._inner, -1)[0] + + # ------------------------- Conversions ----------------------------------- + @abstractmethod + def to_spanset(self) -> SpanSet: + """ + Returns a PeriodSet that contains a Period for each Timestamp in ``self``. + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + set_to_spanset + """ + return set_to_spanset(self._inner) + + @abstractmethod + def to_span(self) -> Span: + """ + Returns a period that encompasses ``self``. + + Returns: + A new :class:`Period` instance + + MEOS Functions: + set_span + """ + return set_span(self._inner) + + # ------------------------- Accessors ------------------------------------- + + def num_elements(self) -> int: + """ + Returns the number of timestamps in ``self``. + Returns: + An :class:`int` + + MEOS Functions: + set_num_values + """ + return set_num_values(self._inner) + + @abstractmethod + def start_element(self) -> T: + """ + Returns the first timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + timestampset_start_timestamp + """ + raise NotImplementedError() + + @abstractmethod + def end_element(self) -> T: + """ + Returns the last timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + timestampset_end_timestamp + """ + raise NotImplementedError() + + @abstractmethod + def element_n(self, n: int) -> T: + """ + Returns the n-th timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + timestampset_timestamp_n + """ + raise NotImplementedError() + + @abstractmethod + def elements(self) -> List[T]: + """ + Returns the list of distinct timestamps in ``self``. + Returns: + A :class:`list[datetime]` instance + + MEOS Functions: + timestampset_timestamps + """ + raise NotImplementedError() + + def __hash__(self) -> int: + """ + Return the hash representation of ``self``. + + Returns: + A new :class:`int` instance + + MEOS Functions: + set_hash + """ + return set_hash(self._inner) + + # ------------------------- Topological Operations ------------------------ + def is_adjacent(self, other) -> bool: + """ + Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them + contains it. + Args: + other: temporal object to compare with + + Returns: + True if adjacent, False otherwise + + MEOS Functions: + adjacent_span_span, adjacent_spanset_span + """ + from .span import Span + from .spanset import SpanSet + if isinstance(other, Span): + return adjacent_span_span(set_span(self._inner), other._inner) + elif isinstance(other, SpanSet): + return adjacent_spanset_spanset(other._inner, set_to_spanset(self._inner)) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_contained_in(self, container) -> bool: + """ + Returns whether ``self`` is contained in ``container``. + + Args: + container: temporal object to compare with + + Returns: + True if contained, False otherwise + + MEOS Functions: + contained_span_span, contained_span_spanset, contained_set_set, contained_spanset_spanset + """ + from .span import Span + from .spanset import SpanSet + if isinstance(container, Span): + return contained_span_span(set_span(self._inner), container._inner) + elif isinstance(container, SpanSet): + return contained_span_spanset(set_span(self._inner), container._inner) + elif isinstance(container, Set): + return contained_set_set(self._inner, container._inner) + else: + raise TypeError(f'Operation not supported with type {container.__class__}') + + def contains(self, content) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_set_set + """ + if isinstance(content, Set): + return contains_set_set(self._inner, content._inner) + else: + raise TypeError(f'Operation not supported with type {content.__class__}') + + def __contains__(self, item): + """ + Returns whether ``self`` contains ``content``. + + Args: + item: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_set_set + """ + return self.contains(item) + + def overlaps(self, other) -> bool: + """ + Returns whether ``self`` overlaps ``other``. That is, both share at least an instant + + Args: + other: temporal object to compare with + + Returns: + True if overlaps, False otherwise + + MEOS Functions: + overlaps_set_set, overlaps_span_span, overlaps_spanset_spanset + """ + from .span import Span + from .spanset import SpanSet + if isinstance(other, Set): + return overlaps_set_set(self._inner, other._inner) + elif isinstance(other, Span): + return overlaps_span_span(set_span(self._inner), other._inner) + elif isinstance(other, SpanSet): + return overlaps_spanset_spanset(set_to_spanset(self._inner), other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_same(self, other) -> bool: + """ + Returns whether the bounding span of `self` is the same as the bounding span of `other`. + + Args: + other: An object to compare to `self`. + + Returns: + True if same, False otherwise. + + See Also: + :meth:`Span.is_same` + """ + return self.to_span().is_same(other) + + # ------------------------- Position Operations --------------------------- + def is_left(self, other) -> bool: + """ + Returns whether ``self`` is strictly to the left of ``other``. That is, ``self`` ends before ``other`` starts. + + Args: + other: object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + left_span_span, left_span_spanset + """ + from .span import Span + from .spanset import SpanSet + if isinstance(other, Set): + return left_set_set(self._inner, other._inner) + elif isinstance(other, Span): + return left_span_span(set_span(self._inner), other._inner) + elif isinstance(other, SpanSet): + return left_span_spanset(set_span(self._inner), other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_over_or_left(self, other) -> bool: + """ + Returns whether ``self`` is to the left of ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or + at the same time). + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overbefore_period_timestamp, overleft_span_span, overleft_span_spanset + """ + from .span import Span + from .spanset import SpanSet + if isinstance(other, Set): + return overleft_set_set(self._inner, other._inner) + elif isinstance(other, Span): + return overleft_span_span(set_span(self._inner), other._inner) + elif isinstance(other, SpanSet): + return overleft_span_spanset(set_span(self._inner), other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_over_or_right(self, other) -> bool: + """ + Returns whether ``self`` is to the right of ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts + (or at the same time). + + Args: + other: temporal object to compare with + + Returns: + True if overlapping or after, False otherwise + + MEOS Functions: + overafter_period_timestamp, overright_span_span, overright_span_spanset + """ + from .span import Span + from .spanset import SpanSet + if isinstance(other, Set): + return overright_set_set(self._inner, other._inner) + elif isinstance(other, Span): + return overright_span_span(set_span(self._inner), other._inner) + elif isinstance(other, SpanSet): + return overright_span_spanset(set_span(self._inner), other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_right(self, other) -> bool: + """ + Returns whether ``self`` is strictly to the right of ``other``. That is, the first timestamp in ``self`` + is after ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if after, False otherwise + + MEOS Functions: + overbefore_timestamp_timestampset, right_set_set, right_span_span, right_span_spanset + """ + from .span import Span + from .spanset import SpanSet + if isinstance(other, Set): + return right_set_set(self._inner, other._inner) + elif isinstance(other, Span): + return right_span_span(set_span(self._inner), other._inner) + elif isinstance(other, SpanSet): + return right_span_spanset(set_span(self._inner), other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Distance Operations --------------------------- + def distance(self, other) -> float: + """ + Returns the distance between ``self`` and ``other``. + + Args: + other: object to compare with + + Returns: + A :class:`datetime.float` instance + + MEOS Functions: + distance_set_set, distance_span_span, distance_spanset_span + """ + from .span import Span + from .spanset import SpanSet + if isinstance(other, Set): + return distance_set_set(self._inner, other._inner) + elif isinstance(other, Span): + return distance_span_span(set_span(self._inner), other._inner) + elif isinstance(other, SpanSet): + return distance_spanset_span(other._inner, set_span(self._inner)) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Set Operations -------------------------------- + @abstractmethod + def intersection(self, other): + """ + Returns the temporal intersection of ``self`` and ``other``. + + Args: + other: temporal object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_set_set, intersection_spanset_span, intersection_spanset_spanset + """ + from .span import Span + from .spanset import SpanSet + if isinstance(other, Set): + return intersection_set_set(self._inner, other._inner) + elif isinstance(other, Span): + return intersection_spanset_span(set_to_spanset(self._inner), other._inner) + elif isinstance(other, SpanSet): + return intersection_spanset_spanset(set_to_spanset(self._inner), other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __mul__(self, other): + """ + Returns the temporal intersection of ``self`` and ``other``. + + Args: + other: temporal object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_set_set, intersection_spanset_span, intersection_spanset_spanset + """ + return self.intersection(other) + + @abstractmethod + def minus(self, other): + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + minus_timestampset_timestamp, minus_set_set, minus_spanset_span, minus_spanset_spanset + """ + from .span import Span + from .spanset import SpanSet + if isinstance(other, Set): + return minus_set_set(self._inner, other._inner) + elif isinstance(other, Span): + return minus_spanset_span(set_to_spanset(self._inner), other._inner) + elif isinstance(other, SpanSet): + return minus_spanset_spanset(set_to_spanset(self._inner), other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __sub__(self, other): + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + minus_timestampset_timestamp, minus_set_set, minus_spanset_span, minus_spanset_spanset + """ + return self.minus(other) + + @abstractmethod + def union(self, other): + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + union_timestampset_timestamp, union_set_set, union_spanset_span, union_spanset_spanset + """ + from .span import Span + from .spanset import SpanSet + if isinstance(other, Set): + return union_set_set(self._inner, other._inner) + elif isinstance(other, Span): + return union_spanset_span(set_to_spanset(self._inner), other._inner) + elif isinstance(other, SpanSet): + return union_spanset_spanset(set_to_spanset(self._inner), other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __add__(self, other): + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + union_timestampset_timestamp, union_set_set, union_spanset_span, union_spanset_spanset + """ + return self.union(other) + + # ------------------------- Comparisons ----------------------------------- + def __eq__(self, other): + """ + Returns whether ``self`` and ``other`` are equal. + + Args: + other: temporal object to compare with + + Returns: + True if equal, False otherwise + + MEOS Functions: + set_eq + """ + if isinstance(other, self.__class__): + return set_eq(self._inner, other._inner) + return False + + def __ne__(self, other): + """ + Returns whether ``self`` and ``other`` are not equal. + + Args: + other: temporal object to compare with + + Returns: + True if not equal, False otherwise + + MEOS Functions: + set_ne + """ + if isinstance(other, self.__class__): + return set_ne(self._inner, other._inner) + return True + + def __lt__(self, other): + """ + Return whether ``self`` is less than ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if less than, False otherwise + + MEOS Functions: + set_lt + """ + if isinstance(other, self.__class__): + return set_lt(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __le__(self, other): + """ + Return whether ``self`` is less than or equal to ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if less than or equal, False otherwise + + MEOS Functions: + set_le + """ + if isinstance(other, self.__class__): + return set_le(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __gt__(self, other): + """ + Return whether ``self`` is greater than ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if greater than, False otherwise + + MEOS Functions: + set_gt + """ + if isinstance(other, self.__class__): + return set_gt(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __ge__(self, other): + """ + Return whether ``self`` is greater than or equal to ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if greater than or equal, False otherwise + + MEOS Functions: + set_ge + """ + if isinstance(other, self.__class__): + return set_ge(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') \ No newline at end of file diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index c94cca0a..215404b1 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -72,8 +72,7 @@ def from_wkb(cls: Type[Self], wkb: bytes) -> Self: MEOS Functions: span_from_wkb """ - result = span_from_wkb(wkb) - return cls(_inner=result) + return cls(_inner=(span_from_wkb(wkb))) @classmethod def from_hexwkb(cls: Type[Self], hexwkb: str) -> Self: @@ -362,7 +361,7 @@ def is_same(self, other) -> bool: raise TypeError(f'Operation not supported with type {other.__class__}') # ------------------------- Position Operations --------------------------- - def is_before(self, other) -> bool: + def is_left(self, other) -> bool: """ Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. @@ -386,7 +385,7 @@ def is_before(self, other) -> bool: else: raise TypeError(f'Operation not supported with type {other.__class__}') - def is_over_or_before(self, other) -> bool: + def is_over_or_left(self, other) -> bool: """ Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or at the same time). @@ -411,78 +410,78 @@ def is_over_or_before(self, other) -> bool: else: raise TypeError(f'Operation not supported with type {other.__class__}') - def is_after(self, other) -> bool: + def is_over_or_right(self, other) -> bool: """ - Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. + Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts + (or at the same time). Args: other: temporal object to compare with Returns: - True if after, False otherwise + True if overlapping or after, False otherwise MEOS Functions: - right_span_span, right_span_spanset + overright_span_span, overright_span_spanset, overafter_period_timestamp, + overafter_period_timestampset, overafter_period_temporal """ from .set import Set from .spanset import SpanSet if isinstance(other, Set): - return right_span_span(self._inner, set_span(other._inner)) + return overright_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Span): - return right_span_span(self._inner, other._inner) + return overright_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): - return right_span_spanset(self._inner, other._inner) + return overright_span_spanset(self._inner, other._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') - def is_over_or_after(self, other) -> bool: + def is_right(self, other) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. Args: other: temporal object to compare with Returns: - True if overlapping or after, False otherwise + True if after, False otherwise MEOS Functions: - overright_span_span, overright_span_spanset, overafter_period_timestamp, - overafter_period_timestampset, overafter_period_temporal + right_span_span, right_span_spanset """ from .set import Set from .spanset import SpanSet if isinstance(other, Set): - return overright_span_span(self._inner, set_span(other._inner)) + return right_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Span): - return overright_span_span(self._inner, other._inner) + return right_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): - return overright_span_spanset(self._inner, other._inner) + return right_span_spanset(self._inner, other._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') # ------------------------- Distance Operations --------------------------- - def distance(self, other) -> timedelta: + def distance(self, other) -> float: """ - Returns the temporal distance between ``self`` and ``other``. + Returns the distance between ``self`` and ``other``. Args: - other: temporal object to compare with + other: object to compare with Returns: - A :class:`datetime.timedelta` instance + A :class:`flat` instance MEOS Functions: - distance_span_span, distance_spanset_span, distance_period_timestamp + distance_span_span, distance_spanset_span """ from .set import Set from .spanset import SpanSet if isinstance(other, Set): - return timedelta(seconds=distance_span_span(self._inner, set_span(other._inner))) + return distance_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Span): - return timedelta(seconds=distance_span_span(self._inner, other._inner)) + return distance_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): - return timedelta(seconds=distance_spanset_span(other._inner, self._inner)) + return distance_spanset_span(other._inner, self._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') diff --git a/pymeos/pymeos/collections/time/period.py b/pymeos/pymeos/collections/time/period.py index 0fd2774c..95b83817 100644 --- a/pymeos/pymeos/collections/time/period.py +++ b/pymeos/pymeos/collections/time/period.py @@ -355,16 +355,16 @@ def is_same(self, other: Union[Time, Box, Temporal]) -> bool: super().is_same(other) # ------------------------- Position Operations --------------------------- - def is_before(self, other: Union[Time, Box, Temporal]) -> bool: + def is_left(self, other: Union[Time, Box, Temporal]) -> bool: """ Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. Examples: - >>> Period('[2012-01-01, 2012-01-02)').is_before(Period('[2012-01-02, 2012-01-03]')) + >>> Period('[2012-01-01, 2012-01-02)').is_left(Period('[2012-01-02, 2012-01-03]')) >>> True - >>> Period('[2012-01-01, 2012-01-02)').is_before(Period('(2012-01-02, 2012-01-03]')) + >>> Period('[2012-01-01, 2012-01-02)').is_left(Period('(2012-01-02, 2012-01-03]')) >>> True - >>> Period('[2012-01-01, 2012-01-02]').is_before(Period('[2012-01-02, 2012-01-03]')) + >>> Period('[2012-01-01, 2012-01-02]').is_left(Period('[2012-01-02, 2012-01-03]')) >>> False Args: @@ -382,23 +382,23 @@ def is_before(self, other: Union[Time, Box, Temporal]) -> bool: if isinstance(other, datetime): return overafter_timestamp_period(datetime_to_timestamptz(other), self._inner) elif isinstance(other, Temporal): - return self.is_before(other.period()) + return self.is_left(other.period()) elif isinstance(other, get_args(Box)): - return self.is_before(other.to_period()) + return self.is_left(other.to_period()) else: - super().is_before(other) + super().is_left(other) - def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: + def is_over_or_left(self, other: Union[Time, Box, Temporal]) -> bool: """ Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or at the same time). Examples: - >>> Period('[2012-01-01, 2012-01-02)').is_over_or_before(Period('[2012-01-02, 2012-01-03]')) + >>> Period('[2012-01-01, 2012-01-02)').is_over_or_left(Period('[2012-01-02, 2012-01-03]')) >>> True - >>> Period('[2012-01-01, 2012-01-02]').is_over_or_before(Period('[2012-01-02, 2012-01-03]')) + >>> Period('[2012-01-01, 2012-01-02]').is_over_or_left(Period('[2012-01-02, 2012-01-03]')) >>> True - >>> Period('[2012-01-03, 2012-01-05]').is_over_or_before(Period('[2012-01-01, 2012-01-04]')) + >>> Period('[2012-01-03, 2012-01-05]').is_over_or_left(Period('[2012-01-01, 2012-01-04]')) >>> False Args: @@ -416,22 +416,22 @@ def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: if isinstance(other, datetime): return overbefore_period_timestamp(self._inner, datetime_to_timestamptz(other)) elif isinstance(other, Temporal): - return self.is_over_or_before(other.period()) + return self.is_over_or_left(other.period()) elif isinstance(other, get_args(Box)): - return self.is_over_or_before(other.to_period()) + return self.is_over_or_left(other.to_period()) else: - super().is_over_or_before(other) + super().is_over_or_left(other) - def is_after(self, other: Union[Time, Box, Temporal]) -> bool: + def is_right(self, other: Union[Time, Box, Temporal]) -> bool: """ Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. Examples: - >>> Period('[2012-01-02, 2012-01-03]').is_after(Period('[2012-01-01, 2012-01-02)')) + >>> Period('[2012-01-02, 2012-01-03]').is_right(Period('[2012-01-01, 2012-01-02)')) >>> True - >>> Period('(2012-01-02, 2012-01-03]').is_after(Period('[2012-01-01, 2012-01-02)')) + >>> Period('(2012-01-02, 2012-01-03]').is_right(Period('[2012-01-01, 2012-01-02)')) >>> True - >>> Period('[2012-01-02, 2012-01-03]').is_after(Period('[2012-01-01, 2012-01-02]')) + >>> Period('[2012-01-02, 2012-01-03]').is_right(Period('[2012-01-01, 2012-01-02]')) >>> False Args: @@ -449,23 +449,23 @@ def is_after(self, other: Union[Time, Box, Temporal]) -> bool: if isinstance(other, datetime): return overbefore_timestamp_period(datetime_to_timestamptz(other), self._inner) elif isinstance(other, Temporal): - return self.is_after(other.period()) + return self.is_right(other.period()) elif isinstance(other, get_args(Box)): - return self.is_after(other.to_period()) + return self.is_right(other.to_period()) else: - super().is_after(other) + super().is_right(other) - def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: + def is_over_or_right(self, other: Union[Time, Box, Temporal]) -> bool: """ Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts (or at the same time). Examples: - >>> Period('[2012-01-02, 2012-01-03]').is_over_or_after(Period('[2012-01-01, 2012-01-02)')) + >>> Period('[2012-01-02, 2012-01-03]').is_over_or_right(Period('[2012-01-01, 2012-01-02)')) >>> True - >>> Period('[2012-01-02, 2012-01-03]').is_over_or_after(Period('[2012-01-01, 2012-01-02]')) + >>> Period('[2012-01-02, 2012-01-03]').is_over_or_right(Period('[2012-01-01, 2012-01-02]')) >>> True - >>> Period('[2012-01-02, 2012-01-03]').is_over_or_after(Period('[2012-01-01, 2012-01-03]')) + >>> Period('[2012-01-02, 2012-01-03]').is_over_or_right(Period('[2012-01-01, 2012-01-03]')) >>> False Args: @@ -483,11 +483,11 @@ def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: if isinstance(other, datetime): return overafter_period_timestamp(self._inner, datetime_to_timestamptz(other)) elif isinstance(other, Temporal): - return self.is_over_or_after(other.period()) + return self.is_over_or_right(other.period()) elif isinstance(other, get_args(Box)): - return self.is_over_or_after(other.to_period()) + return self.is_over_or_right(other.to_period()) else: - super().is_over_or_after(other) + super().is_over_or_right(other) # ------------------------- Distance Operations --------------------------- def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: @@ -512,7 +512,7 @@ def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: elif isinstance(other, get_args(Box)): return self.distance(other.to_period()) else: - super().distance(other) + return timedelta(seconds=super().distance(other)) # ------------------------- Set Operations -------------------------------- @overload diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py index 07ef893f..ba934094 100644 --- a/pymeos/pymeos/collections/time/timestampset.py +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -1,7 +1,693 @@ -from datetime import datetime +from __future__ import annotations -from ..base.set import Set +from datetime import datetime, timedelta +from typing import Optional, List, Union, TYPE_CHECKING, overload, get_args + +from dateutil.parser import parse +from pymeos_cffi import * + +from ..base import Set + +if TYPE_CHECKING: + from ...temporal import Temporal + from .period import Period + from .periodset import PeriodSet + from .time import Time + from ...boxes import Box class TimestampSet(Set[datetime]): - pass + """ + Class for representing lists of distinct timestamp values. + + ``TimestampSet`` objects can be created with a single argument of type string + as in MobilityDB. + + >>> TimestampSet(string='{2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01, 2019-09-11 00:00:00+01}') + + Another possibility is to give a tuple or list of composing timestamps, + which can be instances of ``str`` or ``datetime``. The composing timestamps + must be given in increasing order. + + >>> TimestampSet(timestamp_list=['2019-09-08 00:00:00+01', '2019-09-10 00:00:00+01', '2019-09-11 00:00:00+01']) + >>> TimestampSet(timestamp_list=[parse('2019-09-08 00:00:00+01'), parse('2019-09-10 00:00:00+01'), parse('2019-09-11 00:00:00+01')]) + + """ + + __slots__ = ['_inner'] + + _parse_function = timestampset_in + _parse_value_function = lambda x: pg_timestamptz_in(x, -1) if isinstance(x, str) else datetime_to_timestamptz(x) + _make_function = timestampset_make + + # ------------------------- Constructors ---------------------------------- + + # ------------------------- Output ---------------------------------------- + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + timestampset_out + """ + return timestampset_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + def to_spanset(self) -> PeriodSet: + """ + Returns a PeriodSet that contains a Period for each Timestamp in ``self``. + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + set_to_spanset + """ + from .periodset import PeriodSet + return PeriodSet(_inner=super().to_spanset()) + + def to_periodset(self) -> PeriodSet: + """ + Returns a PeriodSet that contains a Period for each Timestamp in ``self``. + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + set_to_spanset + """ + return self.to_spanset() + + def to_span(self) -> Period: + """ + Returns a period that encompasses ``self``. + + Returns: + A new :class:`Period` instance + + MEOS Functions: + set_span + """ + return Period(_inner=super().to_span()) + + def to_period(self) -> Period: + """ + Returns a period that encompasses ``self``. + + Returns: + A new :class:`Period` instance + + MEOS Functions: + set_span + """ + return self.to_span() + + # ------------------------- Accessors ------------------------------------- + def duration(self) -> timedelta: + """ + Returns the duration of the time ignoring gaps, i.e. the duration from the + first timestamp to the last one. + + Returns: + A :class:`datetime.timedelta` instance representing the duration of the period + + MEOS Functions: + period_duration + """ + return interval_to_timedelta(period_duration(set_span(self._inner))) + + def start_element(self) -> datetime: + """ + Returns the first timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + timestampset_start_timestamp + """ + return timestamptz_to_datetime(timestampset_start_timestamp(self._inner)) + + def end_element(self) -> datetime: + """ + Returns the last timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + timestampset_end_timestamp + """ + return timestamptz_to_datetime(timestampset_end_timestamp(self._inner)) + + def element_n(self, n: int) -> datetime: + """ + Returns the n-th timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + timestampset_timestamp_n + """ + return timestamptz_to_datetime(timestampset_timestamp_n(self._inner, n + 1)) + + def elements(self) -> List[datetime]: + """ + Returns the list of distinct timestamps in ``self``. + Returns: + A :class:`list[datetime]` instance + + MEOS Functions: + timestampset_timestamps + """ + tss = timestampset_values(self._inner) + return [timestamptz_to_datetime(tss[i]) for i in range(self.num_elements())] + + # ------------------------- Transformations ------------------------------- + def shift(self, delta: timedelta) -> TimestampSet: + """ + Returns a new TimestampSet that is the result of shifting ``self`` by ``delta`` + + Examples: + >>> TimestampSet('{2000-01-01, 2000-01-10}').shift(timedelta(days=2)) + >>> 'TimestampSet({2000-01-03 00:00:00+01, 2000-01-12 00:00:00+01})' + + Args: + delta: :class:`datetime.timedelta` instance to shift + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + timestampset_shift_tscale + """ + return self.shift_tscale(shift=delta) + + def tscale(self, duration: timedelta) -> TimestampSet: + """ + Returns a new TimestampSet that with the scaled so that the span of ``self`` is ``duration``. + + Examples: + >>> TimestampSet('{2000-01-01, 2000-01-10}').tscale(timedelta(days=2)) + >>> 'TimestampSet({2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01})' + + Args: + duration: :class:`datetime.timedelta` instance representing the span of the new set + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + timestampset_shift_tscale + """ + return self.shift_tscale(duration=duration) + + def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TimestampSet: + """ + Returns a new TimestampSet that is the result of shifting and scaling ``self``. + + Examples: + >>> TimestampSet('{2000-01-01, 2000-01-10}').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) + >>> 'TimestampSet({2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01})' + + Args: + shift: :class:`datetime.timedelta` instance to shift + duration: :class:`datetime.timedelta` instance representing the span of the new set + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + timestampset_shift_tscale + """ + assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' + tss = timestampset_shift_tscale( + self._inner, + timedelta_to_interval(shift) if shift else None, + timedelta_to_interval(duration) if duration else None + ) + return TimestampSet(_inner=tss) + + # ------------------------- Topological Operations ------------------------ + def is_adjacent(self, other: Union[Period, PeriodSet, Temporal, Box]) -> bool: + """ + Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them + contains it. + + Examples: + >>> TimestampSet('{2012-01-01, 2012-01-02}').is_adjacent(Period('[2012-01-02, 2012-01-03]')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').is_adjacent(Period('[2012-01-02, 2012-01-03]')) + >>> False # Both contain bound + >>> TimestampSet('{2012-01-01, 2012-01-02}').is_adjacent(Period('(2012-01-02, 2012-01-03]')) + >>> False # Neither contain bound + + Args: + other: temporal object to compare with + + Returns: + True if adjacent, False otherwise + + MEOS Functions: + adjacent_span_span, adjacent_spanset_span + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, Temporal): + return self.is_adjacent(other.time()) + elif isinstance(other, get_args(Box)): + return self.is_adjacent(other.to_period()) + else: + super().is_adjacent(other) + + def is_contained_in(self, container: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) -> bool: + """ + Returns whether ``self`` is temporally contained in ``container``. + + Examples: + >>> TimestampSet('{2012-01-02, 2012-01-03}').is_contained_in(Period('[2012-01-01, 2012-01-04]')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').is_contained_in(Period('[2012-01-01, 2012-01-02]')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').is_contained_in(Period('(2012-01-01, 2012-01-02)')) + >>> False + + Args: + container: temporal object to compare with + + Returns: + True if contained, False otherwise + + MEOS Functions: + contained_span_span, contained_span_spanset, contained_set_set, contained_spanset_spanset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(container, Temporal): + return self.is_contained_in(container.time()) + elif isinstance(container, Box): + return self.is_contained_in(container.to_period()) + else: + super().is_contained_in(container) + + def contains(self, content: Union[datetime, TimestampSet, Temporal]) -> bool: + """ + Returns whether ``self`` temporally contains ``content``. + + Examples: + >>> TimestampSet('{2012-01-01, 2012-01-04}').contains(parse('2012-01-01]')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').contains(TimestampSet('{2012-01-01}')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').contains(TimestampSet('{2012-01-01, 2012-01-03}')) + >>> False + + Args: + content: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_timestampset_timestamp, contains_set_set, contains_spanset_spanset + """ + from ...temporal import Temporal + if isinstance(content, datetime): + return contains_timestampset_timestamp(self._inner, datetime_to_timestamptz(content)) + elif isinstance(content, Temporal): + return self.to_spanset().contains(content) + else: + return super().contains(content) + + def __contains__(self, item): + """ + Returns whether ``self`` temporally contains ``content``. + + Examples: + >>> TimestampSet('{2012-01-01, 2012-01-04}').contains(parse('2012-01-01]')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').contains(TimestampSet('{2012-01-01}')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').contains(TimestampSet('{2012-01-01, 2012-01-03}')) + >>> False + + Args: + item: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_timestampset_timestamp, contains_set_set, contains_spanset_spanset + """ + return self.contains(item) + + def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) -> bool: + """ + Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant + + Examples: + >>> TimestampSet('{2012-01-01, 2012-01-02}').overlaps(TimestampSet('{2012-01-02, 2012-01-03}')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').overlaps(Period('[2012-01-02, 2012-01-03]')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').overlaps(Period('(2012-01-02, 2012-01-03]')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if overlaps, False otherwise + + MEOS Functions: + overlaps_set_set, overlaps_span_span, overlaps_spanset_spanset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return contains_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) + elif isinstance(other, Temporal): + return self.to_spanset().overlaps(other) + elif isinstance(other, Box): + return self.to_span().overlaps(other) + else: + return super().overlaps(other) + + def is_same(self, other: Union[Time, Temporal, Box]) -> bool: + """ + Returns whether the bounding period of `self` is the same as the bounding period of `other`. + + Args: + other: A time or temporal object to compare to `self`. + + Returns: + True if same, False otherwise. + + See Also: + :meth:`Period.is_same` + """ + return self.to_period().is_same(other) + + # ------------------------- Position Operations --------------------------- + def is_left(self, other: Union[Time, Temporal, Box]) -> bool: + """ + Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + + Examples: + >>> TimestampSet('{2012-01-01, 2012-01-02}').is_left(TimestampSet('{2012-01-03}')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').is_left(Period('(2012-01-02, 2012-01-03]')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').is_left(Period('[2012-01-02, 2012-01-03]')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overafter_timestamp_period, left_span_span, left_span_spanset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return after_timestamp_timestampset(datetime_to_timestamptz(other), self._inner) + elif isinstance(other, Temporal): + return self.to_period().is_left(other) + elif isinstance(other, get_args(Box)): + return self.to_period().is_left(other) + else: + return super().is_left(other) + + def is_over_or_left(self, other: Union[Time, Temporal, Box]) -> bool: + """ + Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or + at the same time). + + Examples: + >>> TimestampSet('{2012-01-01, 2012-01-02}').is_over_or_left(Period('[2012-01-02, 2012-01-03]')) + >>> True + >>> TimestampSet('{2012-01-01, 2012-01-02}').is_over_or_left(Period('[2012-01-02, 2012-01-03]')) + >>> True + >>> TimestampSet('{2012-01-03, 2012-01-05}').is_over_or_left(Period('[2012-01-01, 2012-01-04]')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overbefore_period_timestamp, overleft_span_span, overleft_span_spanset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return overafter_timestamp_timestampset(datetime_to_timestamptz(other), self._inner) + elif isinstance(other, Temporal): + return self.to_period().is_over_or_left(other) + elif isinstance(other, get_args(Box)): + return self.to_period().is_over_or_left(other.to_period()) + else: + super().is_over_or_left(other) + + def is_over_or_right(self, other: Union[Time, Temporal, Box]) -> bool: + """ + Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts + (or at the same time). + + Examples: + >>> TimestampSet('{2012-01-02, 2012-01-03}').is_over_or_right(Period('[2012-01-01, 2012-01-02)')) + >>> True + >>> TimestampSet('{2012-01-02, 2012-01-03}').is_over_or_right(Period('[2012-01-01, 2012-01-02]')) + >>> True + >>> TimestampSet('{2012-01-02, 2012-01-03}').is_over_or_right(Period('[2012-01-01, 2012-01-03]')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if overlapping or after, False otherwise + + MEOS Functions: + overafter_period_timestamp, overright_span_span, overright_span_spanset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return overbefore_timestamp_timestampset(datetime_to_timestamptz(other), self._inner) + elif isinstance(other, Temporal): + return self.to_period().is_over_or_right(other) + elif isinstance(other, get_args(Box)): + return self.to_period().is_over_or_right(other) + else: + super().is_over_or_right(other) + + def is_right(self, other: Union[Time, Temporal, Box]) -> bool: + """ + Returns whether ``self`` is strictly after ``other``. That is, the first timestamp in ``self`` + is after ``other``. + + Examples: + >>> TimestampSet('{2012-01-02, 2012-01-03}').is_right(Period('[2012-01-01, 2012-01-02)')) + >>> True + >>> TimestampSet('{2012-01-02, 2012-01-03}').is_right(TimestampSet('{2012-01-01}')) + >>> True + >>> TimestampSet('{2012-01-02, 2012-01-03}').is_right(Period('[2012-01-01, 2012-01-02]')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if after, False otherwise + + MEOS Functions: + overbefore_timestamp_timestampset, right_set_set, right_span_span, right_span_spanset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return before_timestamp_timestampset(datetime_to_timestamptz(other), self._inner) + elif isinstance(other, Temporal): + return self.to_period().is_right(other) + elif isinstance(other, get_args(Box)): + return self.to_period().is_right(other) + else: + return super().is_right(other) + + # ------------------------- Distance Operations --------------------------- + def distance(self, other: Union[Time, Temporal, Box]) -> timedelta: + """ + Returns the temporal distance between ``self`` and ``other``. + + Args: + other: temporal object to compare with + + Returns: + A :class:`datetime.timedelta` instance + + MEOS Functions: + distance_timestampset_timestamp, distance_set_set, distance_span_span, distance_spanset_span + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return timedelta(seconds=distance_timestampset_timestamp(self._inner, datetime_to_timestamptz(other))) + elif isinstance(other, Temporal): + return self.to_period().distance(other) + elif isinstance(other, get_args(Box)): + return self.to_period().distance(other) + else: + return timedelta(seconds=super().distance(other)) + + # ------------------------- Set Operations -------------------------------- + @overload + def intersection(self, other: datetime) -> Optional[datetime]: + ... + + @overload + def intersection(self, other: TimestampSet) -> Optional[TimestampSet]: + ... + + @overload + def intersection(self, other: Union[Period, PeriodSet, Temporal, Box]) -> Optional[PeriodSet]: + ... + + def intersection(self, other: Union[Time, Temporal]) -> Optional[Time]: + """ + Returns the temporal intersection of ``self`` and ``other``. + + Args: + other: temporal object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_set_set, intersection_spanset_span, intersection_spanset_spanset + """ + from .period import Period + from .periodset import PeriodSet + if isinstance(other, datetime): + result = intersection_set_set(self._inner, timestamp_to_tstzset(datetime_to_timestamptz(other))) + return timestamptz_to_datetime(result) if result is not None else None + elif isinstance(other, TimestampSet): + result = super().intersection(other) + return TimestampSet(_inner=result) if result is not None else None + elif isinstance(other, Period): + result = super().intersection(other) + return PeriodSet(_inner=result) if result is not None else None + elif isinstance(other, PeriodSet): + result = super().intersection(other) + return PeriodSet(_inner=result) if result is not None else None + elif isinstance(other, Temporal): + return self.intersection(other.time()) + elif isinstance(other, get_args(Box)): + return self.intersection(other.to_period()) + else: + super().intersection(other) + + @overload + def minus(self, other: Union[datetime, TimestampSet]) -> Optional[TimestampSet]: + ... + + @overload + def minus(self, other: Union[Period, PeriodSet, Temporal, Box]) -> Optional[PeriodSet]: + ... + + def minus(self, other: Union[Time, Temporal, Box]) -> Optional[Time]: + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + minus_timestampset_timestamp, minus_set_set, minus_spanset_span, minus_spanset_spanset + """ + from .period import Period + from .periodset import PeriodSet + if isinstance(other, datetime): + result = minus_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) + return TimestampSet(_inner=result) if result is not None else None + elif isinstance(other, TimestampSet): + result = super().minus(other) + return TimestampSet(_inner=result) if result is not None else None + elif isinstance(other, Period): + result = super().minus(other) + return PeriodSet(_inner=result) if result is not None else None + elif isinstance(other, PeriodSet): + result = super().minus(other) + return PeriodSet(_inner=result) if result is not None else None + elif isinstance(other, Temporal): + return self.minus(other.time()) + elif isinstance(other, get_args(Box)): + return self.minus(other.to_period()) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + @overload + def union(self, other: Union[datetime, TimestampSet]) -> TimestampSet: + ... + + @overload + def union(self, other: Union[Period, PeriodSet, Temporal, Box]) -> PeriodSet: + ... + + def union(self, other: Union[Time, Temporal, Box]) -> Union[PeriodSet, TimestampSet]: + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + union_timestampset_timestamp, union_set_set, union_spanset_span, union_spanset_spanset + """ + from .period import Period + from .periodset import PeriodSet + if isinstance(other, datetime): + return TimestampSet(_inner=union_timestampset_timestamp(self._inner, datetime_to_timestamptz(other))) + elif isinstance(other, TimestampSet): + return TimestampSet(_inner=super().union(other)) + elif isinstance(other, Period): + return PeriodSet(_inner=super().union(other)) + elif isinstance(other, PeriodSet): + return PeriodSet(_inner=super().union(other)) + elif isinstance(other, Temporal): + return self.union(other.time()) + elif isinstance(other, get_args(Box)): + return self.union(other.to_period()) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Comparisons ----------------------------------- + + # ------------------------- Plot Operations ------------------------------- + def plot(self, *args, **kwargs): + from ...plotters import TimePlotter + return TimePlotter.plot_timestampset(self, *args, **kwargs) + + # ------------------------- Database Operations --------------------------- + @staticmethod + def read_from_cursor(value, _=None): + """ + Reads a :class:`TimestampSet` from a database cursor. Used when automatically loading objects from the database. + Users should use the class constructor instead. + """ + if not value: + return None + return TimestampSet(string=value) diff --git a/pymeos/tests/time/period_test.py b/pymeos/tests/time/period_test.py index e8c51966..1df41bbc 100644 --- a/pymeos/tests/time/period_test.py +++ b/pymeos/tests/time/period_test.py @@ -261,7 +261,7 @@ def test_is_same(self, other): 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_before(self, other): - self.period.is_before(other) + self.period.is_left(other) @pytest.mark.parametrize( 'other', @@ -271,7 +271,7 @@ def test_is_before(self, other): 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_over_or_before(self, other): - self.period.is_over_or_before(other) + self.period.is_over_or_left(other) @pytest.mark.parametrize( 'other', @@ -281,7 +281,7 @@ def test_is_over_or_before(self, other): 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_after(self, other): - self.period.is_after(other) + self.period.is_right(other) @pytest.mark.parametrize( 'other', @@ -291,7 +291,7 @@ def test_is_after(self, other): 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_over_or_after(self, other): - self.period.is_over_or_after(other) + self.period.is_over_or_right(other) @pytest.mark.parametrize( 'other', diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 6dbeb318..f9272ef5 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -554,9 +554,11 @@ 'stbox_get_space', 'stbox_round', 'stbox_set_srid', + 'stbox_shift_tscale', 'tbox_expand_value', 'tbox_expand_time', 'tbox_round', + 'tbox_shift_tscale', 'contains_tbox_tbox', 'contained_tbox_tbox', 'overlaps_tbox_tbox', diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index 2d8ca1ce..aa479607 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -60,7 +60,6 @@ def __init__(self, ctype: str, ptype: str, conversion: Optional[str]) -> None: 'meos_finalize': remove_error_check_modifier, 'cstring2text': cstring2text_modifier, 'text2cstring': text2cstring_modifier, - 'timestampset_make': timestampset_make_modifier, 'gserialized_from_lwgeom': gserialized_from_lwgeom_modifier, 'spanset_make': spanset_make_modifier, 'temporal_from_wkb': from_wkb_modifier('temporal_from_wkb', 'Temporal'), @@ -75,6 +74,7 @@ def __init__(self, ctype: str, ptype: str, conversion: Optional[str]) -> None: 'spanset_as_wkb': as_wkb_modifier, 'tbox_as_wkb': as_wkb_modifier, 'stbox_as_wkb': as_wkb_modifier, + 'timestampset_make': timestampset_make_modifier, 'intset_make': array_parameter_modifier('values', 'count'), 'bigintset_make': array_parameter_modifier('values', 'count'), 'floatset_make': array_parameter_modifier('values', 'count'), diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py index 01ba25e4..6a0feea0 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions_modifiers.py @@ -73,8 +73,10 @@ def as_wkb_modifier(function: str) -> str: def timestampset_make_modifier(function: str) -> str: return function \ .replace('values: int', 'values: List[int]') \ + .replace(', count: int', '') \ .replace("values_converted = _ffi.cast('const TimestampTz *', values)", - "values_converted = [_ffi.cast('const TimestampTz', x) for x in values]") + "values_converted = [_ffi.cast('const TimestampTz', x) for x in values]") \ + .replace('count', 'len(values)') def spanset_make_modifier(function: str) -> str: diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index da956599..9058cc3a 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -1335,9 +1335,11 @@ extern STBox *stbox_expand_time(const STBox *box, const Interval *interval); extern STBox *stbox_get_space(const STBox *box); extern STBox *stbox_round(const STBox *box, int maxdd); extern STBox *stbox_set_srid(const STBox *box, int32 srid); +extern STBox *stbox_shift_tscale(const STBox *box, const Interval *shift, const Interval *duration); extern TBox *tbox_expand_value(const TBox *box, const double d); extern TBox *tbox_expand_time(const TBox *box, const Interval *interval); extern TBox *tbox_round(const TBox *box, int maxdd); +extern TBox *tbox_shift_tscale(const TBox *box, const Interval *shift, const Interval *duration); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index ab0afc09..552d14d1 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -969,9 +969,9 @@ def textset_make(values: List[str]) -> 'Set *': return result if result != _ffi.NULL else None -def timestampset_make(values: List[int], count: int) -> 'Set *': +def timestampset_make(values: List[int]) -> 'Set *': values_converted = [_ffi.cast('const TimestampTz', x) for x in values] - result = _lib.timestampset_make(values_converted, count) + result = _lib.timestampset_make(values_converted, len(values)) _check_error() return result if result != _ffi.NULL else None @@ -4108,6 +4108,15 @@ def stbox_set_srid(box: 'const STBox *', srid: int) -> 'STBox *': return result if result != _ffi.NULL else None +def stbox_shift_tscale(box: 'const STBox *', shift: 'const Interval *', duration: 'const Interval *') -> 'STBox *': + box_converted = _ffi.cast('const STBox *', box) + shift_converted = _ffi.cast('const Interval *', shift) + duration_converted = _ffi.cast('const Interval *', duration) + result = _lib.stbox_shift_tscale(box_converted, shift_converted, duration_converted) + _check_error() + return result if result != _ffi.NULL else None + + def tbox_expand_value(box: 'const TBox *', d: 'const double') -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) d_converted = _ffi.cast('const double', d) @@ -4131,6 +4140,15 @@ def tbox_round(box: 'const TBox *', maxdd: int) -> 'TBox *': return result if result != _ffi.NULL else None +def tbox_shift_tscale(box: 'const TBox *', shift: 'const Interval *', duration: 'const Interval *') -> 'TBox *': + box_converted = _ffi.cast('const TBox *', box) + shift_converted = _ffi.cast('const Interval *', shift) + duration_converted = _ffi.cast('const Interval *', duration) + result = _lib.tbox_shift_tscale(box_converted, shift_converted, duration_converted) + _check_error() + return result if result != _ffi.NULL else None + + def contains_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'bool': box1_converted = _ffi.cast('const TBox *', box1) box2_converted = _ffi.cast('const TBox *', box2) From 3615220dfcfaa19748039f79ba96bfeb12b6deb4 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Fri, 8 Sep 2023 14:45:59 +0200 Subject: [PATCH 021/101] Improve tests --- pymeos/pymeos/boxes/stbox.py | 359 +++++++++++++++--------- pymeos/pymeos/boxes/tbox.py | 174 +++++++----- pymeos/pymeos/main/tbool.py | 78 +++-- pymeos/pymeos/main/tfloat.py | 230 ++++++++++----- pymeos/pymeos/main/tint.py | 175 ++++++++---- pymeos/pymeos/main/tnumber.py | 100 ++++--- pymeos/pymeos/main/tpoint.py | 141 ++++++---- pymeos/pymeos/main/ttext.py | 192 ++++++++----- pymeos/pymeos/temporal/interpolation.py | 9 +- pymeos/pymeos/temporal/temporal.py | 266 +++++++++++------- pymeos/pymeos/temporal/tinstant.py | 12 +- pymeos/pymeos/temporal/tsequence.py | 45 +-- pymeos/pymeos/temporal/tsequenceset.py | 22 +- pymeos/pymeos/time/period.py | 216 ++++++++------ pymeos/pymeos/time/periodset.py | 241 ++++++++++------ pymeos/pymeos/time/timestampset.py | 248 ++++++++++------ pymeos/tests/boxes/stbox_test.py | 1 + pymeos/tests/boxes/tbox_test.py | 1 + pymeos/tests/main/tbool_test.py | 2 +- pymeos/tests/main/tfloat_test.py | 17 +- pymeos/tests/main/tgeogpoint_test.py | 1 + pymeos/tests/main/tgeompoint_test.py | 66 +++-- pymeos/tests/main/tint_test.py | 35 ++- pymeos/tests/main/ttext_test.py | 1 + pymeos/tests/time/period_test.py | 6 +- pymeos/tests/time/periodset_test.py | 1 + pymeos/tests/time/timestampset_test.py | 1 + 27 files changed, 1703 insertions(+), 937 deletions(-) diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index f08ade92..4d08d44a 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -18,7 +18,8 @@ class STBox: """ - Class for representing a spatio-temporal box. Temporal bounds may be inclusive or exclusive. + Class for representing a spatio-temporal box. Temporal bounds may be inclusive or + exclusive. ``STBox`` objects can be created with a single argument of type string as in MobilityDB. @@ -39,7 +40,8 @@ class STBox: """ __slots__ = ['_inner'] - def _get_box(self, other: Union[Geometry, STBox, Temporal, Time], allow_space_only: bool = True, + def _get_box(self, other: Union[Geometry, STBox, Temporal, Time], + allow_space_only: bool = True, allow_time_only: bool = False) -> STBox: if allow_space_only and isinstance(other, get_args(Geometry)): other_box = geo_to_stbox(geo_to_gserialized(other, self.geodetic())) @@ -76,10 +78,11 @@ def __init__(self, string: Optional[str] = None, *, _inner=None): assert (_inner is not None) or (string is not None) != ( - (xmin is not None and xmax is not None and ymin is not None and ymax is not None) or + (xmin is not None and xmax is not None and ymin is not None and + ymax is not None) or (tmin is not None and tmax is not None)), \ - "Either string must be not None or at least a bound pair (xmin/max and ymin/max, or tmin/max)" \ - " must be not None" + "Either string must be not None or at least a bound pair (xmin/max" \ + " and ymin/max, or tmin/max) must be not None" if _inner is not None: self._inner = _inner @@ -88,12 +91,16 @@ def __init__(self, string: Optional[str] = None, *, else: period = None hast = tmin is not None and tmax is not None - hasx = xmin is not None and xmax is not None and ymin is not None and ymax is not None + hasx = xmin is not None and xmax is not None and ymin is not None \ + and ymax is not None hasz = zmin is not None and zmax is not None if hast: - period = Period(lower=tmin, upper=tmax, lower_inc=tmin_inc, upper_inc=tmax_inc)._inner - self._inner = stbox_make(hasx, hasz, geodetic, srid or 0, float(xmin or 0), float(xmax or 0), - float(ymin or 0), float(ymax or 0), float(zmin or 0), float(zmax or 0), period) + period = Period(lower=tmin, upper=tmax, lower_inc=tmin_inc, + upper_inc=tmax_inc)._inner + self._inner = stbox_make(hasx, hasz, geodetic, srid or 0, + float(xmin or 0), float(xmax or 0), + float(ymin or 0), float(ymax or 0), + float(zmin or 0), float(zmax or 0), period) def __copy__(self) -> STBox: """ @@ -172,7 +179,8 @@ def from_time(time: Time) -> STBox: A new :class:`STBox` instance. MEOS Functions: - timestamp_to_stbox, timestampset_to_stbox, period_to_stbox, periodset_to_stbox + timestamp_to_stbox, timestampset_to_stbox, period_to_stbox, + periodset_to_stbox """ if isinstance(time, datetime): result = timestamp_to_stbox(datetime_to_timestamptz(time)) @@ -209,7 +217,8 @@ def from_geometry_time(geometry: Geometry, time: Union[datetime, Period], elif isinstance(time, Period): result = geo_period_to_stbox(gs, time._inner) else: - raise TypeError(f'Operation not supported with types {geometry.__class__} and {time.__class__}') + raise TypeError(f'Operation not supported with types ' \ + '{geometry.__class__} and {time.__class__}') return STBox(_inner=result) @staticmethod @@ -301,7 +310,8 @@ def as_hexwkb(self) -> str: Returns the WKB representation of ``self`` in hex-encoded ASCII. Returns: - A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. + A :class:`str` object with the WKB representation of ``self`` in + hex-encoded ASCII. MEOS Functions: stbox_as_hexwkb @@ -311,7 +321,8 @@ def as_hexwkb(self) -> str: # ------------------------- Conversions ---------------------------------- def to_geometry(self, precision: int = 15) -> shp.BaseGeometry: """ - Returns the spatial dimension of ``self`` as a `shapely` :class:`~shapely.BaseGeometry` instance. + Returns the spatial dimension of ``self`` as a `shapely` + :class:`~shapely.BaseGeometry` instance. Args: precision: The precision of the geometry coordinates. @@ -322,7 +333,8 @@ def to_geometry(self, precision: int = 15) -> shp.BaseGeometry: MEOS Functions: stbox_to_geo """ - return gserialized_to_shapely_geometry(stbox_to_geo(self._inner), precision) + return gserialized_to_shapely_geometry(stbox_to_geo(self._inner), + precision) def to_period(self) -> Period: """ @@ -442,7 +454,8 @@ def tmin_inc(self) -> bool: Returns whether starting time of ``self`` is inclusive or not Returns: - True if the starting time of ``self`` is inclusive and False otherwise + True if the starting time of ``self`` is inclusive and False + otherwise MEOS Functions: stbox_tmin_inc @@ -543,7 +556,8 @@ def set_srid(self, value: int) -> STBox: # ------------------------- Transformations ------------------------------- def get_space(self) -> STBox: """ - Get the spatial dimension of ``self``, removing the temporal dimension if any + Get the spatial dimension of ``self``, removing the temporal dimension + if any Returns: A new :class:`STBox` instance. @@ -557,9 +571,11 @@ def get_space(self) -> STBox: def expand(self, other: Union[int, float, timedelta]) -> STBox: """ Expands ``self`` with `other`. - If `other` is a :class:`int` or a :class:`float`, the result is equal to ``self`` but with the spatial dimensions - expanded by `other` in all directions. If `other` is a :class:`timedelta`, the result is equal to ``self`` - but with the temporal dimension expanded by `other` in both directions. + If `other` is a :class:`int` or a :class:`float`, the result is equal + to ``self`` but with the spatial dimensions expanded by `other` in all + directions. If `other` is a :class:`timedelta`, the result is equal to + ``self`` but with the temporal dimension expanded by `other` in both + directions. Args: other: The object to expand ``self`` with. @@ -573,7 +589,8 @@ def expand(self, other: Union[int, float, timedelta]) -> STBox: if isinstance(other, int) or isinstance(other, float): result = stbox_expand_space(self._inner, float(other)) elif isinstance(other, timedelta): - result = stbox_expand_time(self._inner, timedelta_to_interval(other)) + result = stbox_expand_time(self._inner, + timedelta_to_interval(other)) else: raise TypeError(f'Operation not supported with type {other.__class__}') return STBox(_inner=result) @@ -598,7 +615,8 @@ def shift(self, delta: timedelta) -> STBox: def tscale(self, duration: timedelta) -> STBox: """ - Returns a new `STBox` with the time dimension having duration `duration`. + Returns a new `STBox` with the time dimension having duration + `duration`. Args: duration: :class:`datetime.timedelta` instance with new duration @@ -617,7 +635,8 @@ def tscale(self, duration: timedelta) -> STBox: def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> STBox: """ - Returns a new `STBox` with the time dimension shifted by `shift` and with duration `duration`. + Returns a new `STBox` with the time dimension shifted by `shift` and + with duration `duration`. Args: shift: :class:`datetime.timedelta` instance to shift @@ -632,7 +651,8 @@ def shift_tscale(self, shift: Optional[timedelta] = None, See Also: :meth:`Period.shift_tscale` """ - assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' + assert shift is not None or duration is not None, \ + 'shift and scale deltas must not be both None' result = stbox_shift_tscale( self._inner, timedelta_to_interval(shift) if shift else None, @@ -676,7 +696,8 @@ def union(self, other: STBox, strict: Optional[bool] = False) -> STBox: def __add__(self, other): """ - Returns the union of `self` with `other`. Fails if the union is not contiguous. + Returns the union of `self` with `other`. Fails if the union is not + contiguous. Args: other: spatiotemporal box to merge with @@ -698,7 +719,8 @@ def intersection(self, other: STBox) -> Optional[STBox]: other: temporal object to merge with Returns: - A :class:`STBox` instance if the instersection is not empty, `None` otherwise. + A :class:`STBox` instance if the instersection is not empty, `None` + otherwise. MEOS Functions: intersection_stbox_stbox @@ -714,7 +736,8 @@ def __mul__(self, other): other: temporal object to merge with Returns: - A :class:`STBox` instance if the instersection is not empty, `None` otherwise. + A :class:`STBox` instance if the instersection is not empty, `None` + otherwise. MEOS Functions: intersection_stbox_stbox @@ -724,12 +747,14 @@ def __mul__(self, other): # ------------------------- Topological Operations ------------------------ def is_adjacent(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: """ - Returns whether ``self`` and `other` are adjacent. Two spatiotemporal boxes are adjacent if they share n - dimensions and the intersection is of at most n-1 dimensions. Note that for `TPoint` instances, the bounding box - of the temporal point is used. + Returns whether ``self`` and `other` are adjacent. Two spatiotemporal + boxes are adjacent if they share n dimensions and the intersection is + of at most n-1 dimensions. Note that for `TPoint` instances, the + bounding box of the temporal point is used. Args: - other: The other spatiotemporal object to check adjacency with ``self``. + other: The other spatiotemporal object to check adjacency with + ``self``. Returns: ``True`` if ``self`` and `other` are adjacent, ``False`` otherwise. @@ -737,15 +762,18 @@ def is_adjacent(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: MEOS Functions: adjacent_stbox_stbox """ - return adjacent_stbox_stbox(self._inner, self._get_box(other, allow_time_only=True)) + return adjacent_stbox_stbox(self._inner, self._get_box(other, + allow_time_only=True)) - def is_contained_in(self, container: Union[Geometry, STBox, Temporal, Time]) -> bool: + def is_contained_in(self, + container: Union[Geometry, STBox, Temporal, Time]) -> bool: """ - Returns whether ``self`` is contained in `container`. Note that for `TPoint` instances, the bounding - box of the temporal point is used. + Returns whether ``self`` is contained in `container`. Note that for + `TPoint` instances, the bounding box of the temporal point is used. Args: - container: The spatiotemporal object to check containment with ``self``. + container: The spatiotemporal object to check containment with + ``self``. Returns: ``True`` if ``self`` is contained in `container`, ``False`` otherwise. @@ -753,15 +781,17 @@ def is_contained_in(self, container: Union[Geometry, STBox, Temporal, Time]) -> MEOS Functions: contained_stbox_stbox """ - return contained_stbox_stbox(self._inner, self._get_box(container, allow_time_only=True)) + return contained_stbox_stbox(self._inner, self._get_box(container, + allow_time_only=True)) def contains(self, content: Union[Geometry, STBox, Temporal, Time]) -> bool: """ - Returns whether ``self`` contains `content`. Note that for `TPoint` instances, the bounding box of - the temporal point is used. + Returns whether ``self`` contains `content`. Note that for `TPoint` + instances, the bounding box of the temporal point is used. Args: - content: The spatiotemporal object to check containment with ``self``. + content: The spatiotemporal object to check containment with + ``self``. Returns: ``True`` if ``self`` contains `content`, ``False`` otherwise. @@ -769,14 +799,16 @@ def contains(self, content: Union[Geometry, STBox, Temporal, Time]) -> bool: MEOS Functions: contains_stbox_stbox """ - return contains_stbox_stbox(self._inner, self._get_box(content, allow_time_only=True)) + return contains_stbox_stbox(self._inner, self._get_box(content, + allow_time_only=True)) def __contains__(self, item): """ Returns whether ``self`` contains `item`. Args: - item: The spatiotemporal object to check if it is contained in ``self``. + item: The spatiotemporal object to check if it is contained in + ``self``. Returns: ``True`` if ``self`` contains ``item``, ``False`` otherwise. @@ -791,8 +823,8 @@ def __contains__(self, item): def overlaps(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: """ - Returns whether ``self`` overlaps `other`. Note that for `TPoint` instances, the bounding box of - the temporal point is used. + Returns whether ``self`` overlaps `other`. Note that for `TPoint` + instances, the bounding box of the temporal point is used. Args: other: The spatiotemporal object to check overlap with ``self``. @@ -803,12 +835,13 @@ def overlaps(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: MEOS Functions: overlaps_stbox_stbox """ - return overlaps_stbox_stbox(self._inner, self._get_box(other, allow_time_only=True)) + return overlaps_stbox_stbox(self._inner, self._get_box(other, + allow_time_only=True)) def is_same(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: """ - Returns whether ``self`` is the same as `other`. Note that for `TPoint` instances, the bounding box of - the temporal point is used. + Returns whether ``self`` is the same as `other`. Note that for `TPoint` + instances, the bounding box of the temporal point is used. Args: other: The spatiotemporal object to check equality with ``self``. @@ -819,18 +852,21 @@ def is_same(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: MEOS Functions: same_stbox_stbox """ - return same_stbox_stbox(self._inner, self._get_box(other, allow_time_only=True)) + return same_stbox_stbox(self._inner, self._get_box(other, + allow_time_only=True)) # ------------------------- Position Operations --------------------------- def is_left(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is strictly to the left of `other`. Checks the X dimension. + Returns whether ``self`` is strictly to the left of `other`. Checks + the X dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is strictly to the left of `other`, ``False`` otherwise. + ``True`` if ``self`` is strictly to the left of `other`, + ``False`` otherwise. MEOS Functions: left_stbox_stbox @@ -839,14 +875,16 @@ def is_left(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_over_or_left(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is to the left `other` allowing for overlap. That is, ``self`` does not extend - to the right of `other`. Checks the X dimension. + Returns whether ``self`` is to the left `other` allowing for overlap. + That is, ``self`` does not extend to the right of `other`. Checks the + X dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is to the left of `other` allowing for overlap, ``False`` otherwise. + ``True`` if ``self`` is to the left of `other` allowing for + overlap, ``False`` otherwise. MEOS Functions: overleft_stbox_stbox, tpoint_to_stbox @@ -855,13 +893,15 @@ def is_over_or_left(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_right(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is strictly to the right of `other`. Checks the X dimension. + Returns whether ``self`` is strictly to the right of `other`. + Checks the X dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is strictly to the right of `other`, ``False`` otherwise. + ``True`` if ``self`` is strictly to the right of `other`, + ``False`` otherwise. MEOS Functions: right_stbox_stbox @@ -870,14 +910,16 @@ def is_right(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_over_or_right(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is to the right of `other` allowing for overlap. That is, ``self`` does not - extend to the left of `other`. Checks the X dimension. + Returns whether ``self`` is to the right of `other` allowing for + overlap. That is, ``self`` does not extend to the left of `other`. + Checks the X dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is to the right of `other` allowing for overlap, ``False`` otherwise. + ``True`` if ``self`` is to the right of `other` allowing for + overlap, ``False`` otherwise. MEOS Functions: overright_stbox_stbox @@ -886,13 +928,15 @@ def is_over_or_right(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_below(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is strictly below `other`. Checks the Y dimension. + Returns whether ``self`` is strictly below `other`. + Checks the Y dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is strictly below `other`, ``False`` otherwise. + ``True`` if ``self`` is strictly below `other`, ``False`` + otherwise. MEOS Functions: below_stbox_stbox @@ -901,14 +945,15 @@ def is_below(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_over_or_below(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is below `other` allowing for overlap. That is, ``self`` does not extend - above `other`. Checks the Y dimension. + Returns whether ``self`` is below `other` allowing for overlap. + That is, ``self`` does not extend above `other`. Checks the Y dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is below `other` allowing for overlap, ``False`` otherwise. + ``True`` if ``self`` is below `other` allowing for overlap, + ``False`` otherwise. MEOS Functions: overbelow_stbox_stbox @@ -917,13 +962,15 @@ def is_over_or_below(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_above(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is strictly above `other`. Checks the Y dimension. + Returns whether ``self`` is strictly above `other`. + Checks the Y dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is strictly above `other`, ``False`` otherwise. + ``True`` if ``self`` is strictly above `other`, ``False`` + otherwise. MEOS Functions: above_stbox_stbox @@ -932,14 +979,16 @@ def is_above(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_over_or_above(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is above `other` allowing for overlap. That is, ``self`` does not extend - below `other`. Checks the Y dimension. + Returns whether ``self`` is above `other` allowing for overlap. + That is, ``self`` does not extend below `other`. + Checks the Y dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is above `other` allowing for overlap, ``False`` otherwise. + ``True`` if ``self`` is above `other` allowing for overlap, + ``False`` otherwise. MEOS Functions: overabove_stbox_stbox @@ -948,13 +997,15 @@ def is_over_or_above(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_front(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is strictly in front of `other`. Checks the Z dimension. + Returns whether ``self`` is strictly in front of `other`. + Checks the Z dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is strictly in front of `other`, ``False`` otherwise. + ``True`` if ``self`` is strictly in front of `other`, ``False`` + otherwise. MEOS Functions: front_stbox_stbox @@ -963,14 +1014,16 @@ def is_front(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_over_or_front(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is in front of `other` allowing for overlap. That is, ``self`` does not extend - behind `other`. Checks the Z dimension. + Returns whether ``self`` is in front of `other` allowing for overlap. + That is, ``self`` does not extend behind `other`. + Checks the Z dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is in front of `other` allowing for overlap, ``False`` otherwise. + ``True`` if ``self`` is in front of `other` allowing for overlap, + ``False`` otherwise. MEOS Functions: overfront_stbox_stbox @@ -979,13 +1032,15 @@ def is_over_or_front(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_behind(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is strictly behind `other`. Checks the Z dimension. + Returns whether ``self`` is strictly behind `other`. + Checks the Z dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is strictly behind `other`, ``False`` otherwise. + ``True`` if ``self`` is strictly behind `other`, ``False`` + otherwise. MEOS Functions: back_stbox_stbox @@ -994,14 +1049,16 @@ def is_behind(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_over_or_behind(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ - Returns whether ``self`` is behind `other` allowing for overlap. That is, ``self`` does not extend - in front of `other`. Checks the Z dimension. + Returns whether ``self`` is behind `other` allowing for overlap. + That is, ``self`` does not extend in front of `other`. + Checks the Z dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is behind `other` allowing for overlap, ``False`` otherwise. + ``True`` if ``self`` is behind `other` allowing for overlap, + ``False`` otherwise. MEOS Functions: overback_stbox_stbox @@ -1010,56 +1067,65 @@ def is_over_or_behind(self, other: Union[Geometry, STBox, TPoint]) -> bool: def is_before(self, other: Union[Box, Temporal, Time]) -> bool: """ - Returns whether ``self`` is strictly before `other`. Checks the time dimension. + Returns whether ``self`` is strictly before `other`. + Checks the time dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is strictly before `other`, ``False`` otherwise. + ``True`` if ``self`` is strictly before `other`, ``False`` + otherwise. """ return self.to_period().is_before(other) def is_over_or_before(self, other: Union[Box, Temporal, Time]) -> bool: """ - Returns whether ``self`` is before `other` allowing for overlap. That is, ``self`` does not extend - after `other`. Checks the time dimension. + Returns whether ``self`` is before `other` allowing for overlap. + That is, ``self`` does not extend after `other`. + Checks the time dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is before `other` allowing for overlap, ``False`` otherwise. + ``True`` if ``self`` is before `other` allowing for overlap, + ``False`` otherwise. """ return self.to_period().is_over_or_before(other) def is_after(self, other: Union[Box, Temporal, Time]) -> bool: """ - Returns whether ``self`` is strictly after `other`. Checks the time dimension. + Returns whether ``self`` is strictly after `other`. + Checks the time dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is strictly after `other`, ``False`` otherwise. + ``True`` if ``self`` is strictly after `other`, ``False`` + otherwise. """ return self.to_period().is_after(other) def is_over_or_after(self, other: Union[Box, Temporal, Time]) -> bool: """ - Returns whether ``self`` is after `other` allowing for overlap. That is, ``self`` does not extend - before `other`. Checks the time dimension. + Returns whether ``self`` is after `other` allowing for overlap. + That is, ``self`` does not extend before `other`. + Checks the time dimension. Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is after `other` allowing for overlap, ``False`` otherwise. + ``True`` if ``self`` is after `other` allowing for overlap, + ``False`` otherwise. """ return self.to_period().is_over_or_after(other) # ------------------------- Distance Operations --------------------------- - def nearest_approach_distance(self, other: Union[Geometry, STBox, TPoint]) -> float: + def nearest_approach_distance(self, other: Union[Geometry, STBox, TPoint]) \ + -> float: """ Returns the distance between the nearest points of ``self`` and `other`. @@ -1067,7 +1133,8 @@ def nearest_approach_distance(self, other: Union[Geometry, STBox, TPoint]) -> fl other: The spatiotemporal object to compare with ``self``. Returns: - A :class:`float` with the distance between the nearest points of ``self`` and ``other``. + A :class:`float` with the distance between the nearest points of + ``self`` and ``other``. MEOS Functions: nad_stbox_geo, nad_stbox_stbox @@ -1085,10 +1152,11 @@ def nearest_approach_distance(self, other: Union[Geometry, STBox, TPoint]) -> fl # ------------------------- Splitting -------------------------------------- def quad_split_flat(self) -> List[STBox]: """ - Returns a list of 4 (or 8 if `self`has Z dimension) :class:`STBox` instances resulting from the quad - split of ``self``. + Returns a list of 4 (or 8 if `self`has Z dimension) :class:`STBox` + instances resulting from the quad split of ``self``. - Indices of returned array are as follows (back only present if Z dimension is present): + Indices of returned array are as follows (back only present if Z + dimension is present): >>> # (front) (back) >>> # ------------- ------------- @@ -1108,7 +1176,8 @@ def quad_split_flat(self) -> List[STBox]: def quad_split(self) -> Union[List[List[STBox]], List[List[List[STBox]]]]: """ - Returns a 2D (YxX) or 3D (ZxYxX) list of :class:`STBox` instances resulting from the quad split of ``self``. + Returns a 2D (YxX) or 3D (ZxYxX) list of :class:`STBox` instances + resulting from the quad split of ``self``. Indices of returned array are as follows: @@ -1139,28 +1208,38 @@ def quad_split(self) -> Union[List[List[STBox]], List[List[List[STBox]]]]: boxes, count = stbox_quad_split(self._inner) if self.has_z(): return [ - [[STBox(_inner=boxes + i) for i in range(2)], [STBox(_inner=boxes + i) for i in range(2, 4)]], - [[STBox(_inner=boxes + i) for i in range(4, 6)], [STBox(_inner=boxes + i) for i in range(6, 8)]] + [[STBox(_inner=boxes + i) for i in range(2)], + [STBox(_inner=boxes + i) for i in range(2, 4)]], + [[STBox(_inner=boxes + i) for i in range(4, 6)], + [STBox(_inner=boxes + i) for i in range(6, 8)]] ] else: - return [[STBox(_inner=boxes + i) for i in range(2)], [STBox(_inner=boxes + i) for i in range(2, 4)]] + return [[STBox(_inner=boxes + i) for i in range(2)], + [STBox(_inner=boxes + i) for i in range(2, 4)]] - def tile(self, size: Optional[float] = None, duration: Optional[Union[timedelta, str]] = None, + def tile(self, size: Optional[float] = None, + duration: Optional[Union[timedelta, str]] = None, origin: Optional[Geometry] = None, - start: Union[datetime, str, None] = None) -> List[List[List[List[STBox]]]]: + start: Union[datetime, str, None] = None) -> \ + List[List[List[List[STBox]]]]: """ - Returns a 4D matrix (XxYxZxT) of `STBox` instances representing the tiles of ``self``. - The resulting matrix has 4 dimensions regardless of the dimensionality of ``self``. If the ``self`` - is missing a dimension, the resulting matrix will have a size of 1 for that dimension. + Returns a 4D matrix (XxYxZxT) of `STBox` instances representing the + tiles of ``self``. The resulting matrix has 4 dimensions regardless + of the dimensionality of ``self``. If the ``self`` is missing a + dimension, the resulting matrix will have a size of 1 for that dimension. Args: - size: The size of the spatial tiles. If the `STBox` instance has a spatial dimension and this - argument is not provided, the tiling will be only temporal. - duration: The duration of the temporal tiles. If the `STBox` instance has a time dimension and this - argument is not provided, the tiling will be only spatial. - origin: The origin of the spatial tiling. If not provided, the origin will be (0, 0, 0). - start: The start time of the temporal tiling. If not provided, the start time will be the starting time of - the `STBox` time dimension. + size: The size of the spatial tiles. If the `STBox` instance has a + spatial dimension and this argument is not provided, the tiling + will be only temporal. + duration: The duration of the temporal tiles. If the `STBox` + instance has a time dimension and this argument is not + provided, the tiling will be only spatial. + origin: The origin of the spatial tiling. If not provided, the + origin will be (0, 0, 0). + start: The start time of the temporal tiling. If not provided, + the start time will be the starting time of the `STBox` + time dimension. Returns: A 4D matrix (XxYxZxT) of `STBox` instances. @@ -1188,23 +1267,30 @@ def tile(self, size: Optional[float] = None, duration: Optional[Union[timedelta, x_factor = y_size * z_size * t_size y_factor = z_size * t_size z_factor = t_size - return [[[[STBox(_inner=tiles + x * x_factor + y * y_factor + z * z_factor + t) for t in range(t_size)] - for z in range(z_size)] for y in range(y_size)] for x in range(x_size)] + return [[[[STBox(_inner=tiles + x * x_factor + y * y_factor + z * z_factor + t) + for t in range(t_size)] + for z in range(z_size)] for y in range(y_size)] for x in range(x_size)] - def tile_flat(self, size: float, duration: Optional[Union[timedelta, str]] = None, + def tile_flat(self, size: float, + duration: Optional[Union[timedelta, str]] = None, origin: Optional[Geometry] = None, start: Union[datetime, str, None] = None) -> List[STBox]: """ - Returns a flat list of `STBox` instances representing the tiles of ``self``. + Returns a flat list of `STBox` instances representing the tiles of + ``self``. Args: - size: The size of the spatial tiles. If the `STBox` instance has a spatial dimension and this - argument is not provided, the tiling will be only temporal. - duration: The duration of the temporal tiles. If the `STBox` instance has a time dimension and this - argument is not provided, the tiling will be only spatial. - origin: The origin of the spatial tiling. If not provided, the origin will be (0, 0, 0). - start: The start time of the temporal tiling. If not provided, the start time will be the starting time of - the `STBox` time dimension. + size: The size of the spatial tiles. If the `STBox` instance has a + spatial dimension and this argument is not provided, the tiling + will be only temporal. + duration: The duration of the temporal tiles. If the `STBox` + instance has a time dimension and this argument is not + provided, the tiling will be only spatial. + origin: The origin of the spatial tiling. If not provided, the + origin will be (0, 0, 0). + start: The start time of the temporal tiling. If not provided, the + start time will be the starting time of the `STBox` time + dimension. Returns: A flat list of `STBox` instances. @@ -1257,8 +1343,9 @@ def __ne__(self, other): def __lt__(self, other): """ - Returns whether ``self`` is less than `other`. Compares first the SRID, then the time dimension, - and finally the spatial dimension (X, then Y then Z lower bounds and then the upper bounds). + Returns whether ``self`` is less than `other`. Compares first the SRID, + then the time dimension, and finally the spatial dimension (X, then Y + then Z lower bounds and then the upper bounds). Args: other: The spatiotemporal object to compare with ``self``. @@ -1275,14 +1362,16 @@ def __lt__(self, other): def __le__(self, other): """ - Returns whether ``self`` is less than or equal to `other`. Compares first the SRID, then the time dimension, - and finally the spatial dimension (X, then Y then Z lower bounds and then the upper bounds). + Returns whether ``self`` is less than or equal to `other`. Compares + first the SRID, then the time dimension, and finally the spatial + dimension (X, then Y then Z lower bounds and then the upper bounds). Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is less than or equal to ``other``, ``False`` otherwise. + ``True`` if ``self`` is less than or equal to ``other``, ``False`` + otherwise. MEOS Functions: stbox_le @@ -1293,8 +1382,9 @@ def __le__(self, other): def __gt__(self, other): """ - Returns whether ``self`` is greater than `other`. Compares first the SRID, then the time dimension, - and finally the spatial dimension (X, then Y then Z lower bounds and then the upper bounds). + Returns whether ``self`` is greater than `other`. Compares first the + SRID, then the time dimension, and finally the spatial dimension (X, + then Y then Z lower bounds and then the upper bounds). Args: other: The spatiotemporal object to compare with ``self``. @@ -1311,14 +1401,16 @@ def __gt__(self, other): def __ge__(self, other): """ - Returns whether ``self`` is greater than or equal to `other`. Compares first the SRID, then the time dimension, - and finally the spatial dimension (X, then Y then Z lower bounds and then the upper bounds). + Returns whether ``self`` is greater than or equal to `other`. Compares + first the SRID, then the time dimension, and finally the spatial + dimension (X, then Y then Z lower bounds and then the upper bounds). Args: other: The spatiotemporal object to compare with ``self``. Returns: - ``True`` if ``self`` is greater than or equal to ``other``, ``False`` otherwise. + ``True`` if ``self`` is greater than or equal to ``other``, + ``False`` otherwise. MEOS Functions: stbox_ge @@ -1340,7 +1432,8 @@ def plot_xy(self, *args, **kwargs): def plot_xt(self, *args, **kwargs): """ - Plots the first spatial dimension and the temporal dimension (XT) of ``self``. + Plots the first spatial dimension and the temporal dimension (XT) of + ``self``. See Also: :func:`~pymeos.plotters.box_plotter.BoxPlotter.plot_stbox_xt` @@ -1350,7 +1443,8 @@ def plot_xt(self, *args, **kwargs): def plot_yt(self, *args, **kwargs): """ - Plots the second spatial dimension and the temporal dimension (YT) of ``self``. + Plots the second spatial dimension and the temporal dimension (YT) of + ``self``. See Also: :func:`~pymeos.plotters.box_plotter.BoxPlotter.plot_stbox_yt` @@ -1362,7 +1456,8 @@ def plot_yt(self, *args, **kwargs): @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`STBox` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`STBox` from a database cursor. Used when automatically + loading objects from the database. Users should use the class constructor instead. """ if not value: diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index 8ca209df..216141b6 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -11,9 +11,11 @@ class TBox: """ - Class for representing numeric temporal boxes. Both numeric and temporal bounds may be inclusive or not. + Class for representing numeric temporal boxes. Both numeric and temporal + bounds may be inclusive or not. - ``TBox`` objects can be created with a single argument of type string as in MobilityDB. + ``TBox`` objects can be created with a single argument of type string as in + MobilityDB. >>> TBox('TBOXINT XT([0, 10),[2020-06-01, 2020-06-05])') >>> TBox('TBOXFLOAT XT([0, 10),[2020-06-01, 2020-06-05])') @@ -28,9 +30,10 @@ class TBox: >>> TBox(xmin=0, xmax=10, tmin='2020-06-01', tmax='2020-06-0', xmax_inc=True, tmax_inc=True) >>> TBox(xmin='0', xmax='10', tmin=parse('2020-06-01'), tmax=parse('2020-06-0')) - Note that you can create a TBox with only the numerical or the temporal dimension. In these cases, it will be - equivalent to a :class:`~pymeos.time.period.Period` (if it only has temporal dimension) or to a - :class:`~spans.floatrange` (if it only has the numeric dimension). + Note that you can create a TBox with only the numerical or the temporal + dimension. In these cases, it will be equivalent to a + :class:`~pymeos.time.period.Period` (if it only has temporal dimension) or + to a :class:`~spans.floatrange` (if it only has the numeric dimension). """ __slots__ = ['_inner'] @@ -54,7 +57,8 @@ def __init__(self, string: Optional[str] = None, *, tmax_inc: bool = False, _inner=None): assert (_inner is not None) or (string is not None) != ( - (xmin is not None and xmax is not None) or (tmin is not None and tmax is not None)), \ + (xmin is not None and xmax is not None) or + (tmin is not None and tmax is not None)), \ "Either string must be not None or at least a bound pair (xmin/max or tmin/max) must be not None" if _inner is not None: self._inner = _inner @@ -67,9 +71,11 @@ def __init__(self, string: Optional[str] = None, *, if isinstance(xmin, int) and isinstance(xmax, int): span = intspan_make(xmin, xmax, xmin_inc, xmax_inc) else: - span = floatspan_make(float(xmin), float(xmax), xmin_inc, xmax_inc) + span = floatspan_make(float(xmin), float(xmax), xmin_inc, + xmax_inc) if tmin is not None and tmax is not None: - period = Period(lower=tmin, upper=tmax, lower_inc=tmin_inc, upper_inc=tmax_inc)._inner + period = Period(lower=tmin, upper=tmax, lower_inc=tmin_inc, + upper_inc=tmax_inc)._inner self._inner = tbox_make(span, period) def __copy__(self) -> TBox: @@ -122,8 +128,8 @@ def from_hexwkb(hexwkb: str) -> TBox: @staticmethod def from_value(value: Union[int, float, intrange, floatrange]) -> TBox: """ - Returns a `TBox` from a numeric value or range. The created `TBox` will only have a numerical - dimension. + Returns a `TBox` from a numeric value or range. The created `TBox` will + only have a numerical dimension. Args: value: value to be canverted into a TBox @@ -149,8 +155,8 @@ def from_value(value: Union[int, float, intrange, floatrange]) -> TBox: @staticmethod def from_time(time: Time) -> TBox: """ - Returns a `TBox` from a :class:`~pymeos.time.time.Time` object. The created `TBox` - will only have a temporal dimension. + Returns a `TBox` from a :class:`~pymeos.time.time.Time` object. The + created `TBox` will only have a temporal dimension. Args: time: value to be canverted into a TBox @@ -159,7 +165,8 @@ def from_time(time: Time) -> TBox: A new :class:`TBox` instance MEOS Functions: - timestamp_to_tbox, timestampset_to_tbox, period_to_tbox, periodset_to_tbox + timestamp_to_tbox, timestampset_to_tbox, period_to_tbox, + periodset_to_tbox """ if isinstance(time, datetime): result = timestamp_to_tbox(datetime_to_timestamptz(time)) @@ -187,23 +194,28 @@ def from_value_time(value: Union[int, float, intrange, floatrange], A new :class:`TBox` instance MEOS Functions: - int_timestamp_to_tbox, int_period_to_tbox, float_timestamp_to_tbox, float_period_to_tbox, - span_timestamp_to_tbox, span_period_to_tbox + int_timestamp_to_tbox, int_period_to_tbox, + float_timestamp_to_tbox, float_period_to_tbox, + span_timestamp_to_tbox, span_period_to_tbox """ if isinstance(value, int) and isinstance(time, datetime): - result = int_timestamp_to_tbox(value, datetime_to_timestamptz(time)) + result = int_timestamp_to_tbox(value, + datetime_to_timestamptz(time)) elif isinstance(value, int) and isinstance(time, Period): result = int_period_to_tbox(value, time._inner) elif isinstance(value, float) and isinstance(time, datetime): - result = float_timestamp_to_tbox(value, datetime_to_timestamptz(time)) + result = float_timestamp_to_tbox(value, + datetime_to_timestamptz(time)) elif isinstance(value, float) and isinstance(time, Period): result = float_period_to_tbox(value, time._inner) elif isinstance(value, intrange) and isinstance(time, datetime): - result = span_timestamp_to_tbox(intrange_to_intspan(value), datetime_to_timestamptz(time)) + result = span_timestamp_to_tbox(intrange_to_intspan(value), + datetime_to_timestamptz(time)) elif isinstance(value, intrange) and isinstance(time, Period): result = span_period_to_tbox(intrange_to_intspan(value), time._inner) elif isinstance(value, floatrange) and isinstance(time, datetime): - result = span_timestamp_to_tbox(floatrange_to_floatspan(value), datetime_to_timestamptz(time)) + result = span_timestamp_to_tbox(floatrange_to_floatspan(value), + datetime_to_timestamptz(time)) elif isinstance(value, floatrange) and isinstance(time, Period): result = span_period_to_tbox(floatrange_to_floatspan(value), time._inner) else: @@ -269,7 +281,8 @@ def as_hexwkb(self) -> str: Returns the WKB representation of ``self`` in hex-encoded ASCII. Returns: - A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. + A :class:`str` object with the WKB representation of ``self`` in + hex-encoded ASCII. MEOS Functions: tbox_as_hexwkb @@ -379,7 +392,8 @@ def tmin(self): Returns the temporal lower bound of ``self``. Returns: - The temporal lower bound of the `TBox` as a :class:`~datetime.datetime` + The temporal lower bound of the `TBox` as a + :class:`~datetime.datetime` MEOS Functions: tbox_tmin @@ -406,7 +420,8 @@ def tmax(self): Returns the temporal upper bound of ``self``. Returns: - The temporal upper bound of the `TBox` as a :class:`~datetime.datetime` + The temporal upper bound of the `TBox` as a + :class:`~datetime.datetime` MEOS Functions: tbox_tmax @@ -431,8 +446,9 @@ def tmax_inc(self) -> bool: # ------------------------- Transformation -------------------------------- def expand(self, other: Union[int, float, timedelta]) -> TBox: """ - Returns the result of expanding ``self`` with the ``other``. Depending on the type of ``other``, the expansion - will be of the numeric dimension (:class:`float`) or temporal (:class:`~datetime.timedelta`). + Returns the result of expanding ``self`` with the ``other``. Depending + on the type of ``other``, the expansion will be of the numeric + dimension (:class:`float`) or temporal (:class:`~datetime.timedelta`). Args: other: object used to expand ``self`` @@ -490,7 +506,8 @@ def tscale(self, duration: timedelta) -> TBox: def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TBox: """ - Returns a new TBox with the temporal span shifted by `shift` and duration `duration`. + Returns a new TBox with the temporal span shifted by `shift` and + duration `duration`. Examples: >>> tbox = TBox('TBoxInt XT([0, 10),[2020-06-01, 2020-06-05])') @@ -498,8 +515,10 @@ def shift_tscale(self, shift: Optional[timedelta] = None, >>> 'TBOXINT XT([0, 10),[2020-06-03 00:00:00+02, 2020-06-07 00:00:00+02])' Args: - shift: :class:`datetime.timedelta` instance to shift the start of the temporal span - duration: :class:`datetime.timedelta` instance representing the duration of the temporal span + shift: :class:`datetime.timedelta` instance to shift the start of + the temporal span + duration: :class:`datetime.timedelta` instance representing the + duration of the temporal span Returns: A new :class:`TBox` instance @@ -510,7 +529,8 @@ def shift_tscale(self, shift: Optional[timedelta] = None, See Also: :meth:`Period.shift_tscale` """ - assert shift is not None or duration is not None, 'shift and duration deltas must not be both None' + assert shift is not None or duration is not None, \ + 'shift and duration deltas must not be both None' result = tbox_shift_tscale( self._inner, timedelta_to_interval(shift) if shift else None, @@ -554,7 +574,8 @@ def union(self, other: TBox, strict: Optional[bool] = True) -> TBox: def __add__(self, other): """ - Returns the union of `self` with `other`. Fails if the union is not contiguous. + Returns the union of `self` with `other`. Fails if the union is not + contiguous. Args: other: temporal object to merge with @@ -576,7 +597,8 @@ def intersection(self, other: TBox) -> Optional[TBox]: other: temporal object to merge with Returns: - A :class:`TBox` instance if the instersection is not empty, `None` otherwise. + A :class:`TBox` instance if the instersection is not empty, `None` + otherwise. MEOS Functions: intersection_tbox_tbox @@ -592,7 +614,8 @@ def __mul__(self, other): other: temporal object to merge with Returns: - A :class:`TBox` instance if the instersection is not empty, `None` otherwise. + A :class:`TBox` instance if the instersection is not empty, `None` + otherwise. MEOS Functions: intersection_tbox_tbox @@ -600,10 +623,11 @@ def __mul__(self, other): return self.intersection(other) # ------------------------- Topological Operations ------------------------ - def is_adjacent(self, other: Union[int, float, intrange, floatrange, TBox, TNumber]) -> bool: + def is_adjacent(self, + other: Union[int, float, intrange, floatrange, TBox, TNumber]) -> bool: """ - Returns whether ``self`` is adjacent to ``other``. That is, they share only the temporal or numerical bound - and only one of them contains it. + Returns whether ``self`` is adjacent to ``other``. That is, they share + only the temporal or numerical bound and only one of them contains it. Examples: >>> TBox('TBoxInt XT([0, 1], [2012-01-01, 2012-01-02))').is_adjacent(TBox('TBoxInt XT([0, 1], [2012-01-02, 2012-01-03])')) @@ -623,14 +647,18 @@ def is_adjacent(self, other: Union[int, float, intrange, floatrange, TBox, TNumb adjacent_tbox_tbox, tnumber_to_tbox """ if isinstance(other, int): - return adjacent_span_span(self._inner_span(), float_to_floaspan(float(other))) + return adjacent_span_span(self._inner_span(), + float_to_floaspan(float(other))) elif isinstance(other, float): - return adjacent_span_span(self._inner_span(), float_to_floaspan(other)) + return adjacent_span_span(self._inner_span(), + float_to_floaspan(other)) elif isinstance(other, intrange): from pymeos_cffi.functions import _ffi - return adjacent_span_span(_ffi.addressof(self._inner, 'span'), intrange_to_intspan(other)) + return adjacent_span_span(_ffi.addressof(self._inner, 'span'), + intrange_to_intspan(other)) elif isinstance(other, floatrange): - return adjacent_span_span(self._inner.span, floatrange_to_floatspan(other)) + return adjacent_span_span(self._inner.span, + floatrange_to_floatspan(other)) elif isinstance(other, TBox): return adjacent_tbox_tbox(self._inner, other._inner) elif isinstance(other, TNumber): @@ -662,7 +690,8 @@ def is_contained_in(self, container: Union[TBox, TNumber]) -> bool: if isinstance(container, TBox): return contained_tbox_tbox(self._inner, container._inner) elif isinstance(container, TNumber): - return contained_tbox_tbox(self._inner, tnumber_to_tbox(container._inner)) + return contained_tbox_tbox(self._inner, + tnumber_to_tbox(container._inner)) else: raise TypeError(f'Operation not supported with type {container.__class__}') @@ -690,7 +719,8 @@ def contains(self, content: Union[TBox, TNumber]) -> bool: if isinstance(content, TBox): return contains_tbox_tbox(self._inner, content._inner) elif isinstance(content, TNumber): - return contains_tbox_tbox(self._inner, tnumber_to_tbox(content._inner)) + return contains_tbox_tbox(self._inner, + tnumber_to_tbox(content._inner)) else: raise TypeError(f'Operation not supported with type {content.__class__}') @@ -719,7 +749,8 @@ def __contains__(self, item): def overlaps(self, other: Union[TBox, TNumber]) -> bool: """ - Returns whether ``self`` overlaps ``other``. That is, both share at least an instant or a value. + Returns whether ``self`` overlaps ``other``. That is, both share at + least an instant or a value. Args: other: temporal object to compare with @@ -733,7 +764,8 @@ def overlaps(self, other: Union[TBox, TNumber]) -> bool: if isinstance(other, TBox): return overlaps_tbox_tbox(self._inner, other._inner) elif isinstance(other, TNumber): - return overlaps_tbox_tbox(self._inner, tnumber_to_tbox(other._inner)) + return overlaps_tbox_tbox(self._inner, + tnumber_to_tbox(other._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -781,8 +813,8 @@ def is_left(self, other: Union[TBox, TNumber]) -> bool: def is_over_or_left(self, other: Union[TBox, TNumber]) -> bool: """ - Returns whether ``self`` is to the left of ``other`` allowing overlap. That is, ``self`` does not extend to the - right of ``other``. + Returns whether ``self`` is to the left of ``other`` allowing overlap. + That is, ``self`` does not extend to the right of ``other``. Args: other: temporal object to compare with @@ -822,8 +854,8 @@ def is_right(self, other: Union[TBox, TNumber]) -> bool: def is_over_or_right(self, other: Union[TBox, TNumber]) -> bool: """ - Returns whether ``self`` is to the right of ``other`` allowing overlap. That is, ``self`` does not extend to the - left of ``other``. + Returns whether ``self`` is to the right of ``other`` allowing overlap. + That is, ``self`` does not extend to the left of ``other``. Args: other: temporal object to compare with @@ -837,7 +869,8 @@ def is_over_or_right(self, other: Union[TBox, TNumber]) -> bool: if isinstance(other, TBox): return overright_tbox_tbox(self._inner, other._inner) elif isinstance(other, TNumber): - return overright_tbox_tbox(self._inner, tnumber_to_tbox(other._inner)) + return overright_tbox_tbox(self._inner, + tnumber_to_tbox(other._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -863,8 +896,8 @@ def is_before(self, other: Union[TBox, TNumber]) -> bool: def is_over_or_before(self, other: Union[TBox, TNumber]) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` does not extend after - ``other``. + Returns whether ``self`` is before ``other`` allowing overlap. That is, + ``self`` does not extend after ``other``. Args: other: temporal object to compare with @@ -878,7 +911,8 @@ def is_over_or_before(self, other: Union[TBox, TNumber]) -> bool: if isinstance(other, TBox): return overbefore_tbox_tbox(self._inner, other._inner) elif isinstance(other, TNumber): - return overbefore_tbox_tbox(self._inner, tnumber_to_tbox(other._inner)) + return overbefore_tbox_tbox(self._inner, + tnumber_to_tbox(other._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -904,8 +938,8 @@ def is_after(self, other: Union[TBox, TNumber]) -> bool: def is_over_or_after(self, other: Union[TBox, TNumber]) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` does not extend before - ``other``. + Returns whether ``self`` is after ``other`` allowing overlap. That is, + ``self`` does not extend before``other``. Args: other: temporal object to compare with @@ -919,7 +953,8 @@ def is_over_or_after(self, other: Union[TBox, TNumber]) -> bool: if isinstance(other, TBox): return overafter_tbox_tbox(self._inner, other._inner) elif isinstance(other, TNumber): - return overafter_tbox_tbox(self._inner, tnumber_to_tbox(other._inner)) + return overafter_tbox_tbox(self._inner, + tnumber_to_tbox(other._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -932,7 +967,8 @@ def nearest_approach_distance(self, other: Union[TBox, TNumber]) -> float: other: temporal object to compare with Returns: - A :class:`float` with the distance between the nearest points of ``self`` and ``other``. + A :class:`float` with the distance between the nearest points of + ``self`` and ``other``. MEOS Functions: nad_tbox_tbox @@ -946,7 +982,8 @@ def nearest_approach_distance(self, other: Union[TBox, TNumber]) -> float: # ------------------------- Splitting -------------------------------------- def tile(self, size: float, duration: Union[timedelta, str], - origin: float = 0.0, start: Union[datetime, str, None] = None) -> List[List[TBox]]: + origin: float = 0.0, start: Union[datetime, str, None] = None) -> \ + List[List[TBox]]: """ Returns 2d matrix of TBoxes resulting of tiling ``self``. @@ -962,15 +999,18 @@ def tile(self, size: float, duration: Union[timedelta, str], MEOS Functions: tbox_tile_list """ - dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else pg_interval_in(duration, -1) + dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) \ + else pg_interval_in(duration, -1) st = datetime_to_timestamptz(start) if isinstance(start, datetime) \ else pg_timestamptz_in(start, -1) if isinstance(start, str) \ else tbox_tmin(self._inner) tiles, rows, columns = tbox_tile_list(self._inner, size, dt, origin, st) - return [[TBox(_inner=tiles + (c * rows + r)) for c in range(columns)] for r in range(rows)] + return [[TBox(_inner=tiles + (c * rows + r)) for c in range(columns)] + for r in range(rows)] def tile_flat(self, size: float, duration: Union[timedelta, str], - origin: float = 0.0, start: Union[datetime, str, None] = None) -> List[TBox]: + origin: float = 0.0, + start: Union[datetime, str, None] = None) -> List[TBox]: """ Returns an array of TBoxes resulting of tiling ``self``. @@ -1026,7 +1066,8 @@ def __ne__(self, other): def __lt__(self, other): """ - Returns whether ``self`` is less than ``other``. The time dimension is compared first, then the space dimension. + Returns whether ``self`` is less than ``other``. The time + dimension is compared first, then the space dimension. Args: other: temporal object to compare with @@ -1043,8 +1084,8 @@ def __lt__(self, other): def __le__(self, other): """ - Returns whether ``self`` is less than or equal to ``other``. The time dimension is compared first, then the - space dimension. + Returns whether ``self`` is less than or equal to ``other``. The time + dimension is compared first, then the space dimension. Args: other: temporal object to compare with @@ -1061,8 +1102,8 @@ def __le__(self, other): def __gt__(self, other): """ - Returns whether ``self`` is greater than ``other``. The time dimension is compared first, then the space - dimension. + Returns whether ``self`` is greater than ``other``. The time dimension + is compared first, then the space dimension. Args: other: temporal object to compare with @@ -1079,8 +1120,8 @@ def __gt__(self, other): def __ge__(self, other): """ - Returns whether ``self`` is greater than or equal to ``other``. The time dimension is compared first, then the - space dimension. + Returns whether ``self`` is greater than or equal to ``other``. The + time dimension is compared first, then the space dimension. Args: other: temporal object to compare with @@ -1110,7 +1151,8 @@ def plot(self, *args, **kwargs): @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`TBox` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`TBox` from a database cursor. Used when automatically + loading objects from the database. Users should use the class constructor instead. """ if not value: diff --git a/pymeos/pymeos/main/tbool.py b/pymeos/pymeos/main/tbool.py index f4b063dd..721d6e22 100644 --- a/pymeos/pymeos/main/tbool.py +++ b/pymeos/pymeos/main/tbool.py @@ -24,7 +24,8 @@ def __init__(self, _inner) -> None: @staticmethod def from_base_temporal(value: bool, base: Temporal) -> TBool: """ - Create a temporal Boolean from a Boolean value and the time frame of another temporal object. + Create a temporal Boolean from a Boolean value and the time frame of + another temporal object. Args: value: Boolean value. @@ -46,7 +47,8 @@ def from_base_time(value: bool, base: datetime) -> TBoolInst: @staticmethod @overload - def from_base_time(value: bool, base: Union[TimestampSet, Period]) -> TBoolSeq: + def from_base_time(value: bool, base: Union[TimestampSet, Period]) -> \ + TBoolSeq: ... @staticmethod @@ -71,13 +73,17 @@ def from_base_time(value: bool, base: Time) -> TBool: tboolseq_from_base_period, tboolseqset_from_base_periodset """ if isinstance(base, datetime): - return TBoolInst(_inner=tboolinst_make(value, datetime_to_timestamptz(base))) + return TBoolInst(_inner=tboolinst_make(value, + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): - return TBoolSeq(_inner=tboolseq_from_base_timestampset(value, base._inner)) + return TBoolSeq(_inner=tboolseq_from_base_timestampset(value, + base._inner)) elif isinstance(base, Period): - return TBoolSeq(_inner=tboolseq_from_base_period(value, base._inner)) + return TBoolSeq(_inner=tboolseq_from_base_period(value, + base._inner)) elif isinstance(base, PeriodSet): - return TBoolSeqSet(_inner=tboolseqset_from_base_periodset(value, base._inner)) + return TBoolSeqSet(_inner=tboolseqset_from_base_periodset(value, + base._inner)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Output ---------------------------------------- @@ -141,7 +147,8 @@ def value_at_timestamp(self, timestamp) -> bool: MEOS Function: tbool_value_at_timestamp """ - return tbool_value_at_timestamp(self._inner, datetime_to_timestamptz(timestamp), True) + return tbool_value_at_timestamp(self._inner, + datetime_to_timestamptz(timestamp), True) # ------------------------- Ever and Always Comparisons ------------------- def always_eq(self, value: bool) -> bool: @@ -231,7 +238,8 @@ def temporal_not_equal(self, other: Union[bool, Temporal]) -> TBool: # ------------------------- Restrictions ---------------------------------- def at(self, other: Union[bool, Time]) -> TBool: """ - Returns a new temporal boolean with the values of `self` restricted to the time or value `other`. + Returns a new temporal boolean with the values of `self` restricted to + the time or value `other`. Args: other: Time or value to restrict to. @@ -240,7 +248,8 @@ def at(self, other: Union[bool, Time]) -> TBool: A new temporal boolean. MEOS Functions: - tbool_at_value, temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, temporal_at_periodset + tbool_at_value, temporal_at_timestamp, temporal_at_timestampset, + temporal_at_period, temporal_at_periodset """ if isinstance(other, bool): result = tbool_at_value(self._inner, other) @@ -250,7 +259,8 @@ def at(self, other: Union[bool, Time]) -> TBool: def minus(self, other: Union[bool, Time]) -> TBool: """ - Returns a new temporal boolean with the values of `self` restricted to the complement of the time or value + Returns a new temporal boolean with the values of `self` restricted to + the complement of the time or value `other`. Args: @@ -260,7 +270,8 @@ def minus(self, other: Union[bool, Time]) -> TBool: A new temporal boolean. MEOS Functions: - tbool_minus_value, temporal_minus_timestamp, temporal_minus_timestampset, temporal_minus_period, + tbool_minus_value, temporal_minus_timestamp, + temporal_minus_timestampset, temporal_minus_period, temporal_minus_periodset """ if isinstance(other, bool): @@ -278,7 +289,8 @@ def temporal_and(self, other: Union[bool, TBool]) -> TBool: other: A temporal or boolean object to combine with `self`. Returns: - A :class:`TBool` with the temporal conjunction of `self` and `other`. + A :class:`TBool` with the temporal conjunction of `self` and + `other`. MEOS Functions: tand_tbool_bool, tand_tbool_tbool @@ -286,7 +298,8 @@ def temporal_and(self, other: Union[bool, TBool]) -> TBool: if isinstance(other, bool): return self.__class__(_inner=tand_tbool_bool(self._inner, other)) elif isinstance(other, TBool): - return self.__class__(_inner=tand_tbool_tbool(self._inner, other._inner)) + return self.__class__(_inner=tand_tbool_tbool(self._inner, + other._inner)) raise TypeError(f'Operation not supported with type {other.__class__}') def __and__(self, other): @@ -320,7 +333,8 @@ def temporal_or(self, other: Union[bool, TBool]) -> TBool: if isinstance(other, bool): return self.__class__(_inner=tor_tbool_bool(self._inner, other)) elif isinstance(other, TBool): - return self.__class__(_inner=tor_tbool_tbool(self._inner, other._inner)) + return self.__class__(_inner=tor_tbool_tbool(self._inner, + other._inner)) raise TypeError(f'Operation not supported with type {other.__class__}') def __or__(self, other): @@ -404,7 +418,8 @@ def when_false(self) -> Optional[PeriodSet]: @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`TBool` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`TBool` from a database cursor. Used when automatically + loading objects from the database. Users should use the class constructor instead. """ if not value: @@ -429,30 +444,41 @@ class TBoolInst(TInstant[bool, 'TBool', 'TBoolInst', 'TBoolSeq', 'TBoolSeqSet'], _make_function = tboolinst_make _cast_function = bool - def __init__(self, string: Optional[str] = None, *, value: Optional[Union[str, bool]] = None, + def __init__(self, string: Optional[str] = None, *, + value: Optional[Union[str, bool]] = None, timestamp: Optional[Union[str, datetime]] = None, _inner=None): - super().__init__(string=string, value=value, timestamp=timestamp, _inner=_inner) + super().__init__(string=string, value=value, timestamp=timestamp, + _inner=_inner) -class TBoolSeq(TSequence[bool, 'TBool', 'TBoolInst', 'TBoolSeq', 'TBoolSeqSet'], TBool): +class TBoolSeq(TSequence[bool, 'TBool', 'TBoolInst', 'TBoolSeq', + 'TBoolSeqSet'], TBool): """ Class for representing temporal boolean values over a period of time. """ ComponentClass = TBoolInst - def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TBoolInst]]] = None, - lower_inc: bool = True, upper_inc: bool = False, expandable: Union[bool, int] = False, - interpolation: TInterpolation = TInterpolation.STEPWISE, normalize: bool = True, _inner=None): - super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, normalize=normalize, _inner=_inner) + def __init__(self, string: Optional[str] = None, *, + instant_list: Optional[List[Union[str, TBoolInst]]] = None, + lower_inc: bool = True, upper_inc: bool = False, + expandable: Union[bool, int] = False, + interpolation: TInterpolation = TInterpolation.STEPWISE, + normalize: bool = True, _inner=None): + super().__init__(string=string, instant_list=instant_list, + lower_inc=lower_inc, upper_inc=upper_inc, + expandable=expandable, interpolation=interpolation, + normalize=normalize, _inner=_inner) -class TBoolSeqSet(TSequenceSet[bool, 'TBool', 'TBoolInst', 'TBoolSeq', 'TBoolSeqSet'], TBool): +class TBoolSeqSet(TSequenceSet[bool, 'TBool', 'TBoolInst', 'TBoolSeq', + 'TBoolSeqSet'], TBool): """ Class for representing temporal boolean values over a period of time with gaps. """ ComponentClass = TBoolSeq - def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, TBoolSeq]]] = None, + def __init__(self, string: Optional[str] = None, *, + sequence_list: Optional[List[Union[str, TBoolSeq]]] = None, normalize: bool = True, _inner=None): - super().__init__(string=string, sequence_list=sequence_list, normalize=normalize, _inner=_inner) + super().__init__(string=string, sequence_list=sequence_list, + normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index 83ac82d4..4b2b37a3 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -22,9 +22,11 @@ class TFloat(TNumber[float, 'TFloat', 'TFloatInst', 'TFloatSeq', 'TFloatSeqSet'] # ------------------------- Constructors ---------------------------------- @staticmethod - def from_base_temporal(value: float, base: Temporal, interpolation: TInterpolation = TInterpolation.LINEAR) -> TFloat: + def from_base_temporal(value: float, base: Temporal, + interpolation: TInterpolation = TInterpolation.LINEAR) -> TFloat: """ - Returns a new temporal float with the value `value` and the temporal frame of `base`. + Returns a new temporal float with the value `value` and the temporal + frame of `base`. Args: value: Value of the temporal float. @@ -47,7 +49,8 @@ def from_base_time(value: float, base: datetime) -> TFloatInst: @staticmethod @overload - def from_base_time(value: float, base: Union[TimestampSet, Period]) -> TFloatSeq: + def from_base_time(value: float, base: Union[TimestampSet, Period]) -> \ + TFloatSeq: ... @staticmethod @@ -56,9 +59,11 @@ def from_base_time(value: float, base: PeriodSet) -> TFloatSeqSet: ... @staticmethod - def from_base_time(value: float, base: Time, interpolation: TInterpolation = None) -> TFloat: + def from_base_time(value: float, base: Time, + interpolation: TInterpolation = None) -> TFloat: """ - Returns a new temporal float with the value `value` and the temporal frame of `base`. + Returns a new temporal float with the value `value` and the temporal + frame of `base`. Args: value: Value of the temporal float. @@ -69,16 +74,21 @@ def from_base_time(value: float, base: Time, interpolation: TInterpolation = Non A new temporal float. MEOS Functions: - tfloatinst_make, tfloatseq_from_base_timestampset, tfloatseq_from_base_time, tfloatseqset_from_base_time + tfloatinst_make, tfloatseq_from_base_timestampset, + tfloatseq_from_base_time, tfloatseqset_from_base_time """ if isinstance(base, datetime): - return TFloatInst(_inner=tfloatinst_make(value, datetime_to_timestamptz(base))) + return TFloatInst(_inner=tfloatinst_make(value, + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): - return TFloatSeq(_inner=tfloatseq_from_base_timestampset(value, base._inner)) + return TFloatSeq(_inner=tfloatseq_from_base_timestampset(value, + base._inner)) elif isinstance(base, Period): - return TFloatSeq(_inner=tfloatseq_from_base_period(value, base._inner, interpolation)) + return TFloatSeq(_inner=tfloatseq_from_base_period(value, + base._inner, interpolation)) elif isinstance(base, PeriodSet): - return TFloatSeqSet(_inner=tfloatseqset_from_base_periodset(value, base._inner, interpolation)) + return TFloatSeqSet(_inner=tfloatseqset_from_base_periodset(value, + base._inner, interpolation)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Output ---------------------------------------- @@ -113,7 +123,8 @@ def as_wkt(self, max_decimals: int = 15) -> str: def to_tint(self) -> TInt: """ Returns a new temporal integer with the values of `self` floored. - This operation can only be performed when the interpolation is stepwise or discrete. + This operation can only be performed when the interpolation is stepwise + or discrete. Returns: A new temporal integer. @@ -126,7 +137,8 @@ def to_tint(self) -> TInt: """ from ..factory import _TemporalFactory if self.interpolation() == TInterpolation.LINEAR: - raise ValueError("Cannot convert a temporal float with linear interpolation to a temporal integer") + raise ValueError("Cannot convert a temporal float with linear " \ + "interpolation to a temporal integer") return _TemporalFactory.create_temporal(tfloat_to_tint(self._inner)) def to_floatrange(self) -> floatrange: @@ -196,7 +208,8 @@ def end_value(self) -> float: def value_set(self) -> Set[float]: """ Returns the set of values of `self`. - Note that when the interpolation is linear, the set will contain only the waypoints. + Note that when the interpolation is linear, the set will contain only + the waypoints. Returns: A :class:`set` with the values of `self`. @@ -240,7 +253,8 @@ def always_equal(self, value: float) -> bool: value: :class:`float` to compare. Returns: - `True` if the values of `self` are always equal to `value`, `False` otherwise. + `True` if the values of `self` are always equal to `value`, + `False` otherwise. MEOS Functions: tfloat_always_eq @@ -255,7 +269,8 @@ def always_not_equal(self, value: float) -> bool: value: :class:`float` to compare. Returns: - `True` if the values of `self` are always not equal to `value`, `False` otherwise. + `True` if the values of `self` are always not equal to `value`, + `False` otherwise. MEOS Functions: tfloat_ever_eq @@ -270,7 +285,8 @@ def always_less(self, value: float) -> bool: value: :class:`float` to compare. Returns: - `True` if the values of `self` are always less than `value`, `False` otherwise. + `True` if the values of `self` are always less than `value`, + `False` otherwise. MEOS Functions: tfloat_always_lt @@ -279,13 +295,15 @@ def always_less(self, value: float) -> bool: def always_less_or_equal(self, value: float) -> bool: """ - Returns whether the values of `self` are always less than or equal to `value`. + Returns whether the values of `self` are always less than or equal to + `value`. Args: value: :class:`float` to compare. Returns: - `True` if the values of `self` are always less than or equal to `value`, `False` otherwise. + `True` if the values of `self` are always less than or equal to + `value`, `False` otherwise. MEOS Functions: tfloat_always_le @@ -294,13 +312,15 @@ def always_less_or_equal(self, value: float) -> bool: def always_greater_or_equal(self, value: float) -> bool: """ - Returns whether the values of `self` are always greater than or equal to `value`. + Returns whether the values of `self` are always greater than or equal + to `value`. Args: value: :class:`float` to compare. Returns: - `True` if the values of `self` are always greater than or equal to `value`, `False` otherwise. + `True` if the values of `self` are always greater than or equal to + `value`, `False` otherwise. MEOS Functions: tfloat_ever_lt @@ -315,7 +335,8 @@ def always_greater(self, value: float) -> bool: value: :class:`float` to compare. Returns: - `True` if the values of `self` are always greater than `value`, `False` otherwise. + `True` if the values of `self` are always greater than `value`, + `False` otherwise. MEOS Functions: tfloat_ever_le @@ -330,7 +351,8 @@ def ever_less(self, value: float) -> bool: value: :class:`float` to compare. Returns: - `True` if the values of `self` are ever less than `value`, `False` otherwise. + `True` if the values of `self` are ever less than `value`, + `False` otherwise. MEOS Functions: tfloat_ever_lt @@ -339,13 +361,15 @@ def ever_less(self, value: float) -> bool: def ever_less_or_equal(self, value: float) -> bool: """ - Returns whether the values of `self` are ever less than or equal to `value`. + Returns whether the values of `self` are ever less than or equal to + `value`. Args: value: :class:`float` to compare. Returns: - `True` if the values of `self` are ever less than or equal to `value`, `False` otherwise. + `True` if the values of `self` are ever less than or equal to + `value`, `False` otherwise. MEOS Functions: tfloat_ever_le @@ -360,7 +384,8 @@ def ever_equal(self, value: float) -> bool: value: :class:`float` to compare. Returns: - `True` if the values of `self` are ever equal to `value`, `False` otherwise. + `True` if the values of `self` are ever equal to `value`, `False` + otherwise. MEOS Functions: tfloat_ever_eq @@ -375,7 +400,8 @@ def ever_not_equal(self, value: float) -> bool: value: :class:`float` to compare. Returns: - `True` if the values of `self` are ever not equal to `value`, `False` otherwise. + `True` if the values of `self` are ever not equal to `value`, + `False` otherwise. MEOS Functions: tfloat_always_eq @@ -384,13 +410,15 @@ def ever_not_equal(self, value: float) -> bool: def ever_greater_or_equal(self, value: float) -> bool: """ - Returns whether the values of `self` are ever greater than or equal to `value`. + Returns whether the values of `self` are ever greater than or equal to + `value`. Args: value: :class:`float` to compare. Returns: - `True` if the values of `self` are ever greater than or equal to `value`, `False` otherwise. + `True` if the values of `self` are ever greater than or equal to + `value`, `False` otherwise. MEOS Functions: tfloat_always_lt @@ -405,7 +433,8 @@ def ever_greater(self, value: float) -> bool: value: :class:`float` to compare. Returns: - `True` if the values of `self` are ever greater than `value`, `False` otherwise. + `True` if the values of `self` are ever greater than `value`, + `False` otherwise. MEOS Functions: tfloat_always_le @@ -420,7 +449,8 @@ def never_equal(self, value: float) -> bool: value: :class:`float` value to compare. Returns: - `True` if the values of `self` are never equal to `value`, `False` otherwise. + `True` if the values of `self` are never equal to `value`, + `False` otherwise. MEOS Functions: tfloat_ever_eq @@ -435,7 +465,8 @@ def never_not_equal(self, value: float) -> bool: value: :class:`float` value to compare. Returns: - `True` if the values of `self` are never not equal to `value`, `False` otherwise. + `True` if the values of `self` are never not equal to `value`, + `False` otherwise. MEOS Functions: tfloat_always_eq @@ -450,7 +481,8 @@ def never_less(self, value: float) -> bool: value: :class:`float` value to compare. Returns: - `True` if the values of `self` are never less than `value`, `False` otherwise. + `True` if the values of `self` are never less than `value`, + `False` otherwise. MEOS Functions: tfloat_ever_lt @@ -459,13 +491,15 @@ def never_less(self, value: float) -> bool: def never_less_or_equal(self, value: float) -> bool: """ - Returns whether the values of `self` are never less than or equal to `value`. + Returns whether the values of `self` are never less than or equal to + `value`. Args: value: :class:`float` value to compare. Returns: - `True` if the values of `self` are never less than or equal to `value`, `False` otherwise. + `True` if the values of `self` are never less than or equal to + `value`, `False` otherwise. MEOS Functions: tfloat_ever_le @@ -474,13 +508,15 @@ def never_less_or_equal(self, value: float) -> bool: def never_greater_or_equal(self, value: float) -> bool: """ - Returns whether the values of `self` are never greater than or equal to `value`. + Returns whether the values of `self` are never greater than or equal to + `value`. Args: value: :class:`float` value to compare. Returns: - `True` if the values of `self` are never greater than or equal to `value`, `False` otherwise. + `True` if the values of `self` are never greater than or equal to + `value`, `False` otherwise. MEOS Functions: tfloat_always_lt @@ -495,7 +531,8 @@ def never_greater(self, value: float) -> bool: value: :class:`float` value to compare. Returns: - `True` if the values of `self` are never greater than `value`, `False` otherwise. + `True` if the values of `self` are never greater than `value`, + `False` otherwise. MEOS Functions: tfloat_always_le @@ -508,7 +545,8 @@ def temporal_equal(self, other: Union[int, float, Temporal]) -> Temporal: Returns the temporal equality relation between `self` and `other`. Args: - other: An :class:`int`, :class:`float` or temporal object to compare to `self`. + other: An :class:`int`, :class:`float` or temporal object to + compare to `self`. Returns: A :class:`TBool` with the result of the temporal equality relation. @@ -527,7 +565,8 @@ def temporal_not_equal(self, other: Union[int, float, Temporal]) -> Temporal: Returns the temporal not equal relation between `self` and `other`. Args: - other: An :class:`int`, :class:`float` or temporal object to compare to `self`. + other: An :class:`int`, :class:`float` or temporal object to + compare to `self`. Returns: A :class:`TBool` with the result of the temporal not equal relation. @@ -546,7 +585,8 @@ def temporal_less(self, other: Union[int, float, Temporal]) -> Temporal: Returns the temporal less than relation between `self` and `other`. Args: - other: An :class:`int`, :class:`float` or temporal object to compare to `self`. + other: An :class:`int`, :class:`float` or temporal object to + compare to `self`. Returns: A :class:`TBool` with the result of the temporal less than relation. @@ -560,15 +600,18 @@ def temporal_less(self, other: Union[int, float, Temporal]) -> Temporal: return super().temporal_less(other) return Temporal._factory(result) - def temporal_less_or_equal(self, other: Union[int, float, Temporal]) -> Temporal: + def temporal_less_or_equal(self, other: Union[int, float, Temporal]) -> \ + Temporal: """ Returns the temporal less or equal relation between `self` and `other`. Args: - other: An :class:`int`, :class:`float` or temporal object to compare to `self`. + other: An :class:`int`, :class:`float` or temporal object to + compare to `self`. Returns: - A :class:`TBool` with the result of the temporal less or equal relation. + A :class:`TBool` with the result of the temporal less or equal + relation. MEOS Functions: tle_tfloat_float, tle_temporal_temporal @@ -579,15 +622,18 @@ def temporal_less_or_equal(self, other: Union[int, float, Temporal]) -> Temporal return super().temporal_less_or_equal(other) return Temporal._factory(result) - def temporal_greater_or_equal(self, other: Union[int, float, Temporal]) -> Temporal: + def temporal_greater_or_equal(self, other: Union[int, float, Temporal]) \ + -> Temporal: """ Returns the temporal greater or equal relation between `self` and `other`. Args: - other: An :class:`int`, :class:`float` or temporal object to compare to `self`. + other: An :class:`int`, :class:`float` or temporal object to + compare to `self`. Returns: - A :class:`TBool` with the result of the temporal greater or equal relation. + A :class:`TBool` with the result of the temporal greater or equal + relation. MEOS Functions: tge_tfloat_float, tge_temporal_temporal @@ -603,10 +649,12 @@ def temporal_greater(self, other: Union[int, float, Temporal]) -> Temporal: Returns the temporal greater than relation between `self` and `other`. Args: - other: An :class:`int`, :class:`float` or temporal object to compare to `self`. + other: An :class:`int`, :class:`float` or temporal object to + compare to `self`. Returns: - A :class:`TBool` with the result of the temporal greater than relation. + A :class:`TBool` with the result of the temporal greater than + relation. MEOS Functions: tgt_tfloat_float, tgt_temporal_temporal @@ -621,7 +669,8 @@ def temporal_greater(self, other: Union[int, float, Temporal]) -> Temporal: def at(self, other: Union[int, float, List[int], List[float], floatrange, List[floatrange], TBox, Time]) -> TFloat: """ - Returns a new temporal float with the values of `self` restricted to the value or time `other`. + Returns a new temporal float with the values of `self` restricted to + the value or time `other`. Args: other: Value or time to restrict to. @@ -638,7 +687,8 @@ def at(self, other: Union[int, float, List[int], List[float], result = tfloat_at_value(self._inner, float(other)) elif isinstance(other, floatrange): result = tnumber_at_span(self._inner, floatrange_to_floatspan(other)) - elif isinstance(other, list) and (isinstance(other[0], int) or isinstance(other[0], float)): + elif isinstance(other, list) and (isinstance(other[0], int) or \ + isinstance(other[0], float)): result = temporal_at_values(self._inner, floatset_make(other)) # elif isinstance(other, list) and (isinstance(other[0], floatrange) or isinstance(other[0], intrange)): # results = [tnumber_at_span(self._inner, value) for value in other if other is not None] @@ -650,8 +700,8 @@ def at(self, other: Union[int, float, List[int], List[float], def minus(self, other: Union[int, float, List[int], List[float], floatrange, List[floatrange], TBox, Time]) -> Temporal: """ - Returns a new temporal float with the values of `self` restricted to the complement of the time or value - `other`. + Returns a new temporal float with the values of `self` restricted to + the complement of the time or value `other`. Args: other: Time or value to restrict to the complement of. @@ -667,9 +717,11 @@ def minus(self, other: Union[int, float, List[int], List[float], if isinstance(other, int) or isinstance(other, float): result = tfloat_minus_value(self._inner, float(other)) elif isinstance(other, floatrange): - result = tnumber_minus_span(self._inner, floatrange_to_floatspan(other)) + result = tnumber_minus_span(self._inner, + floatrange_to_floatspan(other)) elif isinstance(other, list) and isinstance(other[0], float): - result = temporal_minus_values(self._inner, floatset_make(other)) + result = temporal_minus_values(self._inner, + floatset_make(other)) else: return super().minus(other) return Temporal._factory(result) @@ -687,7 +739,8 @@ def value_at_timestamp(self, timestamp) -> float: MEOS Functions: tfloat_value_at_timestamp """ - return tfloat_value_at_timestamp(self._inner, datetime_to_timestamptz(timestamp), True) + return tfloat_value_at_timestamp(self._inner, + datetime_to_timestamptz(timestamp), True) def derivative(self) -> TFloat: """ @@ -708,7 +761,8 @@ def to_degrees(self, normalize: bool = True) -> TFloat: Returns a copy of `self` converted from radians to degrees. Args: - normalize: If True, the result will be normalized to the range [0, 360). + normalize: If True, the result will be normalized to the range + [0, 360). Returns: A new :class:`TFloat` instance. @@ -717,7 +771,8 @@ def to_degrees(self, normalize: bool = True) -> TFloat: tfloat_degrees """ from ..factory import _TemporalFactory - return _TemporalFactory.create_temporal(tfloat_degrees(self._inner, normalize)) + return _TemporalFactory.create_temporal(tfloat_degrees(self._inner, + normalize)) def to_radians(self) -> TFloat: """ @@ -746,10 +801,12 @@ def round(self, maxdd : int = 0) -> TFloat: tfloat_round """ from ..factory import _TemporalFactory - return _TemporalFactory.create_temporal(tfloat_round(self._inner, maxdd)) + return _TemporalFactory.create_temporal(tfloat_round(self._inner, + maxdd)) # ------------------------- Split Operations ------------------------------ - def value_split(self, size: float, start: Optional[float] = 0) -> List[Temporal]: + def value_split(self, size: float, start: Optional[float] = 0) -> \ + List[Temporal]: """ Splits `self` into fragments with respect to value buckets @@ -765,9 +822,11 @@ def value_split(self, size: float, start: Optional[float] = 0) -> List[Temporal] """ tiles, new_count = tfloat_value_split(self._inner, size, start) from ..factory import _TemporalFactory - return [_TemporalFactory.create_temporal(tiles[i]) for i in range(new_count)] + return [_TemporalFactory.create_temporal(tiles[i]) for i in \ + range(new_count)] - def time_value_split(self, value_start: float, value_size: float, time_start: Union[str, datetime], + def time_value_split(self, value_start: float, value_size: float, + time_start: Union[str, datetime], duration: Union[str, timedelta]) -> List[Temporal]: """ Splits `self` into fragments with respect to value and period buckets. @@ -784,17 +843,22 @@ def time_value_split(self, value_start: float, value_size: float, time_start: Un MEOS Functions: tfloat_value_time_split """ - st = datetime_to_timestamptz(time_start) if isinstance(time_start, datetime) \ + st = datetime_to_timestamptz(time_start) \ + if isinstance(time_start, datetime) \ else pg_timestamptz_in(time_start, -1) - dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else pg_interval_in(duration, -1) - tiles, new_count = tfloat_value_time_split(self._inner, value_size, value_start, dt, st) + dt = timedelta_to_interval(duration) \ + if isinstance(duration, timedelta) \ + else pg_interval_in(duration, -1) + tiles, new_count = tfloat_value_time_split(self._inner, value_size, + value_start, dt, st) return [Temporal._factory(tiles[i]) for i in range(new_count)] # ------------------------- Database Operations --------------------------- @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`TFloat` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`TFloat` from a database cursor. Used when automatically + loading objects from the database. Users should use the class constructor instead. """ if not value: @@ -817,37 +881,49 @@ def read_from_cursor(value, _=None): raise Exception("ERROR: Could not parse temporal float value") -class TFloatInst(TInstant[float, 'TFloat', 'TFloatInst', 'TFloatSeq', 'TFloatSeqSet'], TFloat): +class TFloatInst(TInstant[float, 'TFloat', 'TFloatInst', 'TFloatSeq', + 'TFloatSeqSet'], TFloat): """ Class for representing temporal floats at a single instant. """ _make_function = tfloatinst_make _cast_function = float - def __init__(self, string: Optional[str] = None, *, value: Optional[Union[str, float]] = None, + def __init__(self, string: Optional[str] = None, *, + value: Optional[Union[str, float]] = None, timestamp: Optional[Union[str, datetime]] = None, _inner=None): - super().__init__(string=string, value=value, timestamp=timestamp, _inner=_inner) + super().__init__(string=string, value=value, timestamp=timestamp, + _inner=_inner) -class TFloatSeq(TSequence[float, 'TFloat', 'TFloatInst', 'TFloatSeq', 'TFloatSeqSet'], TFloat): +class TFloatSeq(TSequence[float, 'TFloat', 'TFloatInst', 'TFloatSeq', + 'TFloatSeqSet'], TFloat): """ Class for representing temporal floats over a period of time. """ ComponentClass = TFloatInst - def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TFloatInst]]] = None, - lower_inc: bool = True, upper_inc: bool = False, expandable: Union[bool, float] = False, - interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True, _inner=None): - super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, normalize=normalize, _inner=_inner) + def __init__(self, string: Optional[str] = None, *, + instant_list: Optional[List[Union[str, TFloatInst]]] = None, + lower_inc: bool = True, upper_inc: bool = False, + expandable: Union[bool, float] = False, + interpolation: TInterpolation = TInterpolation.LINEAR, + normalize: bool = True, _inner=None): + super().__init__(string=string, instant_list=instant_list, + lower_inc=lower_inc, upper_inc=upper_inc, + expandable=expandable, interpolation=interpolation, + normalize=normalize, _inner=_inner) -class TFloatSeqSet(TSequenceSet[float, 'TFloat', 'TFloatInst', 'TFloatSeq', 'TFloatSeqSet'], TFloat): +class TFloatSeqSet(TSequenceSet[float, 'TFloat', 'TFloatInst', 'TFloatSeq', + 'TFloatSeqSet'], TFloat): """ Class for representing temporal floats over a period of time with gaps. """ ComponentClass = TFloatSeq - def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, TFloatSeq]]] = None, + def __init__(self, string: Optional[str] = None, *, + sequence_list: Optional[List[Union[str, TFloatSeq]]] = None, normalize: bool = True, _inner=None): - super().__init__(string=string, sequence_list=sequence_list, normalize=normalize, _inner=_inner) + super().__init__(string=string, sequence_list=sequence_list, + normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index 355365c2..486221c5 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -24,7 +24,8 @@ class TInt(TNumber[int, 'TInt', 'TIntInst', 'TIntSeq', 'TIntSeqSet'], ABC): @staticmethod def from_base_temporal(value: int, base: Temporal) -> TInt: """ - Returns a new temporal integer with the value `value` and the temporal frame of `base`. + Returns a new temporal integer with the value `value` and the temporal + frame of `base`. Args: value: Value of the temporal integer. @@ -57,7 +58,8 @@ def from_base_time(value: int, base: PeriodSet) -> TIntSeqSet: @staticmethod def from_base_time(value: int, base: Time) -> TInt: """ - Returns a new temporal int with the value `value` and the temporal frame of `base`. + Returns a new temporal int with the value `value` and the temporal + frame of `base`. Args: value: Value of the temporal int. @@ -71,13 +73,16 @@ def from_base_time(value: int, base: Time) -> TInt: tintseq_from_base_period, tintseqset_from_base_periodset """ if isinstance(base, datetime): - return TIntInst(_inner=tintinst_make(value, datetime_to_timestamptz(base))) + return TIntInst(_inner=tintinst_make(value, + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): - return TIntSeq(_inner=tintseq_from_base_timestampset(value, base._inner)) + return TIntSeq(_inner=tintseq_from_base_timestampset(value, + base._inner)) elif isinstance(base, Period): return TIntSeq(_inner=tintseq_from_base_period(value, base._inner)) elif isinstance(base, PeriodSet): - return TIntSeqSet(_inner=tintseqset_from_base_periodset(value, base._inner)) + return TIntSeqSet(_inner=tintseqset_from_base_periodset(value, + base._inner)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Output ---------------------------------------- @@ -233,7 +238,8 @@ def value_at_timestamp(self, timestamp) -> int: MEOS Functions: tint_value_at_timestamp """ - return tint_value_at_timestamp(self._inner, datetime_to_timestamptz(timestamp), True) + return tint_value_at_timestamp(self._inner, + datetime_to_timestamptz(timestamp), True) # ------------------------- Ever and Always Comparisons ------------------- def always_less(self, value: int) -> bool: @@ -244,7 +250,8 @@ def always_less(self, value: int) -> bool: value: :class:`int` to compare. Returns: - `True` if the values of `self` are always less than `value`, `False` otherwise. + `True` if the values of `self` are always less than `value`, + `False` otherwise. MEOS Functions: tint_always_lt @@ -253,13 +260,15 @@ def always_less(self, value: int) -> bool: def always_less_or_equal(self, value: int) -> bool: """ - Returns whether the values of `self` are always less than or equal to `value`. + Returns whether the values of `self` are always less than or equal to + `value`. Args: value: :class:`int` to compare. Returns: - `True` if the values of `self` are always less than or equal to `value`, `False` otherwise. + `True` if the values of `self` are always less than or equal to + `value`, `False` otherwise. MEOS Functions: tint_always_le @@ -274,7 +283,8 @@ def always_equal(self, value: int) -> bool: value: :class:`int` to compare. Returns: - `True` if the values of `self` are always equal to `value`, `False` otherwise. + `True` if the values of `self` are always equal to `value`, + `False` otherwise. MEOS Functions: tint_always_eq @@ -289,7 +299,8 @@ def always_not_equal(self, value: int) -> bool: value: :class:`int` to compare. Returns: - `True` if the values of `self` are always not equal to `value`, `False` otherwise. + `True` if the values of `self` are always not equal to `value`, + `False` otherwise. MEOS Functions: tint_ever_eq @@ -298,13 +309,15 @@ def always_not_equal(self, value: int) -> bool: def always_greater_or_equal(self, value: int) -> bool: """ - Returns whether the values of `self` are always greater than or equal to `value`. + Returns whether the values of `self` are always greater than or equal + to `value`. Args: value: :class:`int` to compare. Returns: - `True` if the values of `self` are always greater than or equal to `value`, `False` otherwise. + `True` if the values of `self` are always greater than or equal to + `value`, `False` otherwise. MEOS Functions: tint_ever_lt @@ -319,7 +332,8 @@ def always_greater(self, value: int) -> bool: value: :class:`int` to compare. Returns: - `True` if the values of `self` are always greater than `value`, `False` otherwise. + `True` if the values of `self` are always greater than `value`, + `False` otherwise. MEOS Functions: tint_ever_le @@ -334,7 +348,8 @@ def ever_less(self, value: int) -> bool: value: :class:`int` to compare. Returns: - `True` if the values of `self` are ever less than `value`, `False` otherwise. + `True` if the values of `self` are ever less than `value`, + `False` otherwise. MEOS Functions: tint_ever_lt @@ -343,13 +358,15 @@ def ever_less(self, value: int) -> bool: def ever_less_or_equal(self, value: int) -> bool: """ - Returns whether the values of `self` are ever less than or equal to `value`. + Returns whether the values of `self` are ever less than or equal to + `value`. Args: value: :class:`int` to compare. Returns: - `True` if the values of `self` are ever less than or equal to `value`, `False` otherwise. + `True` if the values of `self` are ever less than or equal to + `value`, `False` otherwise. MEOS Functions: tint_ever_le @@ -364,7 +381,8 @@ def ever_equal(self, value: int) -> bool: value: :class:`int` to compare. Returns: - `True` if the values of `self` are ever equal to `value`, `False` otherwise. + `True` if the values of `self` are ever equal to `value`, + `False` otherwise. MEOS Functions: tint_ever_eq @@ -379,7 +397,8 @@ def ever_not_equal(self, value: int) -> bool: value: :class:`int` to compare. Returns: - `True` if the values of `self` are ever not equal to `value`, `False` otherwise. + `True` if the values of `self` are ever not equal to `value`, + `False` otherwise. MEOS Functions: tint_always_eq @@ -388,13 +407,15 @@ def ever_not_equal(self, value: int) -> bool: def ever_greater_or_equal(self, value: int) -> bool: """ - Returns whether the values of `self` are ever greater than or equal to `value`. + Returns whether the values of `self` are ever greater than or equal to + `value`. Args: value: :class:`int` to compare. Returns: - `True` if the values of `self` are ever greater than or equal to `value`, `False` otherwise. + `True` if the values of `self` are ever greater than or equal to + `value`, `False` otherwise. MEOS Functions: tint_always_lt @@ -409,7 +430,8 @@ def ever_greater(self, value: int) -> bool: value: :class:`int` to compare. Returns: - `True` if the values of `self` are ever greater than `value`, `False` otherwise. + `True` if the values of `self` are ever greater than `value`, + `False` otherwise. MEOS Functions: tint_always_le @@ -424,7 +446,8 @@ def never_less(self, value: int) -> bool: value: :class:`int` value to compare. Returns: - `True` if the values of `self` are never less than `value`, `False` otherwise. + `True` if the values of `self` are never less than `value`, + `False` otherwise. MEOS Functions: tint_ever_lt @@ -433,13 +456,15 @@ def never_less(self, value: int) -> bool: def never_less_or_equal(self, value: int) -> bool: """ - Returns whether the values of `self` are never less than or equal to `value`. + Returns whether the values of `self` are never less than or equal to + `value`. Args: value: :class:`int` value to compare. Returns: - `True` if the values of `self` are never less than or equal to `value`, `False` otherwise. + `True` if the values of `self` are never less than or equal to + `value`, `False` otherwise. MEOS Functions: tint_ever_le @@ -454,7 +479,8 @@ def never_equal(self, value: int) -> bool: value: :class:`int` value to compare. Returns: - `True` if the values of `self` are never equal to `value`, `False` otherwise. + `True` if the values of `self` are never equal to `value`, + `False` otherwise. MEOS Functions: tint_ever_eq @@ -469,7 +495,8 @@ def never_not_equal(self, value: int) -> bool: value: :class:`int` value to compare. Returns: - `True` if the values of `self` are never not equal to `value`, `False` otherwise. + `True` if the values of `self` are never not equal to `value`, + `False` otherwise. MEOS Functions: tint_always_eq @@ -478,13 +505,15 @@ def never_not_equal(self, value: int) -> bool: def never_greater_or_equal(self, value: int) -> bool: """ - Returns whether the values of `self` are never greater than or equal to `value`. + Returns whether the values of `self` are never greater than or equal to + `value`. Args: value: :class:`int` value to compare. Returns: - `True` if the values of `self` are never greater than or equal to `value`, `False` otherwise. + `True` if the values of `self` are never greater than or equal to + `value`, `False` otherwise. MEOS Functions: tint_always_lt @@ -499,7 +528,8 @@ def never_greater(self, value: int) -> bool: value: :class:`int` value to compare. Returns: - `True` if the values of `self` are never greater than `value`, `False` otherwise. + `True` if the values of `self` are never greater than `value`, + `False` otherwise. MEOS Functions: tint_always_le @@ -572,7 +602,8 @@ def temporal_less_or_equal(self, other: Union[int, Temporal]) -> Temporal: other: A :class:`int` or temporal object to compare to `self`. Returns: - A :class:`TBool` with the result of the temporal less or equal relation. + A :class:`TBool` with the result of the temporal less or equal + relation. MEOS Functions: tle_tint_int, tle_temporal_temporal @@ -591,7 +622,8 @@ def temporal_greater_or_equal(self, other: Union[int, Temporal]) -> Temporal: other: A :class:`int` or temporal object to compare to `self`. Returns: - A :class:`TBool` with the result of the temporal greater or equal relation. + A :class:`TBool` with the result of the temporal greater or equal + relation. MEOS Functions: tge_tint_int, tge_temporal_temporal @@ -610,7 +642,8 @@ def temporal_greater(self, other: Union[int, Temporal]) -> Temporal: other: A :class:`int` or temporal object to compare to `self`. Returns: - A :class:`TBool` with the result of the temporal greater than relation. + A :class:`TBool` with the result of the temporal greater than + relation. MEOS Functions: tgt_tint_int, tgt_temporal_temporal @@ -623,10 +656,11 @@ def temporal_greater(self, other: Union[int, Temporal]) -> Temporal: # ------------------------- Restrictions ---------------------------------- def at(self, other: Union[int, List[int], - intrange, floatrange, List[intrange], List[floatrange], TBox, - datetime, TimestampSet, Period, PeriodSet]) -> Temporal: + intrange, floatrange, List[intrange], List[floatrange], TBox, + datetime, TimestampSet, Period, PeriodSet]) -> Temporal: """ - Returns a new temporal int with th e values of `self` restricted to the time or value `other`. + Returns a new temporal int with th e values of `self` restricted to + the time or value `other`. Args: other: Time or value to restrict to. @@ -635,7 +669,8 @@ def at(self, other: Union[int, List[int], A new temporal int. MEOS Functions: - tint_at_value, temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, temporal_at_periodset + tint_at_value, temporal_at_timestamp, temporal_at_timestampset, + temporal_at_period, temporal_at_periodset """ if isinstance(other, int): result = tint_at_value(self._inner, other) @@ -649,8 +684,8 @@ def minus(self, other: Union[int, List[int], intrange, floatrange, List[intrange], List[floatrange], TBox, datetime, TimestampSet, Period, PeriodSet]) -> Temporal: """ - Returns a new temporal int with the values of `self` restricted to the complement of the time or value - `other`. + Returns a new temporal int with the values of `self` restricted to the + complement of the time or value `other`. Args: other: Time or value to restrict to the complement of. @@ -659,7 +694,8 @@ def minus(self, other: Union[int, List[int], A new temporal int. MEOS Functions: - tint_minus_value, temporal_minus_timestamp, temporal_minus_timestampset, temporal_minus_period, + tint_minus_value, temporal_minus_timestamp, + temporal_minus_timestampset, temporal_minus_period, temporal_minus_periodset """ if isinstance(other, int): @@ -671,18 +707,22 @@ def minus(self, other: Union[int, List[int], return Temporal._factory(result) # ------------------------- Distance -------------------------------------- - def nearest_approach_distance(self, other: Union[int, float, TNumber, TBox]) -> float: + def nearest_approach_distance(self, + other: Union[int, float, TNumber, TBox]) -> float: """ Returns the nearest approach distance between `self` and `other`. Args: - other: A :class:`int`, :class:`float`, :class:`TNumber` or :class:`TBox` to compare to `self`. + other: A :class:`int`, :class:`float`, :class:`TNumber` or + :class:`TBox` to compare to `self`. Returns: - A :class:`float` with the nearest approach distance between `self` and `other`. + A :class:`float` with the nearest approach distance between `self` + and `other`. MEOS Functions: - nad_tint_int, nad_tint_tint, nad_tfloat_float, nad_tfloat_tfloat, nad_tnumber_tbox + nad_tint_int, nad_tint_tint, nad_tfloat_float, nad_tfloat_tfloat, + nad_tnumber_tbox """ if isinstance(other, int): return nad_tint_int(self._inner, other) @@ -709,7 +749,8 @@ def value_split(self, size: int, start: Optional[int] = 0) -> List[TInt]: tiles, new_count = tint_value_split(self._inner, size, start) return [Temporal._factory(tiles[i]) for i in range(new_count)] - def time_value_split(self, value_start: int, value_size: int, time_start: Union[str, datetime], + def time_value_split(self, value_start: int, value_size: int, + time_start: Union[str, datetime], duration: Union[str, timedelta]) -> List[TInt]: """ Splits `self` into fragments with respect to value and period buckets. @@ -726,17 +767,22 @@ def time_value_split(self, value_start: int, value_size: int, time_start: Union[ MEOS Functions: tint_value_time_split """ - st = datetime_to_timestamptz(time_start) if isinstance(time_start, datetime) \ + st = datetime_to_timestamptz(time_start) \ + if isinstance(time_start, datetime) \ else pg_timestamptz_in(time_start, -1) - dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else pg_interval_in(duration, -1) - tiles, new_count = tint_value_time_split(self._inner, value_size, value_start, dt, st) + dt = timedelta_to_interval(duration) \ + if isinstance(duration, timedelta) \ + else pg_interval_in(duration, -1) + tiles, new_count = tint_value_time_split(self._inner, value_size, + value_start, dt, st) return [Temporal._factory(tiles[i]) for i in range(new_count)] # ------------------------- Database Operations --------------------------- @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`TInt` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`TInt` from a database cursor. Used when automatically + loading objects from the database. Users should use the class constructor instead. """ if not value: @@ -760,9 +806,11 @@ class TIntInst(TInstant[int, 'TInt', 'TIntInst', 'TIntSeq', 'TIntSeqSet'], TInt) _make_function = tintinst_make _cast_function = int - def __init__(self, string: Optional[str] = None, *, value: Optional[Union[str, int]] = None, + def __init__(self, string: Optional[str] = None, *, + value: Optional[Union[str, int]] = None, timestamp: Optional[Union[str, datetime]] = None, _inner=None): - super().__init__(string=string, value=value, timestamp=timestamp, _inner=_inner) + super().__init__(string=string, value=value, timestamp=timestamp, + _inner=_inner) class TIntSeq(TSequence[int, 'TInt', 'TIntInst', 'TIntSeq', 'TIntSeqSet'], TInt): @@ -771,19 +819,26 @@ class TIntSeq(TSequence[int, 'TInt', 'TIntInst', 'TIntSeq', 'TIntSeqSet'], TInt) """ ComponentClass = TIntInst - def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TIntInst]]] = None, - lower_inc: bool = True, upper_inc: bool = False, expandable: Union[bool, int] = False, - interpolation: TInterpolation = TInterpolation.STEPWISE, normalize: bool = True, _inner=None): - super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, normalize=normalize, _inner=_inner) + def __init__(self, string: Optional[str] = None, *, + instant_list: Optional[List[Union[str, TIntInst]]] = None, + lower_inc: bool = True, upper_inc: bool = False, + expandable: Union[bool, int] = False, + interpolation: TInterpolation = TInterpolation.STEPWISE, + normalize: bool = True, _inner=None): + super().__init__(string=string, instant_list=instant_list, + lower_inc=lower_inc, upper_inc=upper_inc, expandable=expandable, + interpolation=interpolation, normalize=normalize, _inner=_inner) -class TIntSeqSet(TSequenceSet[int, 'TInt', 'TIntInst', 'TIntSeq', 'TIntSeqSet'], TInt): +class TIntSeqSet(TSequenceSet[int, 'TInt', 'TIntInst', 'TIntSeq', 'TIntSeqSet'], + TInt): """ Class for representing temporal integers over a period of time with gaps. """ ComponentClass = TIntSeq - def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, TIntSeq]]] = None, - normalize: bool = True, _inner=None): - super().__init__(string=string, sequence_list=sequence_list, normalize=normalize, _inner=_inner) + def __init__(self, string: Optional[str] = None, *, + sequence_list: Optional[List[Union[str, TIntSeq]]] = None, + normalize: bool = True, _inner=None): + super().__init__(string=string, sequence_list=sequence_list, + normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/tnumber.py b/pymeos/pymeos/main/tnumber.py index 0246f903..5ac26164 100644 --- a/pymeos/pymeos/main/tnumber.py +++ b/pymeos/pymeos/main/tnumber.py @@ -62,9 +62,11 @@ def time_weighted_average(self) -> float: return tnumber_twavg(self._inner) # ------------------------- Restrictions ---------------------------------- - def at(self, other: Union[intrange, floatrange, List[intrange], List[floatrange], TBox, Time]) -> TG: + def at(self, other: Union[intrange, floatrange, List[intrange], + List[floatrange], TBox, Time]) -> TG: """ - Returns a new temporal object with the values of `self` restricted to the value or time `other`. + Returns a new temporal object with the values of `self` restricted to + the value or time `other`. Args: other: A time or value object to restrict the values of `self` to. @@ -74,7 +76,8 @@ def at(self, other: Union[intrange, floatrange, List[intrange], List[floatrange] MEOS Functions: tnumber_at_span, tnumber_at_spanset, tnumber_at_tbox, - temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, temporal_at_periodset + temporal_at_timestamp, temporal_at_timestampset, + temporal_at_period, temporal_at_periodset """ from ..boxes import TBox if isinstance(other, intrange): @@ -95,26 +98,31 @@ def at(self, other: Union[intrange, floatrange, List[intrange], List[floatrange] return super().at(other) return Temporal._factory(result) - def minus(self, other: Union[intrange, floatrange, List[intrange], List[floatrange], TBox, Time]) -> TG: + def minus(self, other: Union[intrange, floatrange, List[intrange], + List[floatrange], TBox, Time]) -> TG: """ - Returns a new temporal object with the values of `self` restricted to the complement of the value or time - `other`. + Returns a new temporal object with the values of `self` restricted to + the complement of the value or time `other`. Args: - other: A time or value object to restrict the values of `self` to the complement of. + other: A time or value object to restrict the values of `self` to + the complement of. Returns: A new temporal object of the same subtype as `self`. MEOS Functions: tnumber_minus_span, tnumber_minus_spanset, tnumber_minus_tbox, - temporal_minus_timestamp, temporal_minus_timestampset, temporal_minus_period, temporal_minus_periodset + temporal_minus_timestamp, temporal_minus_timestampset, + temporal_minus_period, temporal_minus_periodset """ from ..boxes import TBox if isinstance(other, intrange): - result = tnumber_minus_span(self._inner, intrange_to_intspan(other)) + result = tnumber_minus_span(self._inner, + intrange_to_intspan(other)) elif isinstance(other, floatrange): - result = tnumber_minus_span(self._inner, floatrange_to_floatspan(other)) + result = tnumber_minus_span(self._inner, + floatrange_to_floatspan(other)) elif isinstance(other, list) and isinstance(other[0], intrange): spans = [intrange_to_intspan(ir) for ir in other] spanset = spanset_make(spans, len(spans), True) @@ -132,7 +140,8 @@ def minus(self, other: Union[intrange, floatrange, List[intrange], List[floatran # ------------------------- Position Operations --------------------------- def is_left(self, other: Union[Temporal, Box]) -> bool: """ - Returns whether the bounding box of `self` is left to the bounding box of `other`. + Returns whether the bounding box of `self` is left to the bounding box + of `other`. Args: other: A box or a temporal object to compare to `self`. @@ -147,7 +156,8 @@ def is_left(self, other: Union[Temporal, Box]) -> bool: def is_over_or_left(self, other: Union[Temporal, Box]) -> bool: """ - Returns whether the bounding box of `self` is over or left to the bounding box of `other`. + Returns whether the bounding box of `self` is over or left to the + bounding box of `other`. Args: other: A box or a temporal object to compare to `self`. @@ -162,7 +172,8 @@ def is_over_or_left(self, other: Union[Temporal, Box]) -> bool: def is_right(self, other: Union[Temporal, Box]) -> bool: """ - Returns whether the bounding box of `self` is right to the bounding box of `other`. + Returns whether the bounding box of `self` is right to the bounding + box of `other`. Args: other: A box or a temporal object to compare to `self`. @@ -177,7 +188,8 @@ def is_right(self, other: Union[Temporal, Box]) -> bool: def is_over_or_right(self, other: Union[Temporal, Box]) -> bool: """ - Returns whether the bounding box of `self` is over or right to the bounding box of `other`. + Returns whether the bounding box of `self` is over or right to the + bounding box of `other`. Args: other: A box or a temporal object to compare to `self`. @@ -196,7 +208,8 @@ def add(self, other: Union[int, float, TNumber]) -> TNumber: Returns a new temporal object with the values of `self` plus `other`. Args: - other: A :class:`int`, :class:`float` or :class:`TNumber` to add to `self`. + other: A :class:`int`, :class:`float` or :class:`TNumber` to add + to `self`. Returns: A new temporal object of the same subtype as `self`. @@ -241,7 +254,8 @@ def sub(self, other: Union[int, float, TNumber]) -> TNumber: Returns a new temporal object with the values of `self` minus `other`. Args: - other: A :class:`int`, :class:`float` or :class:`TNumber` to subtract from `self`. + other: A :class:`int`, :class:`float` or :class:`TNumber` to + subtract from `self`. Returns: A new temporal object of the same subtype as `self`. @@ -286,10 +300,12 @@ def rsub(self, other: Union[int, float]) -> TNumber: def mul(self, other: Union[int, float, TNumber]) -> TNumber: """ - Returns a new temporal object with the values of `self` multiplied by `other`. + Returns a new temporal object with the values of `self` multiplied by + `other`. Args: - other: A :class:`int`, :class:`float` or :class:`TNumber` to multiply `self` by. + other: A :class:`int`, :class:`float` or :class:`TNumber` to + multiply `self` by. Returns: A new temporal object of the same subtype as `self`. @@ -311,7 +327,8 @@ def mul(self, other: Union[int, float, TNumber]) -> TNumber: def rmul(self, other: Union[int, float]) -> TNumber: """ - Returns a new temporal object with the values of `self` multiplied by `other`. + Returns a new temporal object with the values of `self` multiplied by + `other`. Args: other: A :class:`int` or :class:`float` to multiply by `self`. @@ -334,10 +351,12 @@ def rmul(self, other: Union[int, float]) -> TNumber: def div(self, other: Union[int, float, TNumber]) -> TNumber: """ - Returns a new temporal object with the values of `self` divided by `other`. + Returns a new temporal object with the values of `self` divided by + `other`. Args: - other: A :class:`int`, :class:`float` or :class:`TNumber` to divide `self` by. + other: A :class:`int`, :class:`float` or :class:`TNumber` to divide + `self` by. Returns: A new temporal object of the same subtype as `self`. @@ -359,7 +378,8 @@ def div(self, other: Union[int, float, TNumber]) -> TNumber: def rdiv(self, other: Union[int, float]) -> TNumber: """ - Returns a new temporal object with the values of `other` divided by `self`. + Returns a new temporal object with the values of `other` divided by + `self`. Args: other: A :class:`int` or :class:`float` to divide by `self`. @@ -385,7 +405,8 @@ def __add__(self, other): Returns a new temporal object with the values of `self` plus `other`. Args: - other: A :class:`int`, :class:`float` or :class:`TNumber` to add to `self`. + other: A :class:`int`, :class:`float` or :class:`TNumber` to add to + `self`. Returns: A new temporal object of the same subtype as `self`. @@ -412,7 +433,8 @@ def __sub__(self, other): Returns a new temporal object with the values of `self` minus `other`. Args: - other: A :class:`int`, :class:`float` or :class:`TNumber` to subtract from `self`. + other: A :class:`int`, :class:`float` or :class:`TNumber` to + subtract from `self`. Returns: A new temporal object of the same subtype as `self`. @@ -439,10 +461,12 @@ def __rsub__(self, other): def __mul__(self, other): """ - Returns a new temporal object with the values of `self` multiplied by `other`. + Returns a new temporal object with the values of `self` multiplied by + `other`. Args: - other: A :class:`int`, :class:`float` or :class:`TNumber` to multiply `self` by. + other: A :class:`int`, :class:`float` or :class:`TNumber` to + multiply `self` by. Returns: A new temporal object of the same subtype as `self`. @@ -454,7 +478,8 @@ def __mul__(self, other): def __rmul__(self, other): """ - Returns a new temporal object with the values of `self` multiplied by `other`. + Returns a new temporal object with the values of `self` multiplied + by `other`. Args: other: A :class:`int` or :class:`float` to multiply by `self`. @@ -469,10 +494,12 @@ def __rmul__(self, other): def __truediv__(self, other): """ - Returns a new temporal object with the values of `self` divided by `other`. + Returns a new temporal object with the values of `self` divided by + `other`. Args: - other: A :class:`int`, :class:`float` or :class:`TNumber` to divide `self` by. + other: A :class:`int`, :class:`float` or :class:`TNumber` to divide + `self` by. Returns: A new temporal object of the same subtype as `self`. @@ -484,7 +511,8 @@ def __truediv__(self, other): def __rtruediv__(self, other): """ - Returns a new temporal object with the values of `other` divided by `self`. + Returns a new temporal object with the values of `other` divided by + `self`. Args: other: A :class:`int` or :class:`float` to divide by `self`. @@ -529,7 +557,8 @@ def distance(self, other: Union[int, float, TNumber]) -> TFloat: Returns the temporal distance between `self` and `other`. Args: - other: A :class:`int`, :class:`float` or :class:`TNumber` to compare to `self`. + other: A :class:`int`, :class:`float` or :class:`TNumber` to + compare to `self`. Returns: A :class:`TFloat` with the distance between `self` and `other`. @@ -547,15 +576,18 @@ def distance(self, other: Union[int, float, TNumber]) -> TFloat: raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) - def nearest_approach_distance(self, other: Union[int, float, TNumber, TBox]) -> float: + def nearest_approach_distance(self, other: Union[int, float, + TNumber, TBox]) -> float: """ Returns the nearest approach distance between `self` and `other`. Args: - other: A :class:`int`, :class:`float`, :class:`TNumber` or :class:`TBox` to compare to `self`. + other: A :class:`int`, :class:`float`, :class:`TNumber` or + :class:`TBox` to compare to `self`. Returns: - A :class:`float` with the nearest approach distance between `self` and `other`. + A :class:`float` with the nearest approach distance between `self` + and `other`. MEOS Functions: nad_tfloat_float, nad_tfloat_tfloat, nad_tnumber_tbox diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index b43ddcab..88fef5eb 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -1160,13 +1160,16 @@ def to_dataframe(self, precision: int = 15) -> GeoDataFrame: precision: The precision of the returned geometry. Returns: - A new :class:`GeoDataFrame` representing the temporal point sequence set. + A new :class:`GeoDataFrame` representing the temporal point + sequence set. """ sequences = self.sequences() data = { - 'sequence': [i + 1 for i, seq in enumerate(sequences) for _ in range(seq.num_instants())], + 'sequence': [i + 1 for i, seq in enumerate(sequences) for _ in + range(seq.num_instants())], 'time': [t for seq in sequences for t in seq.timestamps()], - 'geometry': [v for seq in sequences for v in seq.values(precision=precision)] + 'geometry': [v for seq in sequences for v in + seq.values(precision=precision)] } return GeoDataFrame(data, crs=self.srid()).set_index(keys=['sequence', 'time']) @@ -1188,7 +1191,8 @@ def plot(self, *args, **kwargs): return TemporalPointSequenceSetPlotter.plot_xy(self, *args, **kwargs) -class TGeomPoint(TPoint['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', 'TGeomPointSeqSet'], ABC): +class TGeomPoint(TPoint['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', + 'TGeomPointSeqSet'], ABC): """ Abstract class for temporal geometric points. """ @@ -1197,9 +1201,11 @@ class TGeomPoint(TPoint['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', 'TGeomP # ------------------------- Output ---------------------------------------- @staticmethod - def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], base: Temporal) -> TGeomPoint: + def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], + base: Temporal) -> TGeomPoint: """ - Creates a temporal geometric point from a base geometry and the time frame of another temporal object. + Creates a temporal geometric point from a base geometry and the time + frame of another temporal object. Args: value: The base geometry. @@ -1217,17 +1223,20 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], base: Tempo @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: datetime) -> TGeomPointInst: + def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + base: datetime) -> TGeomPointInst: ... @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Union[TimestampSet, Period]) -> TGeomPointSeq: + def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + base: Union[TimestampSet, Period]) -> TGeomPointSeq: ... @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: PeriodSet) -> TGeomPointSeqSet: + def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + base: PeriodSet) -> TGeomPointSeqSet: ... @staticmethod @@ -1250,13 +1259,17 @@ def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Time, """ gs = geometry_to_gserialized(value) if isinstance(base, datetime): - return TGeomPointInst(_inner=tpointinst_make(gs, datetime_to_timestamptz(base))) + return TGeomPointInst(_inner=tpointinst_make(gs, + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): - return TGeomPointSeq(_inner=tpointseq_from_base_timestampset(gs, base._inner)) + return TGeomPointSeq(_inner=tpointseq_from_base_timestampset(gs, + base._inner)) elif isinstance(base, Period): - return TGeomPointSeq(_inner=tpointseq_from_base_period(gs, base._inner, interpolation)) + return TGeomPointSeq(_inner=tpointseq_from_base_period(gs, + base._inner, interpolation)) elif isinstance(base, PeriodSet): - return TGeomPointSeqSet(_inner=tpointseqset_from_base_periodset(gs, base._inner, interpolation)) + return TGeomPointSeqSet(_inner=tpointseqset_from_base_periodset(gs, + base._inner, interpolation)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Conversions ---------------------------------- @@ -1281,12 +1294,14 @@ def to_shapely_geometry(self, precision: int = 15) -> shpb.BaseGeometry: precision: The precision of the returned geometry. Returns: - A new :class:`~shapely.geometry.base.BaseGeometry` representing the trajectory. + A new :class:`~shapely.geometry.base.BaseGeometry` representing the + trajectory. MEOS Functions: gserialized_to_shapely_geometry """ - return gserialized_to_shapely_geometry(tpoint_trajectory(self._inner), precision) + return gserialized_to_shapely_geometry(tpoint_trajectory(self._inner), + precision) def to_dataframe(self) -> GeoDataFrame: """ @@ -1419,7 +1434,8 @@ def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: return super().temporal_equal(other) return Temporal._factory(result) - def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> Temporal: + def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) \ + -> Temporal: """ Returns the temporal inequality relation between `self` and `other`. @@ -1443,7 +1459,8 @@ def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> Tem @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`TGeogPoint` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`TGeogPoint` from a database cursor. Used when + automatically loading objects from the database. Users should use the class constructor instead. """ if not value: @@ -1466,7 +1483,8 @@ def read_from_cursor(value, _=None): raise Exception("ERROR: Could not parse temporal point value") -class TGeogPoint(TPoint['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', 'TGeogPointSeqSet'], ABC): +class TGeogPoint(TPoint['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', + 'TGeogPointSeqSet'], ABC): """ Abstract class for representing temporal geographic points. """ @@ -1475,9 +1493,11 @@ class TGeogPoint(TPoint['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', 'TGeogP # ------------------------- Output ---------------------------------------- @staticmethod - def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], base: Temporal) -> TGeogPoint: + def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], + base: Temporal) -> TGeogPoint: """ - Creates a temporal geographic point from a base geometry and the time frame of another temporal object. + Creates a temporal geographic point from a base geometry and the time + frame of another temporal object. Args: value: The base geometry. @@ -1495,17 +1515,20 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], base: Tempo @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: datetime) -> TGeogPointInst: + def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + base: datetime) -> TGeogPointInst: ... @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Union[TimestampSet, Period]) -> TGeogPointSeq: + def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + base: Union[TimestampSet, Period]) -> TGeogPointSeq: ... @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: PeriodSet) -> TGeogPointSeqSet: + def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + base: PeriodSet) -> TGeogPointSeqSet: ... @staticmethod @@ -1528,13 +1551,17 @@ def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Time, """ gs = geography_to_gserialized(value) if isinstance(base, datetime): - return TGeogPointInst(_inner=tpointinst_make(gs, datetime_to_timestamptz(base))) + return TGeogPointInst(_inner=tpointinst_make(gs, + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): - return TGeogPointSeq(_inner=tpointseq_from_base_timestampset(gs, base._inner)) + return TGeogPointSeq(_inner=tpointseq_from_base_timestampset(gs, + base._inner)) elif isinstance(base, Period): - return TGeogPointSeq(_inner=tpointseq_from_base_period(gs, base._inner, interpolation)) + return TGeogPointSeq(_inner=tpointseq_from_base_period(gs, + base._inner, interpolation)) elif isinstance(base, PeriodSet): - return TGeogPointSeqSet(_inner=tpointseqset_from_base_periodset(gs, base._inner, interpolation)) + return TGeogPointSeqSet(_inner=tpointseqset_from_base_periodset(gs, + base._inner, interpolation)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Conversions ---------------------------------- @@ -1693,7 +1720,8 @@ def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBo @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`TGeogPoint` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`TGeogPoint` from a database cursor. Used when + automatically loading objects from the database. Users should use the class constructor instead. """ if not value: @@ -1716,22 +1744,26 @@ def read_from_cursor(value, _=None): raise Exception("ERROR: Could not parse temporal point value") -class TGeomPointInst(TPointInst['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): +class TGeomPointInst(TPointInst['TGeomPoint', 'TGeomPointInst', + 'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): """ Class for representing temporal geometric points at a single instant. """ _make_function = lambda *args: None _cast_function = lambda x: None - def __init__(self, string: Optional[str] = None, *, point: Optional[Union[str, pg.Point, shp.Point]] = None, + def __init__(self, string: Optional[str] = None, *, + point: Optional[Union[str, pg.Point, shp.Point]] = None, timestamp: Optional[Union[str, datetime]] = None, srid: Optional[int] = 0, _inner=None) -> None: - super().__init__(string=string, value=point, timestamp=timestamp, _inner=_inner) + super().__init__(string=string, value=point, timestamp=timestamp, + _inner=_inner) if self._inner is None: self._inner = tgeompoint_in(f"SRID={srid};{point}@{timestamp}") -class TGeogPointInst(TPointInst['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', 'TGeogPointSeqSet'], TGeogPoint): +class TGeogPointInst(TPointInst['TGeogPoint', 'TGeogPointInst', + 'TGeogPointSeq', 'TGeogPointSeqSet'], TGeogPoint): """ Class for representing temporal geographic points at a single instant. """ @@ -1743,13 +1775,16 @@ def __init__(self, string: Optional[str] = None, *, Tuple[float, float]]] = None, timestamp: Optional[Union[str, datetime]] = None, srid: Optional[int] = 4326, _inner=None) -> None: - super().__init__(string=string, value=point, timestamp=timestamp, _inner=_inner) + super().__init__(string=string, value=point, timestamp=timestamp, + _inner=_inner) if self._inner is None: - p = f'POINT({point[0]} {point[1]})' if isinstance(point, tuple) else f'{point}' + p = f'POINT({point[0]} {point[1]})' if isinstance(point, tuple) \ + else f'{point}' self._inner = tgeogpoint_in(f"SRID={srid};{p}@{timestamp}") -class TGeomPointSeq(TPointSeq['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): +class TGeomPointSeq(TPointSeq['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', + 'TGeomPointSeqSet'], TGeomPoint): """ Class for representing temporal geometric points over a period of time. """ @@ -1761,27 +1796,36 @@ def __init__(self, string: Optional[str] = None, *, expandable: Union[bool, int] = False, interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True, _inner=None): - super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, normalize=normalize, _inner=_inner) + super().__init__(string=string, instant_list=instant_list, + lower_inc=lower_inc, upper_inc=upper_inc, + expandable=expandable, interpolation=interpolation, + normalize=normalize, _inner=_inner) -class TGeogPointSeq(TPointSeq['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', 'TGeogPointSeqSet'], TGeogPoint): +class TGeogPointSeq(TPointSeq['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', + 'TGeogPointSeqSet'], TGeogPoint): """ Class for representing temporal geographic points over a period of time. """ ComponentClass = TGeogPointInst - def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TGeogPointInst]]] = None, - lower_inc: bool = True, upper_inc: bool = False, expandable: Union[bool, int] = False, - interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True, _inner=None): - super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, + def __init__(self, string: Optional[str] = None, *, + instant_list: Optional[List[Union[str, TGeogPointInst]]] = None, + lower_inc: bool = True, upper_inc: bool = False, + expandable: Union[bool, int] = False, + interpolation: TInterpolation = TInterpolation.LINEAR, + normalize: bool = True, _inner=None): + super().__init__(string=string, instant_list=instant_list, + lower_inc=lower_inc, upper_inc=upper_inc, expandable=expandable, interpolation=interpolation, normalize=normalize, _inner=_inner) -class TGeomPointSeqSet(TPointSeqSet['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): +class TGeomPointSeqSet(TPointSeqSet['TGeomPoint', 'TGeomPointInst', + 'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): """ - Class for representing temporal geometric points over a period of time with gaps. + Class for representing temporal geometric points over a period of time + with gaps. """ ComponentClass = TGeomPointSeq @@ -1793,9 +1837,11 @@ def __init__(self, string: Optional[str] = None, *, expandable=expandable, normalize=normalize, _inner=_inner) -class TGeogPointSeqSet(TPointSeqSet['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', 'TGeogPointSeqSet'], TGeogPoint): +class TGeogPointSeqSet(TPointSeqSet['TGeogPoint', 'TGeogPointInst', + 'TGeogPointSeq', 'TGeogPointSeqSet'], TGeogPoint): """ - Class for representing temporal geographic points over a period of time with gaps. + Class for representing temporal geographic points over a period of time + with gaps. """ ComponentClass = TGeogPointSeq @@ -1804,4 +1850,5 @@ def __init__(self, string: Optional[str] = None, *, expandable: Union[bool, int] = False, normalize: bool = True, _inner=None): super().__init__(string=string, sequence_list=sequence_list, - expandable=expandable, normalize=normalize, _inner=_inner) + expandable=expandable, normalize=normalize, + _inner=_inner) diff --git a/pymeos/pymeos/main/ttext.py b/pymeos/pymeos/main/ttext.py index 1cd8831a..72f3b052 100644 --- a/pymeos/pymeos/main/ttext.py +++ b/pymeos/pymeos/main/ttext.py @@ -25,7 +25,8 @@ def __init__(self, _inner) -> None: @staticmethod def from_base_temporal(value: str, base: Temporal) -> TText: """ - Create a temporal string from a string and the time frame of another temporal object. + Create a temporal string from a string and the time frame of another + temporal object. Args: value: string value of the temporal. @@ -47,7 +48,8 @@ def from_base_time(value: str, base: datetime) -> TTextInst: @staticmethod @overload - def from_base_time(value: str, base: Union[TimestampSet, Period]) -> TTextSeq: + def from_base_time(value: str, base: Union[TimestampSet, Period]) -> \ + TTextSeq: ... @staticmethod @@ -72,13 +74,17 @@ def from_base_time(value: str, base: Time) -> TText: ttextseq_from_base_period, ttextseqset_from_base_periodset """ if isinstance(base, datetime): - return TTextInst(_inner=ttextinst_make(value, datetime_to_timestamptz(base))) + return TTextInst(_inner=ttextinst_make(value, + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): - return TTextSeq(_inner=ttextseq_from_base_timestampset(value, base._inner)) + return TTextSeq(_inner=ttextseq_from_base_timestampset(value, + base._inner)) elif isinstance(base, Period): - return TTextSeq(_inner=ttextseq_from_base_period(value, base._inner)) + return TTextSeq(_inner=ttextseq_from_base_period(value, + base._inner)) elif isinstance(base, PeriodSet): - return TTextSeqSet(_inner=ttextseqset_from_base_periodset(value, base._inner)) + return TTextSeqSet(_inner=ttextseqset_from_base_periodset(value, + base._inner)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Output ---------------------------------------- @@ -170,7 +176,8 @@ def end_value(self) -> str: def upper(self) -> TText: """ - Returns a new temporal string with the values of `self` converted to upper case. + Returns a new temporal string with the values of `self` converted to + upper case. Returns: A new temporal string. @@ -182,7 +189,8 @@ def upper(self) -> TText: def lower(self) -> TText: """ - Returns a new temporal string with the values of `self` converted to lower case. + Returns a new temporal string with the values of `self` converted to + lower case. Returns: A new temporal string. @@ -205,7 +213,8 @@ def value_at_timestamp(self, timestamp: datetime) -> str: MEOS Functions: ttext_value_at_timestamp """ - result = ttext_value_at_timestamp(self._inner, datetime_to_timestamptz(timestamp), True) + result = ttext_value_at_timestamp(self._inner, + datetime_to_timestamptz(timestamp), True) return text2cstring(result[0]) # ------------------------- Ever and Always Comparisons ------------------- @@ -217,7 +226,8 @@ def always_equal(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are always equal to `value`, `False` otherwise. + `True` if the values of `self` are always equal to `value`, + `False` otherwise. MEOS Functions: ttext_always_eq @@ -232,7 +242,8 @@ def always_not_equal(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are always not equal to `value`, `False` otherwise. + `True` if the values of `self` are always not equal to `value`, + `False` otherwise. MEOS Functions: ttext_ever_eq @@ -247,7 +258,8 @@ def always_less(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are always less than `value`, `False` otherwise. + `True` if the values of `self` are always less than `value`, + `False` otherwise. MEOS Functions: ttext_always_lt @@ -256,13 +268,15 @@ def always_less(self, value: str) -> bool: def always_less_or_equal(self, value: str) -> bool: """ - Returns whether the values of `self` are always less than or equal to `value`. + Returns whether the values of `self` are always less than or equal to + `value`. Args: value: String value to compare. Returns: - `True` if the values of `self` are always less than or equal to `value`, `False` otherwise. + `True` if the values of `self` are always less than or equal to + `value`, `False` otherwise. MEOS Functions: ttext_always_le @@ -277,7 +291,8 @@ def always_greater(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are always greater than `value`, `False` otherwise. + `True` if the values of `self` are always greater than `value`, + `False` otherwise. MEOS Functions: ttext_ever_le @@ -286,13 +301,15 @@ def always_greater(self, value: str) -> bool: def always_greater_or_equal(self, value: str) -> bool: """ - Returns whether the values of `self` are always greater than or equal to `value`. + Returns whether the values of `self` are always greater than or equal + to `value`. Args: value: String value to compare. Returns: - `True` if the values of `self` are always greater than or equal to `value`, `False` otherwise. + `True` if the values of `self` are always greater than or equal to + `value`, `False` otherwise. MEOS Functions: ttext_ever_lt @@ -307,7 +324,8 @@ def ever_equal(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are ever equal to `value`, `False` otherwise. + `True` if the values of `self` are ever equal to `value`, `False` + otherwise. MEOS Functions: ttext_ever_eq @@ -322,7 +340,8 @@ def ever_not_equal(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are ever not equal to `value`, `False` otherwise. + `True` if the values of `self` are ever not equal to `value`, + `False` otherwise. MEOS Functions: ttext_always_eq @@ -337,7 +356,8 @@ def ever_less(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are ever less than `value`, `False` otherwise. + `True` if the values of `self` are ever less than `value`, `False` + otherwise. MEOS Functions: ttext_ever_lt @@ -346,13 +366,15 @@ def ever_less(self, value: str) -> bool: def ever_less_or_equal(self, value: str) -> bool: """ - Returns whether the values of `self` are ever less than or equal to `value`. + Returns whether the values of `self` are ever less than or equal to + `value`. Args: value: String value to compare. Returns: - `True` if the values of `self` are ever less than or equal to `value`, `False` otherwise. + `True` if the values of `self` are ever less than or equal to + `value`, `False` otherwise. MEOS Functions: ttext_ever_le @@ -367,7 +389,8 @@ def ever_greater(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are ever greater than `value`, `False` otherwise. + `True` if the values of `self` are ever greater than `value`, + `False` otherwise. MEOS Functions: ttext_always_le @@ -376,13 +399,15 @@ def ever_greater(self, value: str) -> bool: def ever_greater_or_equal(self, value: str) -> bool: """ - Returns whether the values of `self` are ever greater than or equal to `value`. + Returns whether the values of `self` are ever greater than or equal to + `value`. Args: value: String value to compare. Returns: - `True` if the values of `self` are ever greater than or equal to `value`, `False` otherwise. + `True` if the values of `self` are ever greater than or equal to + `value`, `False` otherwise. MEOS Functions: ttext_always_lt @@ -397,7 +422,8 @@ def never_equal(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are never equal to `value`, `False` otherwise. + `True` if the values of `self` are never equal to `value`, `False` + otherwise. MEOS Functions: ttext_ever_eq @@ -412,7 +438,8 @@ def never_not_equal(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are never not equal to `value`, `False` otherwise. + `True` if the values of `self` are never not equal to `value`, + `False` otherwise. MEOS Functions: ttext_always_eq @@ -427,7 +454,8 @@ def never_less(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are never less than `value`, `False` otherwise. + `True` if the values of `self` are never less than `value`, `False` + otherwise. MEOS Functions: ttext_ever_lt @@ -436,13 +464,15 @@ def never_less(self, value: str) -> bool: def never_less_or_equal(self, value: str) -> bool: """ - Returns whether the values of `self` are never less than or equal to `value`. + Returns whether the values of `self` are never less than or equal to + `value`. Args: value: String value to compare. Returns: - `True` if the values of `self` are never less than or equal to `value`, `False` otherwise. + `True` if the values of `self` are never less than or equal to + `value`, `False` otherwise. MEOS Functions: ttext_ever_le @@ -451,13 +481,15 @@ def never_less_or_equal(self, value: str) -> bool: def never_greater_or_equal(self, value: str) -> bool: """ - Returns whether the values of `self` are never greater than or equal to `value`. + Returns whether the values of `self` are never greater than or equal to + `value`. Args: value: String value to compare. Returns: - `True` if the values of `self` are never greater than or equal to `value`, `False` otherwise. + `True` if the values of `self` are never greater than or equal to + `value`, `False` otherwise. MEOS Functions: ttext_always_lt @@ -472,7 +504,8 @@ def never_greater(self, value: str) -> bool: value: String value to compare. Returns: - `True` if the values of `self` are never greater than `value`, `False` otherwise. + `True` if the values of `self` are never greater than `value`, + `False` otherwise. MEOS Functions: ttext_always_le @@ -545,7 +578,8 @@ def temporal_less_or_equal(self, other: Union[str, Temporal]) -> Temporal: other: A string or temporal object to compare to `self`. Returns: - A :class:`TBool` with the result of the temporal less or equal relation. + A :class:`TBool` with the result of the temporal less or equal + relation. MEOS Functions: tle_ttext_text, tle_temporal_temporal @@ -564,7 +598,8 @@ def temporal_greater(self, other: Union[str, Temporal]) -> Temporal: other: A string or temporal object to compare to `self`. Returns: - A :class:`TBool` with the result of the temporal greater than relation. + A :class:`TBool` with the result of the temporal greater than + relation. MEOS Functions: tgt_ttext_text, tgt_temporal_temporal @@ -577,13 +612,15 @@ def temporal_greater(self, other: Union[str, Temporal]) -> Temporal: def temporal_greater_or_equal(self, other: Union[str, Temporal]) -> Temporal: """ - Returns the temporal greater or equal relation between `self` and `other`. + Returns the temporal greater or equal relation between `self` and + `other`. Args: other: A string or temporal object to compare to `self`. Returns: - A :class:`TBool` with the result of the temporal greater or equal relation. + A :class:`TBool` with the result of the temporal greater or equal + relation. MEOS Functions: tge_ttext_text, tge_temporal_temporal @@ -595,9 +632,11 @@ def temporal_greater_or_equal(self, other: Union[str, Temporal]) -> Temporal: return Temporal._factory(result) # ------------------------- Restrictions ---------------------------------- - def at(self, other: Union[str, List[str], datetime, TimestampSet, Period, PeriodSet]) -> TText: + def at(self, other: Union[str, List[str], datetime, TimestampSet, Period, + PeriodSet]) -> TText: """ - Returns a new temporal string with the values of `self` restricted to the time or value `other`. + Returns a new temporal string with the values of `self` restricted to + the time or value `other`. Args: other: Time or value to restrict to. @@ -606,7 +645,8 @@ def at(self, other: Union[str, List[str], datetime, TimestampSet, Period, Period A new temporal string. MEOS Functions: - ttext_at_value, temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, temporal_at_periodset + ttext_at_value, temporal_at_timestamp, temporal_at_timestampset, + temporal_at_period, temporal_at_periodset """ if isinstance(other, str): result = ttext_at_value(self._inner, other) @@ -616,10 +656,11 @@ def at(self, other: Union[str, List[str], datetime, TimestampSet, Period, Period return super().at(other) return Temporal._factory(result) - def minus(self, other: Union[str, List[str], datetime, TimestampSet, Period, PeriodSet]) -> TText: + def minus(self, other: Union[str, List[str], datetime, TimestampSet, + Period, PeriodSet]) -> TText: """ - Returns a new temporal string with the values of `self` restricted to the complement of the time or value - `other`. + Returns a new temporal string with the values of `self` restricted to + the complement of the time or value `other`. Args: other: Time or value to restrict to the complement of. @@ -628,7 +669,8 @@ def minus(self, other: Union[str, List[str], datetime, TimestampSet, Period, Per A new temporal string. MEOS Functions: - ttext_minus_value, temporal_minus_timestamp, temporal_minus_timestampset, temporal_minus_period, + ttext_minus_value, temporal_minus_timestamp, + temporal_minus_timestampset, temporal_minus_period, temporal_minus_periodset """ if isinstance(other, str): @@ -642,11 +684,13 @@ def minus(self, other: Union[str, List[str], datetime, TimestampSet, Period, Per # ------------------------- Text Operations ------------------------------ def concatenate(self, other: Union[str, TText], other_before: bool = False): """ - Returns a new temporal string with the values of `self` concatenated with the values of `other`. + Returns a new temporal string with the values of `self` concatenated + with the values of `other`. Args: other: Temporal string or string to concatenate. - other_before: If `True` the values of `other` are prepended to the values of `self`. + other_before: If `True` the values of `other` are prepended to the + values of `self`. Returns: A new temporal string. @@ -656,10 +700,12 @@ def concatenate(self, other: Union[str, TText], other_before: bool = False): """ if isinstance(other, str): - result = textcat_ttext_text(self._inner, other) if not other_before \ + result = textcat_ttext_text(self._inner, other) \ + if not other_before \ else textcat_text_ttext(other, self._inner) elif isinstance(other, TText): - result = textcat_ttext_ttext(self._inner, other._inner) if not other_before \ + result = textcat_ttext_ttext(self._inner, other._inner) \ + if not other_before \ else textcat_ttext_ttext(other._inner, self._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -667,7 +713,8 @@ def concatenate(self, other: Union[str, TText], other_before: bool = False): def __add__(self, other): """ - Returns a new temporal string with the values of `self` concatenated with the values of `other`. + Returns a new temporal string with the values of `self` concatenated + with the values of `other`. Args: other: Temporal string or string to concatenate. @@ -682,7 +729,8 @@ def __add__(self, other): def __radd__(self, other): """ - Returns a new temporal string with the values of `other` concatenated with the values of `self`. + Returns a new temporal string with the values of `other` concatenated + with the values of `self`. Args: other: Temporal string or string to concatenate. @@ -699,7 +747,8 @@ def __radd__(self, other): @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`TText` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`TText` from a database cursor. Used when automatically + loading objects from the database. Users should use the class constructor instead. """ if not value: @@ -716,37 +765,50 @@ def read_from_cursor(value, _=None): raise Exception("ERROR: Could not parse temporal text value") -class TTextInst(TInstant[str, 'TText', 'TTextInst', 'TTextSeq', 'TTextSeqSet'], TText): +class TTextInst(TInstant[str, 'TText', 'TTextInst', 'TTextSeq', + 'TTextSeqSet'], TText): """ Class for representing temporal strings at a single instant. """ _make_function = ttextinst_make _cast_function = str - def __init__(self, string: Optional[str] = None, *, value: Optional[str] = None, - timestamp: Optional[Union[str, datetime]] = None, _inner=None): - super().__init__(string=string, value=value, timestamp=timestamp, _inner=_inner) + def __init__(self, string: Optional[str] = None, *, + value: Optional[str] = None, + timestamp: Optional[Union[str, datetime]] = None, + _inner=None): + super().__init__(string=string, value=value, timestamp=timestamp, + _inner=_inner) -class TTextSeq(TSequence[str, 'TText', 'TTextInst', 'TTextSeq', 'TTextSeqSet'], TText): +class TTextSeq(TSequence[str, 'TText', 'TTextInst', 'TTextSeq', + 'TTextSeqSet'], TText): """ Class for representing temporal strings over a period of time. """ ComponentClass = TTextInst - def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TTextInst]]] = None, - lower_inc: bool = True, upper_inc: bool = False, expandable: Union[bool, int] = False, - interpolation: TInterpolation = TInterpolation.STEPWISE, normalize: bool = True, _inner=None): - super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, normalize=normalize, _inner=_inner) + def __init__(self, string: Optional[str] = None, *, + instant_list: Optional[List[Union[str, TTextInst]]] = None, + lower_inc: bool = True, upper_inc: bool = False, + expandable: Union[bool, int] = False, + interpolation: TInterpolation = TInterpolation.STEPWISE, + normalize: bool = True, _inner=None): + super().__init__(string=string, instant_list=instant_list, + lower_inc=lower_inc, upper_inc=upper_inc, + expandable=expandable, interpolation=interpolation, + normalize=normalize, _inner=_inner) -class TTextSeqSet(TSequenceSet[str, 'TText', 'TTextInst', 'TTextSeq', 'TTextSeqSet'], TText): +class TTextSeqSet(TSequenceSet[str, 'TText', 'TTextInst', 'TTextSeq', + 'TTextSeqSet'], TText): """ Class for representing temporal strings over a period of time with gaps. """ ComponentClass = TTextSeq - def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, TTextSeq]]] = None, - normalize: bool = True, _inner=None): - super().__init__(string=string, sequence_list=sequence_list, normalize=normalize, _inner=_inner) + def __init__(self, string: Optional[str] = None, *, + sequence_list: Optional[List[Union[str, TTextSeq]]] = None, + normalize: bool = True, _inner=None): + super().__init__(string=string, sequence_list=sequence_list, + normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/temporal/interpolation.py b/pymeos/pymeos/temporal/interpolation.py index 4cb0b577..a6bbbe7b 100644 --- a/pymeos/pymeos/temporal/interpolation.py +++ b/pymeos/pymeos/temporal/interpolation.py @@ -5,7 +5,8 @@ class TInterpolation(IntEnum): """ - Enum for representing the different types of interpolation present in PyMEOS. + Enum for representing the different types of interpolation present in + PyMEOS. """ NONE = 0 DISCRETE = 1 @@ -19,13 +20,15 @@ def from_string(source: str, none: bool = True) -> TInterpolation: Args: source: :class:`string` representing the interpolation - none: indicates whether to return `TIntepolation.NONE` when `source` represents an invalid interpolation + none: indicates whether to return `TIntepolation.NONE` when + `source` represents an invalid interpolation Returns: A new :class:`TInterpolation` instance. Raises: - ValueError: when `source` doesn't represent any valid interpolation and `none` is False + ValueError: when `source` doesn't represent any valid interpolation + and `none` is False """ if source.lower() == 'discrete': diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index 54328586..ea2ce368 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -140,32 +140,38 @@ def from_mfjson(cls: Type[Self], mfjson: str) -> Self: @classmethod def from_merge(cls: Type[Self], *temporals: TG) -> Self: """ - Returns a temporal object that is the result of merging the given temporal objects. + Returns a temporal object that is the result of merging the given + temporal objects. Args: *temporals: The temporal objects to merge. Returns: - A temporal object that is the result of merging the given temporal objects. + A temporal object that is the result of merging the given temporal + objects. MEOS Functions: temporal_merge_array """ - result = temporal_merge_array([temp._inner for temp in temporals if temp is not None], len(temporals)) + result = temporal_merge_array([temp._inner for temp in temporals \ + if temp is not None], len(temporals)) return Temporal._factory(result) @classmethod def from_merge_array(cls: Type[Self], temporals: List[TG]) -> Self: """ - Returns a temporal object that is the result of merging the given temporal objects. + Returns a temporal object that is the result of merging the given + temporal objects. Args: temporals: The temporal objects to merge. Returns: - A temporal object that is the result of merging the given temporal objects. + A temporal object that is the result of merging the given temporal + objects. """ - result = temporal_merge_array([temp._inner for temp in temporals if temp is not None], len(temporals)) + result = temporal_merge_array([temp._inner for temp in temporals \ + if temp is not None], len(temporals)) return Temporal._factory(result) # ------------------------- Output ---------------------------------------- @@ -302,12 +308,14 @@ def time(self) -> PeriodSet: def duration(self, ignore_gaps=False) -> timedelta: """ - Returns the duration of `self`. By default, the gaps in `self` are taken into account, but this can be - changed by setting `ignore_gaps` to ``True``. This will only potentially alter the result for sequence sets and - discrete sequences. + Returns the duration of `self`. By default, the gaps in `self` are + taken into account, but this can be changed by setting `ignore_gaps` + to ``True``. This will only potentially alter the result for sequence + sets and discrete sequences. Parameters: - ignore_gaps: Whether to take into account potential time gaps in the temporal value. + ignore_gaps: Whether to take into account potential time gaps in + the temporal value. MEOS Functions: temporal_duration @@ -316,7 +324,8 @@ def duration(self, ignore_gaps=False) -> timedelta: def period(self) -> Period: """ - Returns the :class:`Period` on which `self` is defined ignoring potential time gaps. + Returns the :class:`Period` on which `self` is defined ignoring + potential time gaps. MEOS Functions: temporal_to_period @@ -325,7 +334,8 @@ def period(self) -> Period: def timespan(self) -> Period: """ - Returns the :class:`Period` on which `self` is defined ignoring potential time gaps. + Returns the :class:`Period` on which `self` is defined ignoring + potential time gaps. MEOS Functions: temporal_to_period @@ -476,7 +486,8 @@ def __hash__(self) -> int: # ------------------------- Transformations ------------------------------- def set_interpolation(self: Self, interpolation: TInterpolation) -> Self: """ - Returns a new :class:`Temporal` object equal to `self` with the given interpolation. + Returns a new :class:`Temporal` object equal to `self` with the given + interpolation. MEOS Functions: temporal_set_interpolation @@ -486,7 +497,8 @@ def set_interpolation(self: Self, interpolation: TInterpolation) -> Self: def shift(self, delta: timedelta) -> Period: """ - Returns a new :class:`Temporal` with the temporal dimension shifted by ``delta``. + Returns a new :class:`Temporal` with the temporal dimension shifted by + ``delta``. Args: delta: :class:`datetime.timedelta` instance to shift @@ -499,10 +511,12 @@ def shift(self, delta: timedelta) -> Period: def tscale(self, duration: timedelta) -> Period: """ - Returns a new :class:`Temporal` scaled so the temporal dimension has duration ``duration``. + Returns a new :class:`Temporal` scaled so the temporal dimension has + duration ``duration``. Args: - duration: :class:`datetime.timedelta` instance representing the duration of the new temporal + duration: :class:`datetime.timedelta` instance representing the + duration of the new temporal MEOS Functions: temporal_tscale @@ -510,19 +524,23 @@ def tscale(self, duration: timedelta) -> Period: scaled = temporal_tscale(self._inner, timedelta_to_interval(duration)) return Temporal._factory(scaled) - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> Self: + def shift_tscale(self, shift: Optional[timedelta] = None, + duration: Optional[timedelta] = None) -> Self: """ - Returns a new :class:`Temporal` with the time dimension shifted by ``shift`` and scaled so the temporal - dimension has duration ``duration``. + Returns a new :class:`Temporal` with the time dimension shifted by + ``shift`` and scaled so the temporal dimension has duration + ``duration``. Args: shift: :class:`datetime.timedelta` instance to shift - duration: :class:`datetime.timedelta` instance representing the duration of the new temporal + duration: :class:`datetime.timedelta` instance representing the + duration of the new temporal MEOS Functions: temporal_shift_tscale """ - assert shift is not None or duration is not None, 'shift and duration must not be both None' + assert shift is not None or duration is not None, \ + 'shift and duration must not be both None' scaled = temporal_shift_tscale( self._inner, timedelta_to_interval(shift) if shift else None, @@ -536,9 +554,10 @@ def temporal_sample(self, duration: Union[str, timedelta], Returns a new :class:`Temporal` downsampled with respect to ``duration``. Args: - duration: A :class:`str` or :class:`timedelta` with the duration of the temporal tiles. - start: A :class:`str` or :class:`datetime` with the start time of the temporal tiles. If None, the start - time of `self` is used. + duration: A :class:`str` or :class:`timedelta` with the duration of + the temporal tiles. + start: A :class:`str` or :class:`datetime` with the start time of + the temporal tiles. If None, the start time of `self` is used. MEOS Functions: temporal_tsample @@ -562,9 +581,10 @@ def temporal_precision(self, duration: Union[str, timedelta], Returns a new :class:`Temporal` with precision reduced to ``duration``. Args: - duration: A :class:`str` or :class:`timedelta` with the duration of the temporal tiles. - start: A :class:`str` or :class:`datetime` with the start time of the temporal tiles. If None, the start - time of `self` is used. + duration: A :class:`str` or :class:`timedelta` with the duration + of the temporal tiles. + start: A :class:`str` or :class:`datetime` with the start time of + the temporal tiles. If None, the start time of `self` is used. MEOS Functions: temporal_tprecision @@ -614,7 +634,8 @@ def to_sequenceset(self, interpolation: TInterpolation) -> TSS: def to_dataframe(self) -> DataFrame: """ - Returns `self` as a :class:`pd.DataFrame` with two columns: `time` and `value`. + Returns `self` as a :class:`pd.DataFrame` with two columns: `time` + and `value`. """ data = { 'time': self.timestamps(), @@ -626,7 +647,8 @@ def to_dataframe(self) -> DataFrame: def append_instant(self, instant: TInstant[TBase], max_dist: Optional[float] = 0.0, max_time: Optional[timedelta] = None) -> TG: """ - Returns a new :class:`Temporal` object equal to `self` with `instant` appended. + Returns a new :class:`Temporal` object equal to `self` with `instant` + appended. Args: instant: :class:`TInstant` to append @@ -640,15 +662,16 @@ def append_instant(self, instant: TInstant[TBase], max_dist: Optional[float] = 0 interv = None else: interv = timedelta_to_interval(max_time) - new_inner = temporal_append_tinstant(self._inner, instant._inner, max_dist, - interv, self._expandable()) + new_inner = temporal_append_tinstant(self._inner, instant._inner, + max_dist, interv, self._expandable()) if new_inner == self._inner: return self return Temporal._factory(new_inner) def append_sequence(self, sequence: TSequence[TBase]) -> TG: """ - Returns a new :class:`Temporal` object equal to `self` with `sequence` appended. + Returns a new :class:`Temporal` object equal to `self` with `sequence` + appended. Args: sequence: :class:`TSequence` to append @@ -662,9 +685,11 @@ def append_sequence(self, sequence: TSequence[TBase]) -> TG: return self return Temporal._factory(new_inner) - def merge(self, other: Union[type(None), Temporal[TBase], List[Temporal[TBase]]]) -> TG: + def merge(self, other: Union[type(None), Temporal[TBase], + List[Temporal[TBase]]]) -> TG: """ - Returns a new :class:`Temporal` object that is the result of merging `self` with `other`. + Returns a new :class:`Temporal` object that is the result of merging + `self` with `other`. MEOS Functions: temporal_merge, temporal_merge_array @@ -676,18 +701,21 @@ def merge(self, other: Union[type(None), Temporal[TBase], List[Temporal[TBase]]] if isinstance(other, Temporal): new_temp = temporal_merge(self._inner, other._inner) elif isinstance(other, list): - new_temp = temporal_merge_array([self._inner, *(o._inner for o in other)], len(other) + 1) + new_temp = temporal_merge_array([self._inner, + *(o._inner for o in other)], len(other) + 1) else: raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(new_temp) def insert(self, other: TG, connect: bool = True) -> TG: """ - Returns a new :class:`Temporal` object equal to `self` with `other` inserted. + Returns a new :class:`Temporal` object equal to `self` with `other` + inserted. Args: other: :class:`Temporal` object to insert in `self` - connect: wether to connect the inserted elements with the existing elements. + connect: wether to connect the inserted elements with the existing + elements. MEOS Functions: temporal_insert @@ -699,11 +727,13 @@ def insert(self, other: TG, connect: bool = True) -> TG: def update(self, other: TG, connect: bool = True) -> TG: """ - Returns a new :class:`Temporal` object equal to `self` updated with `other`. + Returns a new :class:`Temporal` object equal to `self` updated with + `other`. Args: other: :class:`Temporal` object to update `self` with - connect: wether to connect the updated elements with the existing elements. + connect: wether to connect the updated elements with the + existing elements. MEOS Functions: temporal_update @@ -715,23 +745,29 @@ def update(self, other: TG, connect: bool = True) -> TG: def delete(self, other: Time, connect: bool = True) -> TG: """ - Returns a new :class:`Temporal` object equal to `self` with elements at `other` removed. + Returns a new :class:`Temporal` object equal to `self` with elements at + `other` removed. Args: other: :class:`Time` object to remove from `self` - connect: wether to connect the potential gaps generated by the deletions. + connect: wether to connect the potential gaps generated by the + deletions. MEOS Functions: temporal_update """ if isinstance(other, datetime): - new_inner = temporal_delete_timestamp(self._inner, datetime_to_timestamptz(other), connect) + new_inner = temporal_delete_timestamp(self._inner, + datetime_to_timestamptz(other), connect) elif isinstance(other, TimestampSet): - new_inner = temporal_delete_timestampset(self._inner, other._inner, connect) + new_inner = temporal_delete_timestampset(self._inner, other._inner, + connect) elif isinstance(other, Period): - new_inner = temporal_delete_period(self._inner, other._inner, connect) + new_inner = temporal_delete_period(self._inner, other._inner, + connect) elif isinstance(other, PeriodSet): - new_inner = temporal_delete_periodset(self._inner, other._inner, connect) + new_inner = temporal_delete_periodset(self._inner, other._inner, + connect) else: raise TypeError(f'Operation not supported with type {other.__class__}') if new_inner == self._inner: @@ -741,7 +777,8 @@ def delete(self, other: Time, connect: bool = True) -> TG: # ------------------------- Restrictions ---------------------------------- def at(self, other: Time) -> TG: """ - Returns a new temporal object with the values of `self` restricted to the time `other`. + Returns a new temporal object with the values of `self` restricted to + the time `other`. Args: other: A time object to restrict the values of `self` to. @@ -750,10 +787,12 @@ def at(self, other: Time) -> TG: A new temporal object of the same subtype as `self`. MEOS Functions: - temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, temporal_at_periodset + temporal_at_timestamp, temporal_at_timestampset, + temporal_at_period, temporal_at_periodset """ if isinstance(other, datetime): - result = temporal_at_timestamp(self._inner, datetime_to_timestamptz(other)) + result = temporal_at_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): result = temporal_at_timestampset(self._inner, other._inner) elif isinstance(other, Period): @@ -766,7 +805,8 @@ def at(self, other: Time) -> TG: def at_min(self) -> TG: """ - Returns a new temporal object containing the times ``self`` is at its minimum value. + Returns a new temporal object containing the times ``self`` is at its + minimum value. Returns: A new temporal object of the same subtype as `self`. @@ -779,7 +819,8 @@ def at_min(self) -> TG: def at_max(self) -> TG: """ - Returns a new temporal object containing the times ``self`` is at its maximum value. + Returns a new temporal object containing the times ``self`` is at its + maximum value. Returns: A new temporal object of the same subtype as `self`. @@ -792,7 +833,8 @@ def at_max(self) -> TG: def minus(self, other: Time) -> TG: """ - Returns a new temporal object with the values of `self` removing those happening at `other`. + Returns a new temporal object with the values of `self` removing those + happening at `other`. Args: other: A time object to remove from `self`. @@ -801,14 +843,16 @@ def minus(self, other: Time) -> TG: A new temporal object of the same subtype as `self`. MEOS Functions: - temporal_minus_timestamp, temporal_minus_timestampset, temporal_minus_period, temporal_minus_periodset + temporal_minus_timestamp, temporal_minus_timestampset, + temporal_minus_period, temporal_minus_periodset """ if isinstance(other, Period): result = temporal_minus_period(self._inner, other._inner) elif isinstance(other, PeriodSet): result = temporal_minus_periodset(self._inner, other._inner) elif isinstance(other, datetime): - result = temporal_minus_timestamp(self._inner, datetime_to_timestamptz(other)) + result = temporal_minus_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): result = temporal_minus_timestampset(self._inner, other._inner) else: @@ -817,7 +861,8 @@ def minus(self, other: Time) -> TG: def minus_min(self) -> TG: """ - Returns a new temporal object containing the times ``self`` is not at its minimum value. + Returns a new temporal object containing the times ``self`` is not at + its minimum value. Returns: A new temporal object of the same subtype as `self`. @@ -830,7 +875,8 @@ def minus_min(self) -> TG: def minus_max(self) -> TG: """ - Returns a new temporal object containing the times ``self`` is not at its maximum value. + Returns a new temporal object containing the times ``self`` is not at + its maximum value. Returns: A new temporal object of the same subtype as `self`. @@ -844,9 +890,10 @@ def minus_max(self) -> TG: # ------------------------- Topological Operations ------------------------ def is_adjacent(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding box of `self` is adjacent to the bounding box of `other`. - Temporal subclasses may override this method to provide more specific behavior related to their types and - check adjacency over more dimensions. + Returns whether the bounding box of `self` is adjacent to the bounding + box of `other`. Temporal subclasses may override this method to provide + more specific behavior related to their types an check adjacency over + more dimensions. Args: other: A time or temporal object to compare to `self`. @@ -861,7 +908,8 @@ def is_adjacent(self, other: Union[Time, Temporal, Box]) -> bool: def is_temporally_adjacent(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding period of `self` is temporally adjacent to the bounding period of `other`. + Returns whether the bounding period of `self` is temporally adjacent + to the bounding period of `other`. Args: other: A time or temporal object to compare to `self`. @@ -876,8 +924,9 @@ def is_temporally_adjacent(self, other: Union[Time, Temporal, Box]) -> bool: def is_contained_in(self, container: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding period of `self` is contained in the bounding period of `container`. - Temporal subclasses may override this method to provide more specific behavior related to their types + Returns whether the bounding period of `self` is contained in the + bounding period of `container`. Temporal subclasses may override this + method to provide more specific behavior related to their types Args: container: A time or temporal object to compare to `self`. @@ -890,9 +939,11 @@ def is_contained_in(self, container: Union[Time, Temporal, Box]) -> bool: """ return self.bounding_box().is_contained_in(container) - def is_temporally_contained_in(self, container: Union[Time, Temporal, Box]) -> bool: + def is_temporally_contained_in(self, + container: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding period of `self` is contained in the bounding period of `container`. + Returns whether the bounding period of `self` is contained in the + bounding period of `container`. Args: container: A time or temporal object to compare to `self`. @@ -907,8 +958,9 @@ def is_temporally_contained_in(self, container: Union[Time, Temporal, Box]) -> b def contains(self, content: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding period of `self` contains the bounding period of `content`. - Temporal subclasses may override this method to provide more specific behavior related to their types + Returns whether the bounding period of `self` contains the bounding + period of `content`. Temporal subclasses may override this method to + provide more specific behavior related to their types Args: content: A time or temporal object to compare to `self`. @@ -923,7 +975,8 @@ def contains(self, content: Union[Time, Temporal, Box]) -> bool: def __contains__(self, item): """ - Returns whether the bounding period of `self` contains the bounding period of `content`. + Returns whether the bounding period of `self` contains the bounding + period of `content`. Args: item: A time or temporal object to compare to `self`. @@ -938,7 +991,8 @@ def __contains__(self, item): def temporally_contains(self, content: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding period of `self` contains the bounding period of `content`. + Returns whether the bounding period of `self` contains the bounding + period of `content`. Args: content: A time or temporal object to compare to `self`. @@ -953,8 +1007,9 @@ def temporally_contains(self, content: Union[Time, Temporal, Box]) -> bool: def overlaps(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding period of `self` overlaps the bounding period of `other`. - Temporal subclasses may override this method to provide more specific behavior related to their types + Returns whether the bounding period of `self` overlaps the bounding + period of `other`. Temporal subclasses may override this method to + provide more specific behavior related to their types Args: other: A time or temporal object to compare to `self`. @@ -969,7 +1024,8 @@ def overlaps(self, other: Union[Time, Temporal, Box]) -> bool: def temporally_overlaps(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding period of `self` overlaps the bounding period of `other`. + Returns whether the bounding period of `self` overlaps the bounding + period of `other`. Args: other: A time or temporal object to compare to `self`. @@ -984,8 +1040,9 @@ def temporally_overlaps(self, other: Union[Time, Temporal, Box]) -> bool: def is_same(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding period of `self` is the same as the bounding period of `other`. - Temporal subclasses may override this method to provide more specific behavior related to their types + Returns whether the bounding period of `self` is the same as the + bounding period of `other`. Temporal subclasses may override this + method to provide more specific behavior related to their types Args: other: A time or temporal object to compare to `self`. @@ -1016,7 +1073,8 @@ def is_before(self, other: Union[Time, Temporal, Box]) -> bool: def is_over_or_before(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether `self` is before `other` allowing overlap. That is, `self` doesn't extend after `other`. + Returns whether `self` is before `other` allowing overlap. That is, + `self` doesn't extend after `other`. Args: other: A time or temporal object to compare `self` to. @@ -1046,7 +1104,8 @@ def is_after(self, other: Union[Time, Temporal, Box]) -> bool: def is_over_or_after(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether `self` is after `other` allowing overlap. That is, `self` doesn't extend before `other`. + Returns whether `self` is after `other` allowing overlap. That is, + `self` doesn't extend before `other`. Args: other: A time or temporal object to compare `self` to. @@ -1106,15 +1165,18 @@ def hausdorff_distance(self, other: Temporal) -> float: return temporal_hausdorff_distance(self._inner, other._inner) # ------------------------- Split Operations ------------------------------ - def time_split(self, duration: Union[str, timedelta], start: Optional[Union[str, datetime]] = None) -> List[TG]: + def time_split(self, duration: Union[str, timedelta], + start: Optional[Union[str, datetime]] = None) -> List[TG]: """ - Returns a list of temporal objects of the same subtype as `self` with the same values as `self` but split in - temporal tiles of duration `duration` starting at `start`. + Returns a list of temporal objects of the same subtype as `self` with + the same values as `self` but split in temporal tiles of duration + `duration` starting at `start`. Args: - duration: A :class:`str` or :class:`timedelta` with the duration of the temporal tiles. - start: A :class:`str` or :class:`datetime` with the start time of the temporal tiles. If None, the start - time of `self` is used. + duration: A :class:`str` or :class:`timedelta` with the duration + of the temporal tiles. + start: A :class:`str` or :class:`datetime` with the start time of + the temporal tiles. If None, the start time of `self` is used. Returns: A list of temporal objects of the same subtype as `self`. @@ -1125,16 +1187,22 @@ def time_split(self, duration: Union[str, timedelta], start: Optional[Union[str, if start is None: st = temporal_start_timestamp(self._inner) else: - st = datetime_to_timestamptz(start) if isinstance(start, datetime) else pg_timestamptz_in(start, -1) - dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else pg_interval_in(duration, -1) + st = datetime_to_timestamptz(start) \ + if isinstance(start, datetime) \ + else pg_timestamptz_in(start, -1) + dt = timedelta_to_interval(duration) \ + if isinstance(duration, timedelta) \ + else pg_interval_in(duration, -1) tiles, new_count = temporal_time_split(self._inner, dt, st) from ..factory import _TemporalFactory - return [_TemporalFactory.create_temporal(tiles[i]) for i in range(new_count)] + return [_TemporalFactory.create_temporal(tiles[i]) \ + for i in range(new_count)] def time_split_n(self, n: int) -> List[TG]: """ - Returns a list of temporal objects of the same subtype as `self` with the same values as `self` but split in - n temporal tiles of equal duration. + Returns a list of temporal objects of the same subtype as `self` with + the same values as `self` but split in n temporal tiles of equal + duration. Args: n: An :class:`int` with the number of temporal tiles. @@ -1151,17 +1219,19 @@ def time_split_n(self, n: int) -> List[TG]: dt = timedelta_to_interval((self.end_timestamp() - self.start_timestamp()) / n) tiles, new_count = temporal_time_split(self._inner, dt, st) from ..factory import _TemporalFactory - return [_TemporalFactory.create_temporal(tiles[i]) for i in range(new_count)] + return [_TemporalFactory.create_temporal(tiles[i]) \ + for i in range(new_count)] def stops(self, max_distance: Optional[float] = 0.0, min_duration: Optional[timedelta] = timedelta()) -> TSS: """ - Return the subsequences where the objects stay within an area with a given maximum size for at least - the specified duration. + Return the subsequences where the objects stay within an area with a + given maximum size for at least the specified duration. Args: max_distance: A :class:`float` with the maximum distance of a stop. - min_duration: A :class:`timedelta` with the minimum duration of a stop. + min_duration: A :class:`timedelta` with the minimum duration of + a stop. Returns: A :class:`SequenceSet` of the same subtype as `self` with the stops. @@ -1169,7 +1239,8 @@ def stops(self, max_distance: Optional[float] = 0.0, MEOS Functions: temporal_stops """ - new_inner = temporal_stops(self._inner, max_distance, timedelta_to_interval(min_duration)) + new_inner = temporal_stops(self._inner, max_distance, + timedelta_to_interval(min_duration)) from ..factory import _TemporalFactory return _TemporalFactory.create_temporal(new_inner) @@ -1247,7 +1318,8 @@ def temporal_less_or_equal(self, other: Temporal) -> TBool: other: A temporal object to compare to `self`. Returns: - A :class:`TBool` with the result of the temporal less or equal relation. + A :class:`TBool` with the result of the temporal less or equal + relation. MEOS Functions: tle_temporal_temporal @@ -1258,13 +1330,15 @@ def temporal_less_or_equal(self, other: Temporal) -> TBool: def temporal_greater_or_equal(self, other: Temporal) -> TBool: """ - Returns the temporal greater or equal relation between `self` and `other`. + Returns the temporal greater or equal relation between `self` and + `other`. Args: other: A temporal object to compare to `self`. Returns: - A :class:`TBool` with the result of the temporal greater or equal relation. + A :class:`TBool` with the result of the temporal greater or equal + relation. MEOS Functions: tge_temporal_temporal @@ -1281,7 +1355,8 @@ def temporal_greater(self, other: Temporal) -> TBool: other: A temporal object to compare to `self`. Returns: - A :class:`TBool` with the result of the temporal greater than relation. + A :class:`TBool` with the result of the temporal greater than + relation. MEOS Functions: tgt_temporal_temporal @@ -1374,7 +1449,8 @@ def __ge__(self, other): other: A temporal object to compare to `self`. Returns: - A :class:`bool` with the result of the greater or equal than relation. + A :class:`bool` with the result of the greater or equal than + relation. MEOS Functions: temporal_ge diff --git a/pymeos/pymeos/temporal/tinstant.py b/pymeos/pymeos/temporal/tinstant.py index 78a5b265..f9e98c0f 100644 --- a/pymeos/pymeos/temporal/tinstant.py +++ b/pymeos/pymeos/temporal/tinstant.py @@ -18,16 +18,20 @@ class TInstant(Temporal[TBase, TG, TI, TS, TSS], ABC): """ - Base class for temporal instant types, i.e. temporal values that are defined at a single point in time. + Base class for temporal instant types, i.e. temporal values that are + defined at a single point in time. """ __slots__ = ['_inner'] _make_function = None _cast_function = None - def __init__(self, string: Optional[str] = None, *, value: Optional[Union[str, TBase]] = None, - timestamp: Optional[Union[str, datetime]] = None, _inner=None): - assert (_inner is not None) or ((string is not None) != (value is not None and timestamp is not None)), \ + def __init__(self, string: Optional[str] = None, *, + value: Optional[Union[str, TBase]] = None, + timestamp: Optional[Union[str, datetime]] = None, + _inner=None): + assert (_inner is not None) or ((string is not None) != \ + (value is not None and timestamp is not None)), \ "Either string must be not None or both point and timestamp must be not" if _inner is not None: self._inner = as_tinstant(_inner) diff --git a/pymeos/pymeos/temporal/tsequence.py b/pymeos/pymeos/temporal/tsequence.py index fd2932be..d5275215 100644 --- a/pymeos/pymeos/temporal/tsequence.py +++ b/pymeos/pymeos/temporal/tsequence.py @@ -18,39 +18,49 @@ class TSequence(Temporal[TBase, TG, TI, TS, TSS], ABC): """ - Base class for temporal sequence types, i.e. temporal values that are defined over a continuous period of time. + Base class for temporal sequence types, i.e. temporal values that are + defined over a continuous period of time. """ def _expandable(self) -> bool: return self._expandable_sequence # ------------------------- Constructors ---------------------------------- - def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, Any]]] = None, - lower_inc: bool = True, upper_inc: bool = False, expandable: Union[bool, int] = False, - interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True, _inner=None): - assert (_inner is not None) or ((string is not None) != (instant_list is not None)), \ + def __init__(self, string: Optional[str] = None, *, + instant_list: Optional[List[Union[str, Any]]] = None, + lower_inc: bool = True, upper_inc: bool = False, + expandable: Union[bool, int] = False, + interpolation: TInterpolation = TInterpolation.LINEAR, + normalize: bool = True, _inner=None): + assert (_inner is not None) or ((string is not None) != \ + (instant_list is not None)), \ "Either string must be not None or instant_list must be not" if _inner is not None: self._inner = as_tsequence(_inner) elif string is not None: self._inner = as_tsequence(self.__class__._parse_function(string)) else: - self._instants = [x._inner if isinstance(x, self.ComponentClass) else self.__class__._parse_function(x) for - x in instant_list] + self._instants = [x._inner if isinstance(x, self.ComponentClass) \ + else self.__class__._parse_function(x) for x in instant_list] count = len(self._instants) if not expandable: - self._inner = tsequence_make(self._instants, count, lower_inc, upper_inc, - interpolation.value, normalize) + self._inner = tsequence_make(self._instants, count, lower_inc, + upper_inc, interpolation.value, normalize) else: - max_size = max(expandable, count) if isinstance(expandable, int) else 2 * count - self._inner = tsequence_make_exp(self._instants, count, max_size, lower_inc, upper_inc, - interpolation.value, normalize) - self._expandable_sequence = bool(expandable) or self._inner.maxcount > self._inner.count + max_size = max(expandable, count) \ + if isinstance(expandable, int) else 2 * count + self._inner = tsequence_make_exp(self._instants, count, + max_size, lower_inc, upper_inc, interpolation.value, + normalize) + self._expandable_sequence = bool(expandable) or \ + self._inner.maxcount > self._inner.count @classmethod - def from_instants(cls: Type[Self], instant_list: Optional[List[Union[str, Any]]], + def from_instants(cls: Type[Self], + instant_list: Optional[List[Union[str, Any]]], lower_inc: bool = True, upper_inc: bool = False, - interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True) -> Self: + interpolation: TInterpolation = TInterpolation.LINEAR, + normalize: bool = True) -> Self: """ Create a temporal sequence from a list of instants. @@ -64,8 +74,9 @@ def from_instants(cls: Type[Self], instant_list: Optional[List[Union[str, Any]]] Returns: A new temporal sequence. """ - return cls(instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, interpolation=interpolation, - normalize=normalize) + return cls(instant_list=instant_list, lower_inc=lower_inc, + upper_inc=upper_inc, interpolation=interpolation, + normalize=normalize) # ------------------------- Accessors ------------------------------------- def lower_inc(self) -> bool: diff --git a/pymeos/pymeos/temporal/tsequenceset.py b/pymeos/pymeos/temporal/tsequenceset.py index 96e46719..c7d9ba7b 100644 --- a/pymeos/pymeos/temporal/tsequenceset.py +++ b/pymeos/pymeos/temporal/tsequenceset.py @@ -18,7 +18,8 @@ class TSequenceSet(Temporal[TBase, TG, TI, TS, TSS], ABC): """ - Base class for temporal sequence set types, i.e. temporal values that are defined by a set of temporal sequences. + Base class for temporal sequence set types, i.e. temporal values that are + defined by a set of temporal sequences. """ def _expandable(self) -> bool: @@ -29,27 +30,31 @@ def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, Any]]] = None, expandable: Union[bool, int] = False, normalize: bool = True, _inner=None): - assert (_inner is not None) or ((string is not None) != (sequence_list is not None)), \ + assert (_inner is not None) or ((string is not None) != \ + (sequence_list is not None)), \ "Either string must be not None or sequence_list must be not" if _inner is not None: self._inner = as_tsequenceset(_inner) elif string is not None: self._inner = as_tsequenceset(self.__class__._parse_function(string)) else: - sequences = [x._inner if isinstance(x, self.ComponentClass) else self.__class__._parse_function(x) - for x in sequence_list] + sequences = [x._inner if isinstance(x, self.ComponentClass) \ + else self.__class__._parse_function(x) for x in sequence_list] count = len(sequences) self._inner = tsequenceset_make(sequences, count, normalize) if not expandable: self._inner = tsequenceset_make(sequences, count, normalize) else: - max_size = max(expandable, count) if isinstance(expandable, int) else 2 * count + max_size = max(expandable, count) \ + if isinstance(expandable, int) else 2 * count self._inner = tsequenceset_make_exp(sequences, count, max_size, normalize) - self._expandable_sequenceset = bool(expandable) or self._inner.maxcount > self._inner.count + self._expandable_sequenceset = bool(expandable) or \ + self._inner.maxcount > self._inner.count @classmethod - def from_sequences(cls: Type[Self], sequence_list: Optional[List[Union[str, Any]]] = None, + def from_sequences(cls: Type[Self], + sequence_list: Optional[List[Union[str, Any]]] = None, normalize: bool = True) -> Self: """ Create a temporal sequence set from a list of sequences. @@ -102,7 +107,8 @@ def to_dataframe(self) -> DataFrame: """ sequences = self.sequences() data = { - 'sequence': [i for i, seq in enumerate(sequences) for _ in range(seq.num_instants())], + 'sequence': [i for i, seq in enumerate(sequences) \ + for _ in range(seq.num_instants())], 'time': [t for seq in sequences for t in seq.timestamps()], 'value': [v for seq in sequences for v in seq.values()] } diff --git a/pymeos/pymeos/time/period.py b/pymeos/pymeos/time/period.py index 9f6e4ba0..a7f1c382 100644 --- a/pymeos/pymeos/time/period.py +++ b/pymeos/pymeos/time/period.py @@ -24,9 +24,10 @@ class Period: >>> Period('(2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01)') - Another possibility is to provide the ``lower`` and ``upper`` named parameters (of type str or datetime), and - optionally indicate whether the bounds are inclusive or exclusive (by default, the lower bound is inclusive and the - upper is exclusive): + Another possibility is to provide the ``lower`` and ``upper`` named + parameters (of type str or datetime), and optionally indicate whether the + bounds are inclusive or exclusive (by default, the lower bound is inclusive + and the upper is exclusive): >>> Period(lower='2019-09-08 00:00:00+01', upper='2019-09-10 00:00:00+01') >>> Period(lower='2019-09-08 00:00:00+01', upper='2019-09-10 00:00:00+01', lower_inc=False, upper_inc=True) @@ -43,15 +44,18 @@ def __init__(self, string: Optional[str] = None, *, upper_inc: bool = False, _inner=None): super().__init__() - assert (_inner is not None) or ((string is not None) != (lower is not None and upper is not None)), \ + assert (_inner is not None) or ((string is not None) != + (lower is not None and upper is not None)), \ "Either string must be not None or both lower and upper must be not" if _inner is not None: self._inner = _inner elif string is not None: self._inner = period_in(string) else: - lower_ts = pg_timestamptz_in(lower, -1) if isinstance(lower, str) else datetime_to_timestamptz(lower) - upper_ts = pg_timestamptz_in(upper, -1) if isinstance(upper, str) else datetime_to_timestamptz(upper) + lower_ts = pg_timestamptz_in(lower, -1) \ + if isinstance(lower, str) else datetime_to_timestamptz(lower) + upper_ts = pg_timestamptz_in(upper, -1) \ + if isinstance(upper, str) else datetime_to_timestamptz(upper) self._inner = period_make(lower_ts, upper_ts, lower_inc, upper_inc) def __copy__(self): @@ -144,7 +148,8 @@ def as_hexwkb(self) -> str: Returns the WKB representation of ``self`` in hex-encoded ASCII. Returns: - A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. + A :class:`str` object with the WKB representation of ``self`` in + hex-encoded ASCII. MEOS Functions: span_as_hexwkb @@ -195,7 +200,8 @@ def lower_inc(self) -> bool: Returns whether the lower bound of the period is inclusive or not Returns: - True if the lower bound of the period is inclusive and False otherwise + `True` if the lower bound of the period is inclusive and `False` + otherwise MEOS Functions: span_lower_inc @@ -207,7 +213,8 @@ def upper_inc(self) -> bool: Returns whether the upper bound of the period is inclusive or not Returns: - True if the upper bound of the period is inclusive and False otherwise + `True` if the upper bound of the period is inclusive and `False` + otherwise MEOS Functions: span_upper_inc @@ -219,7 +226,8 @@ def duration(self) -> timedelta: Returns the duration of the period. Returns: - A :class:`datetime.timedelta` instance representing the duration of the period + A :class:`datetime.timedelta` instance representing the duration of + the period MEOS Functions: period_duration @@ -228,7 +236,7 @@ def duration(self) -> timedelta: def duration_in_seconds(self) -> float: """ - Returns the duration of the period. + Returns the duration of the period in seconds. Returns: Returns a `float` representing the duration of the period in seconds @@ -272,14 +280,16 @@ def shift(self, delta: timedelta) -> Period: def tscale(self, duration: timedelta) -> Period: """ - Returns a new period that starts as ``self`` but has duration ``duration`` + Returns a new period that starts as ``self`` but has duration + ``duration`` Examples: >>> Period('[2000-01-01, 2000-01-10]').tscale(timedelta(days=2)) >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' Args: - duration: :class:`datetime.timedelta` instance representing the duration of the new period + duration: :class:`datetime.timedelta` instance representing the + duration of the new period Returns: A new :class:`Period` instance @@ -289,9 +299,11 @@ def tscale(self, duration: timedelta) -> Period: """ return self.shift_tscale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> Period: + def shift_tscale(self, shift: Optional[timedelta] = None, + duration: Optional[timedelta] = None) -> Period: """ - Returns a new period that starts at ``self`` shifted by ``shift`` and has duration ``duration`` + Returns a new period that starts at ``self`` shifted by ``shift`` and + has duration ``duration`` Examples: >>> Period('[2000-01-01, 2000-01-10]').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) @@ -299,7 +311,8 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim Args: shift: :class:`datetime.timedelta` instance to shift - duration: :class:`datetime.timedelta` instance representing the duration of the new period + duration: :class:`datetime.timedelta` instance representing the + duration of the new period Returns: A new :class:`Period` instance @@ -307,7 +320,8 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim MEOS Functions: period_shift_tscale """ - assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' + assert shift is not None or duration is not None, \ + 'shift and scale deltas must not be both None' modified = period_shift_tscale( self._inner, timedelta_to_interval(shift) if shift else None, @@ -315,32 +329,11 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim ) return Period(_inner=modified) - # def expand(self, other: Period) -> Period: - # """ - # Returns a new period that includes both ``self`` and ``other`` - - # Examples: - # >>> Period('[2000-01-01, 2000-01-04)').expand(Period('[2000-01-05, 2000-01-10]')) - # >>> 'Period([2000-01-01 00:00:00+01, 2000-01-10 00:00:00+01])' - - # Args: - # other: :class:`Period` instance to expand the period - - # Returns: - # A new :class:`Period` instance - - # MEOS Functions: - # span_expand - # """ - # copy = span_copy(self._inner) - # span_expand(other._inner, copy) - # return Period(_inner=copy) - # ------------------------- Topological Operations ------------------------ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them - contains it. + Returns whether ``self`` is temporally adjacent to ``other``. That is, + they share a bound but only one of them contains it. Examples: >>> Period('[2012-01-01, 2012-01-02)').is_adjacent(Period('[2012-01-02, 2012-01-03]')) @@ -357,8 +350,9 @@ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: True if adjacent, False otherwise MEOS Functions: - adjacent_span_span, adjacent_span_spanset, adjacent_period_timestamp, - adjacent_period_timestampset, adjacent_period_temporal + adjacent_span_span, adjacent_span_spanset, + adjacent_period_timestamp, adjacent_period_timestampset, + adjacent_period_temporal """ from .periodset import PeriodSet from .timestampset import TimestampSet @@ -379,7 +373,8 @@ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: else: raise TypeError(f'Operation not supported with type {other.__class__}') - def is_contained_in(self, container: Union[Period, PeriodSet, Box, Temporal]) -> bool: + def is_contained_in(self, container: Union[Period, PeriodSet, Box, + Temporal]) -> bool: """ Returns whether ``self`` is temporally contained in ``container``. @@ -408,9 +403,11 @@ def is_contained_in(self, container: Union[Period, PeriodSet, Box, Temporal]) -> elif isinstance(container, PeriodSet): return contained_span_spanset(self._inner, container._inner) elif isinstance(container, Temporal): - return contained_span_span(self._inner, temporal_to_period(container._inner)) + return contained_span_span(self._inner, + temporal_to_period(container._inner)) elif isinstance(container, Box): - return contained_span_span(self._inner, container.to_period()._inner) + return contained_span_span(self._inner, + container.to_period()._inner) else: raise TypeError(f'Operation not supported with type {container.__class__}') @@ -445,11 +442,13 @@ def contains(self, content: Union[Time, Box, Temporal]) -> bool: elif isinstance(content, PeriodSet): return contains_span_spanset(self._inner, content._inner) elif isinstance(content, datetime): - return contains_period_timestamp(self._inner, datetime_to_timestamptz(content)) + return contains_period_timestamp(self._inner, + datetime_to_timestamptz(content)) elif isinstance(content, TimestampSet): return contains_span_span(self._inner, set_span(content._inner)) elif isinstance(content, Temporal): - return contains_span_span(self._inner, temporal_to_period(content._inner)) + return contains_span_span(self._inner, + temporal_to_period(content._inner)) elif isinstance(content, get_args(Box)): return contains_span_span(self._inner, content.to_period()._inner) else: @@ -474,14 +473,16 @@ def __contains__(self, item): True if contains, False otherwise MEOS Functions: - contains_span_span, contains_span_spanset, contains_period_timestamp, - contains_period_timestampset, contains_period_temporal + contains_span_span, contains_span_spanset, + contains_period_timestamp, contains_period_timestampset, + contains_period_temporal """ return self.contains(item) def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant + Returns whether ``self`` temporally overlaps ``other``. That is, both + share at least an instant Examples: >>> Period('[2012-01-01, 2012-01-02]').overlaps(Period('[2012-01-02, 2012-01-03]')) @@ -498,8 +499,8 @@ def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: True if overlaps, False otherwise MEOS Functions: - overlaps_span_span, overlaps_span_spanset, overlaps_period_timestampset, - overlaps_period_temporal + overlaps_span_span, overlaps_span_spanset, + overlaps_period_timestampset, overlaps_period_temporal """ from .periodset import PeriodSet from .timestampset import TimestampSet @@ -510,11 +511,13 @@ def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, PeriodSet): return overlaps_spanset_span(other._inner, self._inner) elif isinstance(other, datetime): - return overlaps_span_span(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) + return overlaps_span_span(self._inner, + timestamp_to_period(datetime_to_timestamptz(other))) elif isinstance(other, TimestampSet): return overlaps_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return overlaps_span_span(self._inner, temporal_to_period(other._inner)) + return overlaps_span_span(self._inner, + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): return overlaps_span_span(self._inner, other.to_period()._inner) else: @@ -546,7 +549,8 @@ def is_same(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, PeriodSet): return span_eq(self._inner, spanset_span(other._inner)) elif isinstance(other, datetime): - return span_eq(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) + return span_eq(self._inner, + timestamp_to_period(datetime_to_timestamptz(other))) elif isinstance(other, TimestampSet): return span_eq(self._inner, set_span(other._inner)) else: @@ -555,7 +559,8 @@ def is_same(self, other: Union[Time, Box, Temporal]) -> bool: # ------------------------- Position Operations --------------------------- def is_before(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + Returns whether ``self`` is strictly before ``other``. That is, + ``self`` ends before ``other`` starts. Examples: >>> Period('[2012-01-01, 2012-01-02)').is_before(Period('[2012-01-02, 2012-01-03]')) @@ -584,7 +589,8 @@ def is_before(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, PeriodSet): return left_span_spanset(self._inner, other._inner) elif isinstance(other, datetime): - return overafter_timestamp_period(datetime_to_timestamptz(other), self._inner) + return overafter_timestamp_period(datetime_to_timestamptz(other), + self._inner) if isinstance(other, TimestampSet): return left_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): @@ -596,8 +602,8 @@ def is_before(self, other: Union[Time, Box, Temporal]) -> bool: def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). + Returns whether ``self`` is before ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same time). Examples: >>> Period('[2012-01-01, 2012-01-02)').is_over_or_before(Period('[2012-01-02, 2012-01-03]')) @@ -614,8 +620,9 @@ def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: True if before, False otherwise MEOS Functions: - overleft_span_span, overleft_span_spanset, overbefore_period_timestamp, - overbefore_period_timestampset, overbefore_period_temporal + overleft_span_span, overleft_span_spanset, + overbefore_period_timestamp, overbefore_period_timestampset, + overbefore_period_temporal """ from .periodset import PeriodSet from .timestampset import TimestampSet @@ -626,11 +633,13 @@ def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, PeriodSet): return overleft_span_spanset(self._inner, other._inner) elif isinstance(other, datetime): - return overbefore_period_timestamp(self._inner, datetime_to_timestamptz(other)) + return overbefore_period_timestamp(self._inner, + datetime_to_timestamptz(other)) if isinstance(other, TimestampSet): return overleft_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return overleft_span_span(self._inner, temporal_to_period(other._inner)) + return overleft_span_span(self._inner, + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): return overleft_span_span(self._inner, other.to_period()._inner) else: @@ -638,7 +647,8 @@ def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: def is_after(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. + Returns whether ``self`` is strictly after ``other``. That is, ``self`` + starts after ``other`` ends. Examples: >>> Period('[2012-01-02, 2012-01-03]').is_after(Period('[2012-01-01, 2012-01-02)')) @@ -667,11 +677,13 @@ def is_after(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, PeriodSet): return right_span_spanset(self._inner, other._inner) elif isinstance(other, datetime): - return overbefore_timestamp_period(datetime_to_timestamptz(other), self._inner) + return overbefore_timestamp_period(datetime_to_timestamptz(other), + self._inner) if isinstance(other, TimestampSet): return right_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return right_span_span(self._inner, temporal_to_period(other._inner)) + return right_span_span(self._inner, + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): return right_span_span(self._inner, other.to_period()._inner) else: @@ -679,8 +691,8 @@ def is_after(self, other: Union[Time, Box, Temporal]) -> bool: def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is after ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same time). Examples: >>> Period('[2012-01-02, 2012-01-03]').is_over_or_after(Period('[2012-01-01, 2012-01-02)')) @@ -697,8 +709,9 @@ def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: True if overlapping or after, False otherwise MEOS Functions: - overright_span_span, overright_span_spanset, overafter_period_timestamp, - overafter_period_timestampset, overafter_period_temporal + overright_span_span, overright_span_spanset, + overafter_period_timestamp, overafter_period_timestampset, + overafter_period_temporal """ from .periodset import PeriodSet from .timestampset import TimestampSet @@ -709,11 +722,13 @@ def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, PeriodSet): return overright_span_spanset(self._inner, other._inner) elif isinstance(other, datetime): - return overafter_period_timestamp(self._inner, datetime_to_timestamptz(other)) + return overafter_period_timestamp(self._inner, + datetime_to_timestamptz(other)) if isinstance(other, TimestampSet): return overright_span_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return overright_span_span(self._inner, temporal_to_period(other._inner)) + return overright_span_span(self._inner, + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): return overright_span_span(self._inner, other.to_period()._inner) else: @@ -731,24 +746,31 @@ def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: A :class:`datetime.timedelta` instance MEOS Functions: - distance_span_span, distance_spanset_span, distance_period_timestamp + distance_span_span, distance_spanset_span, + distance_period_timestamp """ from .periodset import PeriodSet from .timestampset import TimestampSet from ..temporal import Temporal from ..boxes import Box if isinstance(other, Temporal): - return timedelta(seconds=distance_span_span(self._inner, temporal_to_period(other._inner))) + return timedelta(seconds=distance_span_span(self._inner, + temporal_to_period(other._inner))) elif isinstance(other, Period): - return timedelta(seconds=distance_span_span(self._inner, other._inner)) + return timedelta(seconds=distance_span_span(self._inner, + other._inner)) elif isinstance(other, PeriodSet): - return timedelta(seconds=distance_spanset_span(other._inner, self._inner)) + return timedelta(seconds=distance_spanset_span(other._inner, + self._inner)) elif isinstance(other, datetime): - return timedelta(seconds=distance_period_timestamp(self._inner, datetime_to_timestamptz(other))) + return timedelta(seconds=distance_period_timestamp(self._inner, + datetime_to_timestamptz(other))) elif isinstance(other, TimestampSet): - return timedelta(seconds=distance_span_span(self._inner, set_span(other._inner))) + return timedelta(seconds=distance_span_span(self._inner, + set_span(other._inner))) elif isinstance(other, get_args(Box)): - return timedelta(seconds=distance_span_span(self._inner, other.to_period()._inner)) + return timedelta(seconds=distance_span_span(self._inner, + other.to_period()._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -762,7 +784,8 @@ def intersection(self, other: Period) -> Optional[Period]: ... @overload - def intersection(self, other: Union[TimestampSet, PeriodSet]) -> Optional[PeriodSet]: + def intersection(self, other: Union[TimestampSet, PeriodSet]) -> \ + Optional[PeriodSet]: ... def intersection(self, other: Time) -> Optional[Time]: @@ -776,15 +799,19 @@ def intersection(self, other: Time) -> Optional[Time]: A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_span_span, intersection_spanset_span, intersection_period_timestamp + intersection_span_span, intersection_spanset_span, + intersection_period_timestamp """ from .periodset import PeriodSet from .timestampset import TimestampSet if isinstance(other, datetime): - result = intersection_period_timestamp(self._inner, datetime_to_timestamptz(other)) - return timestamptz_to_datetime(result) if result is not None else None + result = intersection_period_timestamp(self._inner, + datetime_to_timestamptz(other)) + return timestamptz_to_datetime(result) \ + if result is not None else None elif isinstance(other, TimestampSet): - result = intersection_spanset_span(set_to_spanset(other._inner), self._inner) + result = intersection_spanset_span(set_to_spanset(other._inner), + self._inner) return TimestampSet(_inner=result) if result is not None else None elif isinstance(other, Period): result = intersection_span_span(self._inner, other._inner) @@ -806,7 +833,8 @@ def __mul__(self, other): A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_span_span, intersection_spanset_span, intersection_period_timestamp + intersection_span_span, intersection_spanset_span, + intersection_period_timestamp """ return self.intersection(other) @@ -826,10 +854,12 @@ def minus(self, other: Time) -> PeriodSet: from .periodset import PeriodSet from .timestampset import TimestampSet if isinstance(other, datetime): - result = minus_period_timestamp(self._inner, datetime_to_timestamptz(other)) + result = minus_period_timestamp(self._inner, + datetime_to_timestamptz(other)) return PeriodSet(_inner=result) if result is not None else None elif isinstance(other, TimestampSet): - result = minus_span_spanset(self._inner, set_to_spanset(other._inner)) + result = minus_span_spanset(self._inner, + set_to_spanset(other._inner)) return PeriodSet(_inner=result) if result is not None else None elif isinstance(other, Period): result = minus_span_span(self._inner, other._inner) @@ -871,13 +901,16 @@ def union(self, other: Time) -> PeriodSet: from .periodset import PeriodSet from .timestampset import TimestampSet if isinstance(other, datetime): - return PeriodSet(_inner=union_period_timestamp(self._inner, datetime_to_timestamptz(other))) + return PeriodSet(_inner=union_period_timestamp(self._inner, + datetime_to_timestamptz(other))) elif isinstance(other, TimestampSet): - return PeriodSet(_inner=union_spanset_span(set_to_spanset(other._inner), self._inner)) + return PeriodSet(_inner=union_spanset_span(set_to_spanset(other._inner), + self._inner)) if isinstance(other, Period): return PeriodSet(_inner=union_span_span(self._inner, other._inner)) elif isinstance(other, PeriodSet): - return PeriodSet(_inner=union_spanset_span(other._inner, self._inner)) + return PeriodSet(_inner=union_spanset_span(other._inner, + self._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -1008,7 +1041,8 @@ def plot(self, *args, **kwargs): @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`Period` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`Period` from a database cursor. Used when automatically + loading objects from the database. Users should use the class constructor instead. """ if not value: diff --git a/pymeos/pymeos/time/periodset.py b/pymeos/pymeos/time/periodset.py index 3101d6ff..7b20bf4b 100644 --- a/pymeos/pymeos/time/periodset.py +++ b/pymeos/pymeos/time/periodset.py @@ -35,17 +35,21 @@ class PeriodSet: __slots__ = ['_inner'] # ------------------------- Constructors ---------------------------------- - def __init__(self, string: Optional[str] = None, *, period_list: Optional[List[Union[str, Period]]] = None, + def __init__(self, string: Optional[str] = None, *, + period_list: Optional[List[Union[str, Period]]] = None, normalize: bool = True, _inner=None): super().__init__() - assert (_inner is not None) or ((string is not None) != (period_list is not None)), \ + assert (_inner is not None) or ((string is not None) != + (period_list is not None)), \ "Either string must be not None or period_list must be not" if _inner is not None: self._inner = _inner elif string is not None: self._inner = periodset_in(string) else: - periods = [period_in(period)[0] if isinstance(period, str) else period._inner[0] for period in period_list] + periods = [period_in(period)[0] + if isinstance(period, str) else period._inner[0] + for period in period_list] self._inner = spanset_make(periods, normalize) def __copy__(self): @@ -136,7 +140,8 @@ def as_hexwkb(self) -> str: """ Returns the WKB representation of ``self`` in hex-encoded ASCII. Returns: - A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. + A :class:`str` object with the WKB representation of ``self`` in + hex-encoded ASCII. MEOS Functions: spanset_as_hexwkb @@ -161,22 +166,25 @@ def to_period(self) -> Period: def duration(self, ignore_gaps: Optional[bool] = False) -> timedelta: """ Returns the duration of the periodset. By default, i.e., when the - second argument is False, the function takes into account the gaps within, - i.e., returns the sum of the durations of the periods within. + second argument is False, the function takes into account the gaps + within, i.e., returns the sum of the durations of the periods within. Otherwise, the function returns the duration of the periodset ignoring any gap, i.e., the duration from the lower bound of the first period to the upper bound of the last period. Parameters: - ignore_gaps: Whether to take into account potential time gaps in the periodset. + ignore_gaps: Whether to take into account potential time gaps in + the periodset. Returns: - A :class:`datetime.timedelta` instance representing the duration of the periodset + A :class:`datetime.timedelta` instance representing the duration of + the periodset MEOS Functions: periodset_duration """ - return interval_to_timedelta(periodset_duration(self._inner, ignore_gaps)) + return interval_to_timedelta(periodset_duration(self._inner, + ignore_gaps)) def num_timestamps(self) -> int: """ @@ -255,7 +263,7 @@ def start_period(self) -> Period: A :class:`Period` instance MEOS Functions: - periodset_lower + spanset_start_span """ from .period import Period return Period(_inner=spanset_start_span(self._inner)) @@ -267,7 +275,7 @@ def end_period(self) -> Period: A :class:`Period` instance MEOS Functions: - periodset_upper + spanset_end_span """ from .period import Period return Period(_inner=spanset_end_span(self._inner)) @@ -316,7 +324,8 @@ def __hash__(self) -> int: # ------------------------- Transformations ------------------------------- def shift(self, delta: timedelta) -> PeriodSet: """ - Returns a new periodset that is the result of shifting ``self`` by ``delta`` + Returns a new periodset that is the result of shifting ``self`` by + ``delta`` Examples: >>> Period('[2000-01-01, 2000-01-10]').shift(timedelta(days=2)) @@ -342,7 +351,8 @@ def tscale(self, duration: timedelta) -> PeriodSet: >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' Args: - duration: :class:`datetime.timedelta` instance representing the duration of the new period + duration: :class:`datetime.timedelta` instance representing the + duration of the new period Returns: A new :class:`PeriodSet` instance @@ -352,9 +362,11 @@ def tscale(self, duration: timedelta) -> PeriodSet: """ return self.shift_tscale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> PeriodSet: + def shift_tscale(self, shift: Optional[timedelta] = None, + duration: Optional[timedelta] = None) -> PeriodSet: """ - Returns a new periodset that starts at ``self`` shifted by ``shift`` and has duration ``duration`` + Returns a new periodset that starts at ``self`` shifted by ``shift`` + and has duration ``duration`` Examples: >>> Period('[2000-01-01, 2000-01-10]').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) @@ -362,7 +374,8 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim Args: shift: :class:`datetime.timedelta` instance to shift - duration: :class:`datetime.timedelta` instance representing the duration of the new period + duration: :class:`datetime.timedelta` instance representing the + duration of the new period Returns: A new :class:`PeriodSet` instance @@ -370,7 +383,8 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim MEOS Functions: periodset_shift_tscale """ - assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' + assert shift is not None or duration is not None, \ + 'shift and scale deltas must not be both None' ps = periodset_shift_tscale( self._inner, timedelta_to_interval(shift) if shift else None, @@ -381,8 +395,8 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim # ------------------------- Topological Operations ------------------------ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them - contains it. + Returns whether ``self`` is temporally adjacent to ``other``. That is, + they share a bound but only one of them contains it. Examples: >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_adjacent(PeriodSet('{[2012-01-02, 2012-01-03]}')) @@ -399,8 +413,8 @@ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: True if adjacent, False otherwise MEOS Functions: - adjacent_spanset_span, adjacent_spanset_spanset, adjacent_periodset_timestamp, - adjacent_periodset_timestampset + adjacent_spanset_span, adjacent_spanset_spanset, + adjacent_periodset_timestamp, adjacent_periodset_timestampset """ from .period import Period from .timestampset import TimestampSet @@ -411,17 +425,21 @@ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: if isinstance(other, PeriodSet): return adjacent_spanset_spanset(self._inner, other._inner) elif isinstance(other, datetime): - return adjacent_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + return adjacent_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): - return adjacent_spanset_spanset(self._inner, set_to_spanset(other._inner)) + return adjacent_spanset_spanset(self._inner, + set_to_spanset(other._inner)) elif isinstance(other, Temporal): - return adjacent_spanset_spanset(self._inner, temporal_time(other._inner)) + return adjacent_spanset_spanset(self._inner, + temporal_time(other._inner)) elif isinstance(other, get_args(Box)): return adjacent_spanset_span(self._inner, other.to_period()._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') - def is_contained_in(self, container: Union[Period, PeriodSet, Box, Temporal]) -> bool: + def is_contained_in(self, + container: Union[Period, PeriodSet, Box, Temporal]) -> bool: """ Returns whether ``self`` is temporally contained in ``container``. @@ -440,7 +458,8 @@ def is_contained_in(self, container: Union[Period, PeriodSet, Box, Temporal]) -> True if contained, False otherwise MEOS Functions: - contained_spanset_span, contained_spanset_spanset, contained_periodset_temporal + contained_spanset_span, contained_spanset_spanset, + contained_periodset_temporal """ from .period import Period from ..temporal import Temporal @@ -450,9 +469,11 @@ def is_contained_in(self, container: Union[Period, PeriodSet, Box, Temporal]) -> elif isinstance(container, PeriodSet): return contained_spanset_spanset(self._inner, container._inner) elif isinstance(container, Temporal): - return contained_spanset_spanset(self._inner, temporal_time(container._inner)) + return contained_spanset_spanset(self._inner, + temporal_time(container._inner)) elif isinstance(container, get_args(Box)): - return contained_spanset_span(self._inner, container.to_period()._inner) + return contained_spanset_span(self._inner, + container.to_period()._inner) else: raise TypeError(f'Operation not supported with type {container.__class__}') @@ -475,7 +496,8 @@ def contains(self, content: Union[Time, Box, Temporal]) -> bool: True if contains, False otherwise MEOS Functions: - contains_spanset_span, contains_spanset_spanset, contains_periodset_timestamp + contains_spanset_span, contains_spanset_spanset, + contains_periodset_timestamp """ from .period import Period from .timestampset import TimestampSet @@ -486,13 +508,17 @@ def contains(self, content: Union[Time, Box, Temporal]) -> bool: if isinstance(content, PeriodSet): return contains_spanset_spanset(self._inner, content._inner) elif isinstance(content, datetime): - return contains_periodset_timestamp(self._inner, datetime_to_timestamptz(content)) + return contains_periodset_timestamp(self._inner, + datetime_to_timestamptz(content)) elif isinstance(content, TimestampSet): - return contains_spanset_spanset(self._inner, set_to_spanset(content._inner)) + return contains_spanset_spanset(self._inner, + set_to_spanset(content._inner)) elif isinstance(content, Temporal): - return contains_spanset_spanset(self._inner, temporal_time(content._inner)) + return contains_spanset_spanset(self._inner, + temporal_time(content._inner)) elif isinstance(content, get_args(Box)): - return contains_spanset_span(self._inner, content.to_period()._inner) + return contains_spanset_span(self._inner, + content.to_period()._inner) else: raise TypeError(f'Operation not supported with type {content.__class__}') @@ -515,14 +541,17 @@ def __contains__(self, item): True if contains, False otherwise MEOS Functions: - contains_spanset_span, contains_spanset_spanset, contains_periodset_timestamp, - contains_periodset_timestampset, contains_periodset_temporal + contains_spanset_span, contains_spanset_spanset, + contains_periodset_timestamp, contains_periodset_timestampset, + contains_periodset_temporal """ return self.contains(item) - def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Box, Temporal]) -> bool: + def overlaps(self, + other: Union[Period, PeriodSet, TimestampSet, Box, Temporal]) -> bool: """ - Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant + Returns whether ``self`` temporally overlaps ``other``. That is, both + share at least an instant Examples: >>> PeriodSet('{[2012-01-01, 2012-01-02]}').overlaps(PeriodSet('{[2012-01-02, 2012-01-03]}')) @@ -552,7 +581,8 @@ def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Box, Temporal]) elif isinstance(other, TimestampSet): return overlaps_spanset_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return overlaps_spanset_span(self._inner, temporal_to_period(other._inner)) + return overlaps_spanset_span(self._inner, + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): return overlaps_spanset_span(self._inner, other.to_period()._inner) else: @@ -560,7 +590,8 @@ def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Box, Temporal]) def is_same(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether the bounding period of `self` is the same as the bounding period of `other`. + Returns whether the bounding period of `self` is the same as the + bounding period of `other`. Args: other: A time or temporal object to compare to `self`. @@ -576,7 +607,8 @@ def is_same(self, other: Union[Time, Box, Temporal]) -> bool: # ------------------------- Position Operations --------------------------- def is_before(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + Returns whether ``self`` is strictly before ``other``. That is, + ``self`` ends before ``other`` starts. Examples: >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_before(PeriodSet('{[2012-01-02, 2012-01-03]}')) @@ -593,22 +625,25 @@ def is_before(self, other: Union[Time, Box, Temporal]) -> bool: True if before, False otherwise MEOS Functions: - before_periodset_timestamp, left_spanset_span, left_spanset_spanset + before_periodset_timestamp, left_spanset_span, left_spanset_spanset """ from .period import Period from .timestampset import TimestampSet from ..temporal import Temporal from ..boxes import Box if isinstance(other, datetime): - return before_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + return before_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): - return left_spanset_spanset(self._inner, set_to_spanset(other._inner)) + return left_spanset_spanset(self._inner, + set_to_spanset(other._inner)) elif isinstance(other, Period): return left_spanset_span(self._inner, other._inner) elif isinstance(other, PeriodSet): return left_spanset_spanset(self._inner, other._inner) elif isinstance(other, Temporal): - return left_spanset_span(self._inner, temporal_to_period(other._inner)) + return left_spanset_span(self._inner, + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): return left_spanset_span(self._inner, other.to_period()._inner) else: @@ -616,8 +651,8 @@ def is_before(self, other: Union[Time, Box, Temporal]) -> bool: def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). + Returns whether ``self`` is before ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same time). Examples: >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_over_or_before(PeriodSet('{[2012-01-02, 2012-01-03]}')) @@ -634,8 +669,9 @@ def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: True if before, False otherwise MEOS Functions: - overleft_spanset_span, overleft_spanset_spanset, overbefore_periodset_timestamp, - overbefore_periodset_timestampset, overbefore_periodset_temporal + overleft_spanset_span, overleft_spanset_spanset, + overbefore_periodset_timestamp, overbefore_periodset_timestampset, + overbefore_periodset_temporal """ from .period import Period from .timestampset import TimestampSet @@ -646,11 +682,13 @@ def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: if isinstance(other, PeriodSet): return overleft_spanset_spanset(self._inner, other._inner) elif isinstance(other, datetime): - return overbefore_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + return overbefore_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) if isinstance(other, TimestampSet): return overleft_spanset_span(self._inner, set_span(other._inner)) elif isinstance(other, Temporal): - return overleft_spanset_span(self._inner, temporal_to_period(other._inner)) + return overleft_spanset_span(self._inner, + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): return overleft_spanset_span(self._inner, other.to_period()._inner) else: @@ -658,7 +696,8 @@ def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: def is_after(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is strictly after ``other``.That is, ``self`` starts after ``other`` ends. + Returns whether ``self`` is strictly after ``other``.That is, ``self`` + starts after ``other`` ends. Examples: >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_after(PeriodSet('{[2012-01-01, 2012-01-02)}')) @@ -675,14 +714,16 @@ def is_after(self, other: Union[Time, Box, Temporal]) -> bool: True if after, False otherwise MEOS Functions: - right_spanset_span, right_spanset_spanset, overbefore_timestamp_periodset + right_spanset_span, right_spanset_spanset, + overbefore_timestamp_periodset """ from .period import Period from .timestampset import TimestampSet from ..temporal import Temporal from ..boxes import Box if isinstance(other, datetime): - return overbefore_timestamp_periodset(datetime_to_timestamptz(other), self._inner) + return overbefore_timestamp_periodset( + datetime_to_timestamptz(other), self._inner) elif isinstance(other, TimestampSet): return right_spanset_span(self._inner, set_span(other._inner)) elif isinstance(other, Period): @@ -690,7 +731,8 @@ def is_after(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, PeriodSet): return right_spanset_spanset(self._inner, other._inner) elif isinstance(other, Temporal): - return right_spanset_span(self._inner, temporal_to_period(other._inner)) + return right_spanset_span(self._inner, + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): return right_spanset_span(self._inner, other.to_period()._inner) else: @@ -698,8 +740,8 @@ def is_after(self, other: Union[Time, Box, Temporal]) -> bool: def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is after ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same time). Examples: >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_over_or_after(PeriodSet('{[2012-01-01, 2012-01-02)}')) @@ -716,15 +758,16 @@ def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: True if overlapping or after, False otherwise MEOS Functions: - overright_spanset_span, overright_spanset_spanset, overafter_periodset_timestamp, - overafter_periodset_timestampset, + overright_spanset_span, overright_spanset_spanset, + overafter_periodset_timestamp, overafter_periodset_timestampset """ from .period import Period from .timestampset import TimestampSet from ..temporal import Temporal from ..boxes import Box if isinstance(other, datetime): - return overafter_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + return overafter_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): return overright_spanset_span(self._inner, set_span(other._inner)) elif isinstance(other, Period): @@ -732,9 +775,11 @@ def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, PeriodSet): return overright_spanset_spanset(self._inner, other._inner) elif isinstance(other, Temporal): - return overright_spanset_span(self._inner, temporal_to_period(other._inner)) + return overright_spanset_span(self._inner, + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): - return overright_spanset_span(self._inner, other.to_period()._inner) + return overright_spanset_span(self._inner, + other.to_period()._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -750,25 +795,31 @@ def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: A :class:`datetime.timedelta` instance MEOS Functions: - distance_periodset_period, distance_periodset_periodset, distance_periodset_timestamp, - distance_periodset_timestampset + distance_periodset_period, distance_periodset_periodset, + distance_periodset_timestamp, distance_periodset_timestampset """ from .period import Period from .timestampset import TimestampSet from ..temporal import Temporal from ..boxes import Box if isinstance(other, Temporal): - return timedelta(seconds=distance_spanset_span(self._inner, temporal_to_period(other._inner))) + return timedelta(seconds=distance_spanset_span(self._inner, + temporal_to_period(other._inner))) elif isinstance(other, Period): - return timedelta(seconds=distance_spanset_span(self._inner, other._inner)) + return timedelta(seconds=distance_spanset_span(self._inner, + other._inner)) elif isinstance(other, PeriodSet): - return timedelta(seconds=distance_spanset_spanset(self._inner, other._inner)) + return timedelta(seconds=distance_spanset_spanset(self._inner, + other._inner)) elif isinstance(other, datetime): - return timedelta(seconds=distance_periodset_timestamp(self._inner, datetime_to_timestamptz(other))) + return timedelta(seconds=distance_periodset_timestamp(self._inner, + datetime_to_timestamptz(other))) elif isinstance(other, TimestampSet): - return timedelta(seconds=distance_spanset_span(self._inner, set_span(other._inner))) + return timedelta(seconds=distance_spanset_span(self._inner, + set_span(other._inner))) elif isinstance(other, get_args(Box)): - return timedelta(seconds=distance_spanset_span(self._inner, other.to_period()._inner)) + return timedelta(seconds=distance_spanset_span(self._inner, + other.to_period()._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -789,7 +840,8 @@ def intersection(self, other: datetime) -> datetime: def intersection(self, other: TimestampSet) -> TimestampSet: ... - def intersection(self, other: Time) -> Union[PeriodSet, datetime, TimestampSet]: + def intersection(self, other: Time) -> \ + Union[PeriodSet, datetime, TimestampSet]: """ Returns the temporal intersection of ``self`` and ``other``. @@ -800,16 +852,21 @@ def intersection(self, other: Time) -> Union[PeriodSet, datetime, TimestampSet]: A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_periodset_timestamp, intersection_spanset_spanset, intersection_spanset_span + intersection_periodset_timestamp, intersection_spanset_spanset, + intersection_spanset_span """ from .period import Period from .timestampset import TimestampSet if isinstance(other, datetime): - result = intersection_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) - return timestamptz_to_datetime(result) if result is not None else None + result = intersection_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) + return timestamptz_to_datetime(result) \ + if result is not None else None elif isinstance(other, TimestampSet): - result = intersection_spanset_spanset(self._inner, set_to_spanset(other._inner)) - return TimestampSet(_inner=result) if result is not None else None + result = intersection_spanset_spanset(self._inner, + set_to_spanset(other._inner)) + return TimestampSet(_inner=result) \ + if result is not None else None elif isinstance(other, Period): result = intersection_spanset_span(self._inner, other._inner) return PeriodSet(_inner=result) if result is not None else None @@ -830,7 +887,8 @@ def __mul__(self, other): A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_periodset_timestamp, intersection_spanset_spanset, intersection_spanset_span + intersection_periodset_timestamp, intersection_spanset_spanset, + intersection_spanset_span """ return self.intersection(other) @@ -850,9 +908,11 @@ def minus(self, other: Time) -> PeriodSet: from .period import Period from .timestampset import TimestampSet if isinstance(other, datetime): - result = minus_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + result = minus_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): - result = minus_spanset_spanset(self._inner, set_to_spanset(other._inner)) + result = minus_spanset_spanset(self._inner, + set_to_spanset(other._inner)) elif isinstance(other, Period): result = minus_spanset_span(self._inner, other._inner) elif isinstance(other, PeriodSet): @@ -872,7 +932,8 @@ def __sub__(self, other): A :class:`PeriodSet` instance. MEOS Functions: - minus_spanset_span, minus_spanset_spanset, minus_periodset_timestamp + minus_spanset_span, minus_spanset_spanset, + minus_periodset_timestamp """ return self.minus(other) @@ -887,19 +948,23 @@ def union(self, other: Time) -> PeriodSet: A :class:`PeriodSet` instance. MEOS Functions: - union_periodset_timestamp, union_spanset_spanset, union_spanset_span + union_periodset_timestamp, union_spanset_spanset, + union_spanset_span """ from .period import Period from .timestampset import TimestampSet if isinstance(other, datetime): - return PeriodSet(_inner=union_periodset_timestamp(self._inner, datetime_to_timestamptz(other))) + return PeriodSet(_inner=union_periodset_timestamp(self._inner, + datetime_to_timestamptz(other))) elif isinstance(other, TimestampSet): - return PeriodSet(_inner=union_spanset_spanset(self._inner, set_to_spanset(other._inner))) + return PeriodSet(_inner=union_spanset_spanset(self._inner, + set_to_spanset(other._inner))) elif isinstance(other, Period): - return PeriodSet(_inner=union_spanset_span(self._inner, other._inner)) + return PeriodSet(_inner=union_spanset_span(self._inner, + other._inner)) elif isinstance(other, PeriodSet): - return PeriodSet(_inner=union_spanset_spanset(self._inner, other._inner)) - + return PeriodSet(_inner=union_spanset_spanset(self._inner, + other._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -914,7 +979,8 @@ def __add__(self, other): A :class:`PeriodSet` instance. MEOS Functions: - union_periodset_timestamp, union_spanset_spanset, union_spanset_span + union_periodset_timestamp, union_spanset_spanset, + union_spanset_span """ return self.union(other) @@ -1030,7 +1096,8 @@ def plot(self, *args, **kwargs): @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`PeriodSet` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`PeriodSet` from a database cursor. Used when + automatically loading objects from the database. Users should use the class constructor instead. """ if not value: diff --git a/pymeos/pymeos/time/timestampset.py b/pymeos/pymeos/time/timestampset.py index 4e1a1dd0..519f0505 100644 --- a/pymeos/pymeos/time/timestampset.py +++ b/pymeos/pymeos/time/timestampset.py @@ -35,18 +35,21 @@ class TimestampSet: __slots__ = ['_inner'] # ------------------------- Constructors ---------------------------------- - def __init__(self, string: Optional[str] = None, *, timestamp_list: Optional[List[Union[str, datetime]]] = None, + def __init__(self, string: Optional[str] = None, *, + timestamp_list: Optional[List[Union[str, datetime]]] = None, _inner=None): super().__init__() - assert (_inner is not None) or ((string is not None) != (timestamp_list is not None)), \ + assert (_inner is not None) or ((string is not None) != + (timestamp_list is not None)), \ "Either string must be not None or timestamp_list must be not" if _inner is not None: self._inner = _inner elif string is not None: self._inner = timestampset_in(string) else: - times = [pg_timestamp_in(ts, -1) if isinstance(ts, str) else datetime_to_timestamptz(ts) - for ts in timestamp_list] + times = [pg_timestamp_in(ts, -1) + if isinstance(ts, str) else datetime_to_timestamptz(ts) + for ts in timestamp_list] self._inner = timestampset_make(times, len(times)) def __copy__(self): @@ -66,6 +69,7 @@ def __copy__(self): def from_wkb(wkb: bytes) -> TimestampSet: """ Returns a `TimestampSet` from its WKB representation. + Args: wkb: WKB representation @@ -80,7 +84,9 @@ def from_wkb(wkb: bytes) -> TimestampSet: @staticmethod def from_hexwkb(hexwkb: str) -> TimestampSet: """ - Returns a `TimestampSet` from its WKB representation in hex-encoded ASCII. + Returns a `TimestampSet` from its WKB representation in hex-encoded + ASCII. + Args: hexwkb: WKB representation in hex-encoded ASCII @@ -121,6 +127,7 @@ def __repr__(self): def as_wkb(self) -> bytes: """ Returns the WKB representation of ``self``. + Returns: A :class:`str` object with the WKB representation of ``self``. @@ -132,8 +139,10 @@ def as_wkb(self) -> bytes: def as_hexwkb(self) -> str: """ Returns the WKB representation of ``self`` in hex-encoded ASCII. + Returns: - A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. + A :class:`str` object with the WKB representation of ``self`` in + hex-encoded ASCII. MEOS Functions: set_as_hexwkb @@ -143,7 +152,8 @@ def as_hexwkb(self) -> str: # ------------------------- Conversions ----------------------------------- def to_periodset(self) -> PeriodSet: """ - Returns a PeriodSet that contains a Period for each Timestamp in ``self``. + Returns a PeriodSet that contains a Period for each Timestamp in + ``self``. Returns: A new :class:`PeriodSet` instance @@ -157,11 +167,12 @@ def to_periodset(self) -> PeriodSet: # ------------------------- Accessors ------------------------------------- def duration(self) -> timedelta: """ - Returns the duration of the time ignoring gaps, i.e. the duration from the - first timestamp to the last one. + Returns the duration of the time ignoring gaps, i.e. the duration from + the first timestamp to the last one. Returns: - A :class:`datetime.timedelta` instance representing the duration of the period + A :class:`datetime.timedelta` instance representing the duration of + the period MEOS Functions: period_duration @@ -184,6 +195,7 @@ def period(self) -> Period: def num_timestamps(self) -> int: """ Returns the number of timestamps in ``self``. + Returns: An :class:`int` @@ -195,6 +207,7 @@ def num_timestamps(self) -> int: def start_timestamp(self) -> datetime: """ Returns the first timestamp in ``self``. + Returns: A :class:`datetime` instance @@ -206,6 +219,7 @@ def start_timestamp(self) -> datetime: def end_timestamp(self) -> datetime: """ Returns the last timestamp in ``self``. + Returns: A :class:`datetime` instance @@ -217,6 +231,7 @@ def end_timestamp(self) -> datetime: def timestamp_n(self, n: int) -> datetime: """ Returns the n-th timestamp in ``self``. + Returns: A :class:`datetime` instance @@ -231,6 +246,7 @@ def timestamp_n(self, n: int) -> datetime: def timestamps(self) -> List[datetime]: """ Returns the list of distinct timestamps in ``self``. + Returns: A :class:`list[datetime]` instance @@ -238,7 +254,8 @@ def timestamps(self) -> List[datetime]: timestampset_timestamps """ tss = timestampset_values(self._inner) - return [timestamptz_to_datetime(tss[i]) for i in range(self.num_timestamps())] + return [timestamptz_to_datetime(tss[i]) \ + for i in range(self.num_timestamps())] def __hash__(self) -> int: """ @@ -255,7 +272,8 @@ def __hash__(self) -> int: # ------------------------- Transformations ------------------------------- def shift(self, delta: timedelta) -> TimestampSet: """ - Returns a new TimestampSet that is the result of shifting ``self`` by ``delta`` + Returns a new TimestampSet that is the result of shifting ``self`` by + ``delta`` Examples: >>> TimestampSet('{2000-01-01, 2000-01-10}').shift(timedelta(days=2)) @@ -274,7 +292,8 @@ def shift(self, delta: timedelta) -> TimestampSet: def tscale(self, duration: timedelta) -> TimestampSet: """ - Returns a new TimestampSet that with the scaled so that the span of ``self`` is ``duration``. + Returns a new TimestampSet that with the scaled so that the span of + ``self`` is ``duration``. Examples: >>> TimestampSet('{2000-01-01, 2000-01-10}').tscale(timedelta(days=2)) @@ -291,9 +310,11 @@ def tscale(self, duration: timedelta) -> TimestampSet: """ return self.shift_tscale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TimestampSet: + def shift_tscale(self, shift: Optional[timedelta] = None, + duration: Optional[timedelta] = None) -> TimestampSet: """ - Returns a new TimestampSet that is the result of shifting and scaling ``self``. + Returns a new TimestampSet that is the result of shifting and scaling + ``self``. Examples: >>> TimestampSet('{2000-01-01, 2000-01-10}').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) @@ -301,7 +322,8 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim Args: shift: :class:`datetime.timedelta` instance to shift - duration: :class:`datetime.timedelta` instance representing the span of the new set + duration: :class:`datetime.timedelta` instance representing the + span of the new set Returns: A new :class:`PeriodSet` instance @@ -309,7 +331,8 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim MEOS Functions: timestampset_shift_tscale """ - assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' + assert shift is not None or duration is not None, \ + 'shift and scale deltas must not be both None' tss = timestampset_shift_tscale( self._inner, timedelta_to_interval(shift) if shift else None, @@ -320,8 +343,8 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim # ------------------------- Topological Operations ------------------------ def is_adjacent(self, other: Union[Period, PeriodSet, Temporal, Box]) -> bool: """ - Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them - contains it. + Returns whether ``self`` is temporally adjacent to ``other``. That is, + they share a bound but only one of them contains it. Examples: >>> TimestampSet('{2012-01-01, 2012-01-02}').is_adjacent(Period('[2012-01-02, 2012-01-03]')) @@ -338,7 +361,7 @@ def is_adjacent(self, other: Union[Period, PeriodSet, Temporal, Box]) -> bool: True if adjacent, False otherwise MEOS Functions: - adjacent_span_span, adjacent_spanset_span + adjacent_span_span, adjacent_spanset_span """ from .period import Period from .periodset import PeriodSet @@ -347,15 +370,19 @@ def is_adjacent(self, other: Union[Period, PeriodSet, Temporal, Box]) -> bool: if isinstance(other, Period): return adjacent_span_span(set_span(self._inner), other._inner) elif isinstance(other, PeriodSet): - return adjacent_spanset_spanset(other._inner, set_to_spanset(self._inner)) + return adjacent_spanset_spanset(other._inner, + set_to_spanset(self._inner)) elif isinstance(other, Temporal): - return adjacent_span_span(set_span(self._inner), temporal_to_period(other._inner)) + return adjacent_span_span(set_span(self._inner), + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): - return adjacent_span_span(set_span(self._inner), other.to_period()._inner) + return adjacent_span_span(set_span(self._inner), + other.to_period()._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') - def is_contained_in(self, container: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) -> bool: + def is_contained_in(self, container: Union[Period, PeriodSet, TimestampSet, + Temporal, Box]) -> bool: """ Returns whether ``self`` is temporally contained in ``container``. @@ -374,7 +401,8 @@ def is_contained_in(self, container: Union[Period, PeriodSet, TimestampSet, Temp True if contained, False otherwise MEOS Functions: - contained_span_span, contained_span_spanset, contained_set_set, contained_spanset_spanset + contained_span_span, contained_span_spanset, contained_set_set, + contained_spanset_spanset """ from .period import Period from .periodset import PeriodSet @@ -387,9 +415,11 @@ def is_contained_in(self, container: Union[Period, PeriodSet, TimestampSet, Temp elif isinstance(container, TimestampSet): return contained_set_set(self._inner, container._inner) elif isinstance(container, Temporal): - return contained_spanset_spanset(set_to_spanset(self._inner), temporal_time(container._inner)) + return contained_spanset_spanset(set_to_spanset(self._inner), + temporal_time(container._inner)) elif isinstance(container, Box): - return contained_span_span(set_span(self._inner), container.to_period()._inner) + return contained_span_span(set_span(self._inner), + container.to_period()._inner) else: raise TypeError(f'Operation not supported with type {container.__class__}') @@ -412,15 +442,18 @@ def contains(self, content: Union[datetime, TimestampSet, Temporal]) -> bool: True if contains, False otherwise MEOS Functions: - contains_timestampset_timestamp, contains_set_set, contains_spanset_spanset + contains_timestampset_timestamp, contains_set_set, + contains_spanset_spanset """ from ..temporal import Temporal if isinstance(content, datetime): - return contains_timestampset_timestamp(self._inner, datetime_to_timestamptz(content)) + return contains_timestampset_timestamp(self._inner, + datetime_to_timestamptz(content)) elif isinstance(content, TimestampSet): return contains_set_set(self._inner, content._inner) elif isinstance(content, Temporal): - return contains_spanset_spanset(set_to_spanset(self._inner), temporal_time(content._inner)) + return contains_spanset_spanset(set_to_spanset(self._inner), + temporal_time(content._inner)) else: raise TypeError(f'Operation not supported with type {content.__class__}') @@ -443,13 +476,16 @@ def __contains__(self, item): True if contains, False otherwise MEOS Functions: - contains_timestampset_timestamp, contains_set_set, contains_spanset_spanset + contains_timestampset_timestamp, contains_set_set, + contains_spanset_spanset """ return self.contains(item) - def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) -> bool: + def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Temporal, + Box]) -> bool: """ - Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant + Returns whether ``self`` temporally overlaps ``other``. That is, both + share at least an instant Examples: >>> TimestampSet('{2012-01-01, 2012-01-02}').overlaps(TimestampSet('{2012-01-02, 2012-01-03}')) @@ -466,30 +502,35 @@ def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) True if overlaps, False otherwise MEOS Functions: - overlaps_set_set, overlaps_span_span, overlaps_spanset_spanset + overlaps_set_set, overlaps_span_span, overlaps_spanset_spanset """ from .period import Period from .periodset import PeriodSet from ..temporal import Temporal from ..boxes import Box if isinstance(other, datetime): - return contains_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) + return contains_timestampset_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): return overlaps_set_set(self._inner, other._inner) elif isinstance(other, Period): return overlaps_span_span(set_span(self._inner), other._inner) elif isinstance(other, PeriodSet): - return overlaps_spanset_spanset(set_to_spanset(self._inner), other._inner) + return overlaps_spanset_spanset(set_to_spanset(self._inner), + other._inner) elif isinstance(other, Temporal): - return overlaps_spanset_spanset(set_to_spanset(self._inner), temporal_time(other._inner)) + return overlaps_spanset_spanset(set_to_spanset(self._inner), + temporal_time(other._inner)) elif isinstance(other, Box): - return overlaps_span_span(set_span(self._inner), other.to_period()._inner) + return overlaps_span_span(set_span(self._inner), + other.to_period()._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') def is_same(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding period of `self` is the same as the bounding period of `other`. + Returns whether the bounding period of `self` is the same as the + bounding period of `other`. Args: other: A time or temporal object to compare to `self`. @@ -505,8 +546,8 @@ def is_same(self, other: Union[Time, Temporal, Box]) -> bool: # ------------------------- Position Operations --------------------------- def is_after(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether ``self`` is strictly after ``other``. That is, the first timestamp in ``self`` - is after ``other``. + Returns whether ``self`` is strictly after ``other``. That is, the + first timestamp in ``self`` is after ``other``. Examples: >>> TimestampSet('{2012-01-02, 2012-01-03}').is_after(Period('[2012-01-01, 2012-01-02)')) @@ -523,14 +564,16 @@ def is_after(self, other: Union[Time, Temporal, Box]) -> bool: True if after, False otherwise MEOS Functions: - overbefore_timestamp_timestampset, right_set_set, right_span_span, right_span_spanset + overbefore_timestamp_timestampset, right_set_set, right_span_span, + right_span_spanset """ from .period import Period from .periodset import PeriodSet from ..temporal import Temporal from ..boxes import Box if isinstance(other, datetime): - return overbefore_timestamp_timestampset(datetime_to_timestamptz(other), self._inner) + return overbefore_timestamp_timestampset( + datetime_to_timestamptz(other), self._inner) elif isinstance(other, TimestampSet): return right_set_set(self._inner, other._inner) elif isinstance(other, Period): @@ -538,15 +581,18 @@ def is_after(self, other: Union[Time, Temporal, Box]) -> bool: elif isinstance(other, PeriodSet): return right_span_spanset(set_span(self._inner), other._inner) elif isinstance(other, Temporal): - return right_span_span(set_span(self._inner), temporal_to_period(other._inner)) + return right_span_span(set_span(self._inner), + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): - return right_span_span(set_span(self._inner), other.to_period()._inner) + return right_span_span(set_span(self._inner), + other.to_period()._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') def is_before(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + Returns whether ``self`` is strictly before ``other``. That is, + ``self`` ends before ``other`` starts. Examples: >>> TimestampSet('{2012-01-01, 2012-01-02}').is_before(TimestampSet('{2012-01-03}')) @@ -570,7 +616,8 @@ def is_before(self, other: Union[Time, Temporal, Box]) -> bool: from ..temporal import Temporal from ..boxes import Box if isinstance(other, datetime): - return overafter_timestamp_period(datetime_to_timestamptz(other), set_span(self._inner)) + return overafter_timestamp_period(datetime_to_timestamptz(other), + set_span(self._inner)) elif isinstance(other, TimestampSet): return left_set_set(self._inner, other._inner) elif isinstance(other, Period): @@ -578,16 +625,18 @@ def is_before(self, other: Union[Time, Temporal, Box]) -> bool: elif isinstance(other, PeriodSet): return left_span_spanset(set_span(self._inner), other._inner) elif isinstance(other, Temporal): - return left_span_span(set_span(self._inner), temporal_to_period(other._inner)) + return left_span_span(set_span(self._inner), + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): - return left_span_span(set_span(self._inner), other.to_period()._inner) + return left_span_span(set_span(self._inner), + other.to_period()._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') def is_over_or_after(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is after ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same time). Examples: >>> TimestampSet('{2012-01-02, 2012-01-03}').is_over_or_after(Period('[2012-01-01, 2012-01-02)')) @@ -611,7 +660,8 @@ def is_over_or_after(self, other: Union[Time, Temporal, Box]) -> bool: from ..temporal import Temporal from ..boxes import Box if isinstance(other, datetime): - return overafter_period_timestamp(set_span(self._inner), datetime_to_timestamptz(other)) + return overafter_period_timestamp(set_span(self._inner), + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): return overright_set_set(self._inner, other._inner) elif isinstance(other, Period): @@ -619,16 +669,18 @@ def is_over_or_after(self, other: Union[Time, Temporal, Box]) -> bool: elif isinstance(other, PeriodSet): return overright_span_spanset(set_span(self._inner), other._inner) elif isinstance(other, Temporal): - return overright_span_span(set_span(self._inner), temporal_to_period(other._inner)) + return overright_span_span(set_span(self._inner), + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): - return overright_span_span(set_span(self._inner), other.to_period()._inner) + return overright_span_span(set_span(self._inner), + other.to_period()._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') def is_over_or_before(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). + Returns whether ``self`` is before ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same time). Examples: >>> TimestampSet('{2012-01-01, 2012-01-02}').is_over_or_before(Period('[2012-01-02, 2012-01-03]')) @@ -645,14 +697,16 @@ def is_over_or_before(self, other: Union[Time, Temporal, Box]) -> bool: True if before, False otherwise MEOS Functions: - overbefore_period_timestamp, overleft_span_span, overleft_span_spanset + overbefore_period_timestamp, overleft_span_span, + overleft_span_spanset """ from .period import Period from .periodset import PeriodSet from ..temporal import Temporal from ..boxes import Box if isinstance(other, datetime): - return overbefore_period_timestamp(set_span(self._inner), datetime_to_timestamptz(other)) + return overbefore_period_timestamp(set_span(self._inner), + datetime_to_timestamptz(other)) if isinstance(other, TimestampSet): return overleft_set_set(self._inner, other._inner) if isinstance(other, Period): @@ -660,9 +714,11 @@ def is_over_or_before(self, other: Union[Time, Temporal, Box]) -> bool: if isinstance(other, PeriodSet): return overleft_span_spanset(set_span(self._inner), other._inner) elif isinstance(other, Temporal): - return overleft_span_span(set_span(self._inner), temporal_to_period(other._inner)) + return overleft_span_span(set_span(self._inner), + temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): - return overleft_span_span(set_span(self._inner), other.to_period()._inner) + return overleft_span_span(set_span(self._inner), + other.to_period()._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -678,24 +734,30 @@ def distance(self, other: Union[Time, Temporal, Box]) -> timedelta: A :class:`datetime.timedelta` instance MEOS Functions: - distance_timestampset_timestamp, distance_set_set, distance_span_span, distance_spanset_span + distance_timestampset_timestamp, distance_set_set, + distance_span_span, distance_spanset_span """ from .period import Period from .periodset import PeriodSet from ..temporal import Temporal from ..boxes import Box if isinstance(other, datetime): - return timedelta(seconds=distance_timestampset_timestamp(self._inner, datetime_to_timestamptz(other))) + return timedelta(seconds=distance_timestampset_timestamp(self._inner, + datetime_to_timestamptz(other))) elif isinstance(other, TimestampSet): return timedelta(seconds=distance_set_set(self._inner, other._inner)) elif isinstance(other, Period): - return timedelta(seconds=distance_span_span(set_span(self._inner), other._inner)) + return timedelta(seconds=distance_span_span(set_span(self._inner), + other._inner)) elif isinstance(other, PeriodSet): - return timedelta(seconds=distance_spanset_span(other._inner, set_span(self._inner))) + return timedelta(seconds=distance_spanset_span(other._inner, + set_span(self._inner))) elif isinstance(other, Temporal): - return timedelta(seconds=distance_span_span(set_span(self._inner), temporal_to_period(other._inner))) + return timedelta(seconds=distance_span_span(set_span(self._inner), + temporal_to_period(other._inner))) elif isinstance(other, get_args(Box)): - return timedelta(seconds=distance_span_span(set_span(self._inner), other.to_period()._inner)) + return timedelta(seconds=distance_span_span(set_span(self._inner), + other.to_period()._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -709,7 +771,8 @@ def intersection(self, other: TimestampSet) -> Optional[TimestampSet]: ... @overload - def intersection(self, other: Union[Period, PeriodSet, Temporal]) -> Optional[PeriodSet]: + def intersection(self, other: Union[Period, PeriodSet, Temporal]) -> \ + Optional[PeriodSet]: ... def intersection(self, other: Union[Time, Temporal]) -> Optional[Time]: @@ -723,21 +786,25 @@ def intersection(self, other: Union[Time, Temporal]) -> Optional[Time]: A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_set_set, intersection_spanset_span, intersection_spanset_spanset + intersection_set_set, intersection_spanset_span, + intersection_spanset_spanset """ from .period import Period from .periodset import PeriodSet if isinstance(other, datetime): - result = intersection_set_set(self._inner, timestamp_to_tstzset(datetime_to_timestamptz(other))) + result = intersection_set_set(self._inner, + timestamp_to_tstzset(datetime_to_timestamptz(other))) return timestamptz_to_datetime(result) if result is not None else None elif isinstance(other, TimestampSet): result = intersection_set_set(self._inner, other._inner) return TimestampSet(_inner=result) if result is not None else None elif isinstance(other, Period): - result = intersection_spanset_span(set_to_spanset(self._inner), other._inner) + result = intersection_spanset_span(set_to_spanset(self._inner), + other._inner) return PeriodSet(_inner=result) if result is not None else None elif isinstance(other, PeriodSet): - result = intersection_spanset_spanset(set_to_spanset(self._inner), other._inner) + result = intersection_spanset_spanset(set_to_spanset(self._inner), + other._inner) return PeriodSet(_inner=result) if result is not None else None else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -753,12 +820,14 @@ def __mul__(self, other): A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_set_set, intersection_spanset_span, intersection_spanset_spanset + intersection_set_set, intersection_spanset_span, + intersection_spanset_spanset """ return self.intersection(other) @overload - def minus(self, other: Union[datetime, TimestampSet]) -> Optional[TimestampSet]: + def minus(self, other: Union[datetime, TimestampSet]) -> \ + Optional[TimestampSet]: ... @overload @@ -776,21 +845,25 @@ def minus(self, other: Time) -> Optional[Time]: A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - minus_timestampset_timestamp, minus_set_set, minus_spanset_span, minus_spanset_spanset + minus_timestampset_timestamp, minus_set_set, minus_spanset_span, + minus_spanset_spanset """ from .period import Period from .periodset import PeriodSet if isinstance(other, datetime): - result = minus_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) + result = minus_timestampset_timestamp(self._inner, + datetime_to_timestamptz(other)) return TimestampSet(_inner=result) if result is not None else None elif isinstance(other, TimestampSet): result = minus_set_set(self._inner, other._inner) return TimestampSet(_inner=result) if result is not None else None elif isinstance(other, Period): - result = minus_spanset_span(set_to_spanset(self._inner), other._inner) + result = minus_spanset_span(set_to_spanset(self._inner), + other._inner) return PeriodSet(_inner=result) if result is not None else None elif isinstance(other, PeriodSet): - result = minus_spanset_spanset(set_to_spanset(self._inner), other._inner) + result = minus_spanset_spanset(set_to_spanset(self._inner), + other._inner) return PeriodSet(_inner=result) if result is not None else None else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -806,7 +879,8 @@ def __sub__(self, other): A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - minus_timestampset_timestamp, minus_set_set, minus_spanset_span, minus_spanset_spanset + minus_timestampset_timestamp, minus_set_set, minus_spanset_span, + minus_spanset_spanset """ return self.minus(other) @@ -829,18 +903,22 @@ def union(self, other: Time) -> Union[PeriodSet, TimestampSet]: A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - union_timestampset_timestamp, union_set_set, union_spanset_span, union_spanset_spanset + union_timestampset_timestamp, union_set_set, union_spanset_span, + union_spanset_spanset """ from .period import Period from .periodset import PeriodSet if isinstance(other, datetime): - return TimestampSet(_inner=union_timestampset_timestamp(self._inner, datetime_to_timestamptz(other))) + return TimestampSet(_inner=union_timestampset_timestamp(self._inner, + datetime_to_timestamptz(other))) elif isinstance(other, TimestampSet): return TimestampSet(_inner=union_set_set(self._inner, other._inner)) elif isinstance(other, Period): - return PeriodSet(_inner=union_spanset_span(set_to_spanset(self._inner), other._inner)) + return PeriodSet(_inner=union_spanset_span( + set_to_spanset(self._inner), other._inner)) elif isinstance(other, PeriodSet): - return PeriodSet(_inner=union_spanset_spanset(set_to_spanset(self._inner), other._inner)) + return PeriodSet(_inner=union_spanset_spanset( + set_to_spanset(self._inner), other._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -855,7 +933,8 @@ def __add__(self, other): A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - union_timestampset_timestamp, union_set_set, union_spanset_span, union_spanset_spanset + union_timestampset_timestamp, union_set_set, union_spanset_span, + union_spanset_spanset """ return self.union(other) @@ -971,7 +1050,8 @@ def plot(self, *args, **kwargs): @staticmethod def read_from_cursor(value, _=None): """ - Reads a :class:`TimestampSet` from a database cursor. Used when automatically loading objects from the database. + Reads a :class:`TimestampSet` from a database cursor. Used when + automatically loading objects from the database. Users should use the class constructor instead. """ if not value: diff --git a/pymeos/tests/boxes/stbox_test.py b/pymeos/tests/boxes/stbox_test.py index 65121871..cca13a7f 100644 --- a/pymeos/tests/boxes/stbox_test.py +++ b/pymeos/tests/boxes/stbox_test.py @@ -215,6 +215,7 @@ def test_from_quad_split_flat(self, stbox, expected): ids=['STBox X', 'STBox Z', 'STBox T', 'STBox XT', 'STBox ZT'] ) def test_from_as_constructor(self, stbox): + assert stbox == STBox(str(stbox)) assert stbox == stbox.from_wkb(stbox.as_wkb()) assert stbox == stbox.from_hexwkb(stbox.as_hexwkb()) diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index 182d7ea1..a1e65bf1 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -160,6 +160,7 @@ def test_from_tnumber_constructor(self, tnumber, expected): ids=['TBoxFloat X', 'TBox T', 'TBoxFloat XT'] ) def test_from_as_constructor(self, tbox): + assert tbox == TBox(str(tbox)) assert tbox == tbox.from_wkb(tbox.as_wkb()) assert tbox == tbox.from_hexwkb(tbox.as_hexwkb()) diff --git a/pymeos/tests/main/tbool_test.py b/pymeos/tests/main/tbool_test.py index 38d39a1f..c8da98fc 100644 --- a/pymeos/tests/main/tbool_test.py +++ b/pymeos/tests/main/tbool_test.py @@ -144,7 +144,7 @@ def test_instant_list_sequence_constructor(self, list, interpolation, normalize, ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_from_as_constructor(self, temporal): - # assert temporal == temporal.from_wkt(temporal.as_wkt()) + assert temporal == temporal.__class__(str(temporal)) assert temporal == temporal.from_wkb(temporal.as_wkb()) assert temporal == temporal.from_hexwkb(temporal.as_hexwkb()) assert temporal == temporal.from_mfjson(temporal.as_mfjson()) diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index 56bf76c2..1838676e 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -151,6 +151,7 @@ def test_instant_list_sequence_constructor(self, list, interpolation, normalize, 'Stepwise Sequence', 'Stepwise SequenceSet'] ) def test_from_as_constructor(self, temporal): + assert temporal == temporal.__class__(str(temporal)) assert temporal == temporal.from_wkb(temporal.as_wkb()) assert temporal == temporal.from_hexwkb(temporal.as_hexwkb()) assert temporal == temporal.from_mfjson(temporal.as_mfjson()) @@ -1659,36 +1660,36 @@ def test_at_minus_time(self, temporal, restrictor): (tfi, 1.5), (tfi, 2.5), (tfi, floatrange(1.5, 1.5, True, True)), - # (tfi, [1.5,2.5]), + (tfi, [1.5,2.5]), # (tfi, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), (tfds, 1.5), (tfds, 2.5), (tfds, floatrange(1.5, 1.5, True, True)), - # (tfds, [1.5,2.5]), + (tfds, [1.5,2.5]), # (tfds, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), (tfs, 1.5), (tfs, 2.5), (tfs, floatrange(1.5, 1.5, True, True)), - # (tfs, [1.5,2.5]), + (tfs, [1.5,2.5]), # (tfs, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), (tfss, 1.5), (tfss, 2.5), (tfss, floatrange(1.5, 1.5, True, True)), - # (tfss, [1.5,2.5]), + (tfss, [1.5,2.5]), # (tfss, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), ], ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', - # 'Instant-ValueList', 'Instant-RangeList', + 'Instant-ValueList', # 'Instant-RangeList', 'Discrete Sequence-1.5', 'Discrete Sequence-2.5', 'Discrete Sequence-Range', - # 'Discrete Sequence-ValueList', 'Discrete Sequence-RangeList', + 'Discrete Sequence-ValueList', # 'Discrete Sequence-RangeList', 'Sequence-1.5', 'Sequence-2.5', 'Sequence-Range', - # 'Sequence-ValueList', 'Sequence-RangeList', + 'Sequence-ValueList', # 'Sequence-RangeList', 'SequenceSet-1.5', 'SequenceSet-2.5', 'SequenceSet-Range', - # 'SequenceSet-ValueList', 'SequenceSet-RangeList', + 'SequenceSet-ValueList', # 'SequenceSet-RangeList', ] ) def test_at_minus_values(self, temporal, restrictor): diff --git a/pymeos/tests/main/tgeogpoint_test.py b/pymeos/tests/main/tgeogpoint_test.py index 2154b0aa..7187e545 100644 --- a/pymeos/tests/main/tgeogpoint_test.py +++ b/pymeos/tests/main/tgeogpoint_test.py @@ -161,6 +161,7 @@ def test_instant_list_sequence_constructor(self, list, interpolation, normalize, 'Instant 3D', 'Discrete Sequence 3D', 'Sequence 3D', 'SequenceSet 3D'] ) def test_from_as_constructor(self, temporal): + assert temporal == temporal.__class__(str(temporal)) assert temporal == temporal.from_wkb(temporal.as_wkb()) assert temporal == temporal.from_hexwkb(temporal.as_hexwkb()) assert temporal == temporal.from_mfjson(temporal.as_mfjson()) diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index 7e74da22..dc33cd8a 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -157,6 +157,7 @@ def test_instant_list_sequence_constructor(self, list, interpolation, normalize, 'Instant 3D', 'Discrete Sequence 3D', 'Sequence 3D', 'SequenceSet 3D'] ) def test_from_as_constructor(self, temporal): + assert temporal == temporal.__class__(str(temporal)) assert temporal == temporal.from_wkb(temporal.as_wkb()) assert temporal == temporal.from_hexwkb(temporal.as_hexwkb()) assert temporal == temporal.from_mfjson(temporal.as_mfjson()) @@ -844,19 +845,19 @@ def test_length(self, temporal, expected): def test_cumulative_length(self, temporal, expected): assert temporal.cumulative_length() == expected - # @pytest.mark.parametrize( - # 'temporal, expected', - # [ - # (tpi, None), - # (tpds, None), - # (tps, TFloatSeq('Interp=Step;[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02]') / 3600 / 24), - # (tpss, TFloatSeqSet('Interp=Step;{[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02],' - # '[0@2019-09-03, 0@2019-09-05]}') / 3600 / 24), - # ], - # ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - # ) - # def test_speed(self, temporal, expected): - # assert temporal.speed() == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, None), + (tpds, None), + (tps, TFloatSeq('Interp=Step;[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02]') / 3600 / 24), + (tpss, TFloatSeqSet('Interp=Step;{[1.4142135623730951@2019-09-01, 1.4142135623730951@2019-09-02],' + '[0@2019-09-03, 0@2019-09-05]}') / 3600 / 24), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_speed(self, temporal, expected): + assert temporal.speed() == expected @pytest.mark.parametrize( 'temporal, expected', @@ -1011,9 +1012,9 @@ def test_angular_difference(self, temporal, expected): (tpi, Point(1,1)), (tpds, Point(1.5,1.5)), (tps, Point(1.5,1.5)), - # (tpss, Point(1.167,1.167)), + (tpss, Point(1.166666666666667,1.166666666666667)), ], - ids=['Instant', 'Discrete Sequence', 'Sequence'] #, 'SequenceSet'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_time_weighted_centroid(self, temporal, expected): assert temporal.time_weighted_centroid() == expected @@ -1927,14 +1928,27 @@ class TestTGeomPointEverSpatialOperations(TestTGeomPoint): ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_temporal_ever_contained_withindist_intersects_touches(self, temporal, expected): + def test_temporal_ever_contained_withindist_intersects(self, temporal, expected): assert temporal.is_ever_contained_in(Polygon([(0,0),(0,2),(2,2),(2,0),(0,0)])) == expected assert temporal.is_ever_contained_in(STBox('STBOX X((0,0),(2,2))')) == expected assert temporal.is_ever_within_distance(Point(1,1), 1) == expected assert temporal.is_ever_within_distance(TGeomPointInst('Point(1 1)@2019-09-01'), 1) == expected assert temporal.ever_intersects(Point(1,1)) == expected assert temporal.ever_intersects(TGeomPointInst('Point(1 1)@2019-09-01')) == expected - # assert temporal.ever_touches(Point(1,1)) == expected + + # Verify that these results are correct wrt lifting the 9DEM definition of touches + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, False), + (tpds, False), + (tps, False), + (tpss, False), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_temporal_ever_touches(self, temporal, expected): + assert temporal.ever_touches(Point(1,1)) == expected @pytest.mark.parametrize( 'temporal, expected', @@ -1968,10 +1982,24 @@ class TestTGeomPointTemporalSpatialOperations(TestTGeomPoint): ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_temporal_intersects_disjoint_touches(self, temporal, expected): + def test_temporal_intersects_disjoint(self, temporal, expected): assert temporal.intersects(Point(1,1)) == expected assert temporal.disjoint(Point(1,1)) == ~expected - # assert temporal.touches(Point(1,1)) == expected + + # Verify that these results are correct wrt lifting the 9DEM definition of touches + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, TBoolInst('False@2019-09-01')), + (tpds, TBoolSeq('{False@2019-09-01, False@2019-09-02}')), + (tps, TBoolSeqSet('[False@2019-09-01, False@2019-09-02]')), + (tpss, TBoolSeqSet('{[False@2019-09-01, False@2019-09-02],' + '[False@2019-09-03, False@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_temporal_touches(self, temporal, expected): + assert temporal.touches(Point(1,1)) == expected @pytest.mark.parametrize( 'temporal, argument, expected', diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index abf01fce..ae376e97 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -147,6 +147,7 @@ def test_instant_list_sequence_constructor(self, list, interpolation, normalize, ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_from_as_constructor(self, temporal): + assert temporal == temporal.__class__(str(temporal)) assert temporal == temporal.from_wkb(temporal.as_wkb()) assert temporal == temporal.from_hexwkb(temporal.as_hexwkb()) assert temporal == temporal.from_mfjson(temporal.as_mfjson()) @@ -1491,7 +1492,6 @@ def test_minus_max(self, temporal, expected): def test_minus_min(self, temporal, expected): assert temporal.minus_min() == expected - # TODO ADD the tests that are currently commented out in at and minus @pytest.mark.parametrize( 'temporal, restrictor', [ @@ -1501,6 +1501,9 @@ def test_minus_min(self, temporal, expected): (tii, period_set), (tii, 1), (tii, 2), + (tii, intrange(1, 1, True, True)), + (tii, [1,2]), + # (tii, [intrange(1, 1, True, True), intrange(2, 2, True, True)]), (tids, timestamp), (tids, timestamp_set), @@ -1508,6 +1511,9 @@ def test_minus_min(self, temporal, expected): (tids, period_set), (tids, 1), (tids, 2), + (tids, intrange(1, 1, True, True)), + (tids, [1,2]), + # (tds, [intrange(1, 1, True, True), intrange(2, 2, True, True)]), (tis, timestamp), (tis, timestamp_set), @@ -1515,6 +1521,9 @@ def test_minus_min(self, temporal, expected): (tis, period_set), (tis, 1), (tis, 2), + (tis, intrange(1, 1, True, True)), + (tis, [1,2]), + # (tis, [intrange(1, 1, True, True), intrange(2, 2, True, True)]), (tiss, timestamp), (tiss, timestamp_set), @@ -1522,15 +1531,25 @@ def test_minus_min(self, temporal, expected): (tiss, period_set), (tiss, 1), (tiss, 2), - + (tiss, intrange(1, 1, True, True)), + (tiss, [1,2]), + # (tiss, [intrange(1, 1, True, True), intrange(2, 2, True, True)]), ], - ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', 'Instant-PeriodSet', 'Instant-1', - 'Instant-2', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', + ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', + 'Instant-PeriodSet', 'Instant-1', 'Instant-2', + 'Instant-Range', 'Instant-[1,2]', # 'Instant-RangeSet', + 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', 'Discrete Sequence-1', - 'Discrete Sequence-2', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', 'Sequence-1', 'Sequence-2', 'SequenceSet-Timestamp', - 'SequenceSet-TimestampSet', 'SequenceSet-Period', 'SequenceSet-PeriodSet', 'SequenceSet-1', - 'SequenceSet-2'] + 'Discrete Sequence-2', 'Discrete Sequence-Range', + 'Discrete Sequence-[1,2]', # 'Discrete Sequence-RangeSet', + 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', + 'Sequence-PeriodSet', 'Sequence-1', 'Sequence-2', + 'Sequence-Range', 'Sequence-[1,2]', # 'Sequence-RangeSet', + 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', + 'SequenceSet-Period', 'SequenceSet-PeriodSet', + 'SequenceSet-1', 'SequenceSet-2', + 'SequenceSet-Range', 'SequenceSet-[1,2]', # 'SequenceSet-RangeSet', + ] ) def test_at_minus(self, temporal, restrictor): assert TInt.merge(temporal.at(restrictor), temporal.minus(restrictor)) == temporal diff --git a/pymeos/tests/main/ttext_test.py b/pymeos/tests/main/ttext_test.py index 3bba2f95..67b5fca2 100644 --- a/pymeos/tests/main/ttext_test.py +++ b/pymeos/tests/main/ttext_test.py @@ -141,6 +141,7 @@ def test_instant_list_sequence_constructor(self, list, interpolation, normalize, ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_from_as_constructor(self, temporal): + assert temporal == temporal.__class__(str(temporal)) assert temporal == temporal.from_wkb(temporal.as_wkb()) assert temporal == temporal.from_hexwkb(temporal.as_hexwkb()) assert temporal == temporal.from_mfjson(temporal.as_mfjson()) diff --git a/pymeos/tests/time/period_test.py b/pymeos/tests/time/period_test.py index e8c51966..fe7c488b 100644 --- a/pymeos/tests/time/period_test.py +++ b/pymeos/tests/time/period_test.py @@ -82,6 +82,7 @@ def test_hexwkb_constructor(self): datetime(2019, 9, 10, tzinfo=timezone.utc), False, False) def test_from_as_constructor(self): + assert self.period == Period(str(self.period)) assert self.period == Period.from_wkb(self.period.as_wkb()) assert self.period == Period.from_hexwkb(self.period.as_hexwkb()) @@ -181,11 +182,6 @@ def test_shift_tscale(self): self.assert_period_equality(shifted_scaled, datetime(2019, 9, 12, 0, tzinfo=timezone.utc), datetime(2019, 9, 12, 4, tzinfo=timezone.utc), False, False) - # def test_expand(self): - # expanded = self.period.expand(Period('(2021-01-01 00:00:00+0, 2021-02-01 00:00:00+0)')) - # self.assert_period_equality(expanded, datetime(2019, 9, 8, tzinfo=timezone.utc), - # datetime(2021, 2, 1, tzinfo=timezone.utc), False, False) - class TestPeriodTopologicalPositionFunctions(TestPeriod): period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') diff --git a/pymeos/tests/time/periodset_test.py b/pymeos/tests/time/periodset_test.py index e85fc4b2..e0dee8d7 100644 --- a/pymeos/tests/time/periodset_test.py +++ b/pymeos/tests/time/periodset_test.py @@ -36,6 +36,7 @@ def test_period_list_constructor(self): ]) def test_from_as_constructor(self): + assert self.periodset == PeriodSet(str(self.periodset)) assert self.periodset == PeriodSet.from_wkb(self.periodset.as_wkb()) assert self.periodset == PeriodSet.from_hexwkb(self.periodset.as_hexwkb()) diff --git a/pymeos/tests/time/timestampset_test.py b/pymeos/tests/time/timestampset_test.py index e91e0abd..19d1435b 100644 --- a/pymeos/tests/time/timestampset_test.py +++ b/pymeos/tests/time/timestampset_test.py @@ -40,6 +40,7 @@ def test_hexwkb_constructor(self): datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) def test_from_as_constructor(self): + assert self.ts_set == TimestampSet(str(self.ts_set)) assert self.ts_set == TimestampSet.from_wkb(self.ts_set.as_wkb()) assert self.ts_set == TimestampSet.from_hexwkb(self.ts_set.as_hexwkb()) From 2fd46cf938d4da0f198acdb9b9eddd2e8bbb0f1c Mon Sep 17 00:00:00 2001 From: Diviloper Date: Sun, 10 Sep 2023 12:47:16 +0200 Subject: [PATCH 022/101] Finish collection abstraction --- pymeos/pymeos/__init__.py | 3 +- pymeos/pymeos/collections/base/collection.py | 59 ++ pymeos/pymeos/collections/base/set.py | 12 +- pymeos/pymeos/collections/base/span.py | 6 +- pymeos/pymeos/collections/base/spanset.py | 704 +++++++++++++- pymeos/pymeos/collections/time/period.py | 10 +- pymeos/pymeos/collections/time/periodset.py | 857 +++++++++++++++++- pymeos/pymeos/collections/time/time.py | 4 +- .../collections/time/time_collection.py | 22 + .../pymeos/collections/time/timestampset.py | 3 +- 10 files changed, 1656 insertions(+), 24 deletions(-) create mode 100644 pymeos/pymeos/collections/base/collection.py create mode 100644 pymeos/pymeos/collections/time/time_collection.py diff --git a/pymeos/pymeos/__init__.py b/pymeos/pymeos/__init__.py index 98b1a345..0fd9c980 100644 --- a/pymeos/pymeos/__init__.py +++ b/pymeos/pymeos/__init__.py @@ -3,8 +3,7 @@ from .main import * from .meos_init import * from .temporal import * -from .time import PeriodSet, TimestampSet, Time -from .collections import Period +from .collections import Period, PeriodSet, TimestampSet, Time __version__ = '1.1.3a1' __all__ = [ diff --git a/pymeos/pymeos/collections/base/collection.py b/pymeos/pymeos/collections/base/collection.py new file mode 100644 index 00000000..e078fa8c --- /dev/null +++ b/pymeos/pymeos/collections/base/collection.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from datetime import datetime, timedelta +from typing import Generic, TypeVar, Type, Callable, Any, TYPE_CHECKING, Iterable +from typing import Optional, Union, overload, get_args, List + +from pymeos_cffi import * + +if TYPE_CHECKING: + from .spanset import SpanSet + from .span import Span + +T = TypeVar('T') +Self = TypeVar('Self', bound='Set[Any]') + + +class Collection(Generic[T], ABC): + # ------------------------- Topological Operations ------------------------ + @abstractmethod + def is_adjacent(self, other) -> bool: + raise NotImplementedError() + + @abstractmethod + def is_contained_in(self, container) -> bool: + raise NotImplementedError() + + @abstractmethod + def contains(self, content) -> bool: + raise NotImplementedError() + + @abstractmethod + def __contains__(self, item): + raise NotImplementedError() + + @abstractmethod + def overlaps(self, other) -> bool: + raise NotImplementedError() + + @abstractmethod + def is_same(self, other) -> bool: + raise NotImplementedError() + + # ------------------------- Position Operations --------------------------- + @abstractmethod + def is_left(self, other) -> bool: + raise NotImplementedError() + + @abstractmethod + def is_over_or_left(self, other) -> bool: + raise NotImplementedError() + + @abstractmethod + def is_over_or_right(self, other) -> bool: + raise NotImplementedError() + + @abstractmethod + def is_right(self, other) -> bool: + raise NotImplementedError() diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py index 74b7506b..feabd6da 100644 --- a/pymeos/pymeos/collections/base/set.py +++ b/pymeos/pymeos/collections/base/set.py @@ -1,12 +1,14 @@ from __future__ import annotations from abc import ABC, abstractmethod -from datetime import datetime, timedelta -from typing import Generic, TypeVar, Type, Callable, Any, TYPE_CHECKING, Iterable -from typing import Optional, Union, overload, get_args, List +from datetime import datetime +from typing import Optional, Union, List +from typing import TypeVar, Type, Callable, Any, TYPE_CHECKING, Iterable from pymeos_cffi import * +from .collection import Collection + if TYPE_CHECKING: from .spanset import SpanSet from .span import Span @@ -15,7 +17,7 @@ Self = TypeVar('Self', bound='Set[Any]') -class Set(Generic[T], ABC): +class Set(Collection[T], ABC): """ Base class for all set classes. """ @@ -698,4 +700,4 @@ def __ge__(self, other): """ if isinstance(other, self.__class__): return set_ge(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') \ No newline at end of file + raise TypeError(f'Operation not supported with type {other.__class__}') diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index 215404b1..4e241b66 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -7,6 +7,8 @@ from pymeos_cffi import * +from .collection import Collection + if TYPE_CHECKING: from .set import Set from .spanset import SpanSet @@ -15,7 +17,7 @@ Self = TypeVar('Self', bound='Span[Any]') -class Span(Generic[T], ABC): +class Span(Collection[T], ABC): """ Base class for all span classes. """ @@ -503,7 +505,7 @@ def intersection(self, other): from .set import Set from .spanset import SpanSet if isinstance(other, Set): - return intersection_spanset_span(set_to_spanset(other._inner), self._inner) + return self.intersection(set_to_spanset(other._inner)) elif isinstance(other, Span): return intersection_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): diff --git a/pymeos/pymeos/collections/base/spanset.py b/pymeos/pymeos/collections/base/spanset.py index 19759b53..2a8cf79f 100644 --- a/pymeos/pymeos/collections/base/spanset.py +++ b/pymeos/pymeos/collections/base/spanset.py @@ -1,19 +1,715 @@ from __future__ import annotations from abc import ABC, abstractmethod -from datetime import datetime, timedelta -from typing import Generic, TypeVar, Type, Callable, Any -from typing import Optional, Union, overload, get_args +from typing import Optional, Union +from typing import TypeVar, Type, Callable, Any, TYPE_CHECKING, List from pymeos_cffi import * +from .collection import Collection + +if TYPE_CHECKING: + from .set import Set + from .span import Span + T = TypeVar('T') Self = TypeVar('Self', bound='Span[Any]') -class SpanSet(Generic[T], ABC): +class SpanSet(Collection[T], ABC): """ Base class for all spanset classes. """ __slots__ = ['_inner'] + + _parse_function: Callable[[str], 'CData'] = None + _parse_value_function: Callable[[Union[str, T]], Any] = None + + # ------------------------- Constructors ---------------------------------- + def __init__(self, string: Optional[str] = None, *, period_list: Optional[List[Union[str, Span]]] = None, + normalize: bool = True, _inner=None): + super().__init__() + assert (_inner is not None) or ((string is not None) != (period_list is not None)), \ + "Either string must be not None or period_list must be not" + if _inner is not None: + self._inner = _inner + elif string is not None: + self._inner = self.__class__._parse_function(string) + else: + periods = [self.__class__._parse_value_function(p) for p in period_list] + self._inner = spanset_make(periods, normalize) + + def __copy__(self: Self) -> Self: + """ + Return a copy of ``self``. + + Returns: + A new :class:`SpanSet` instance + + MEOS Functions: + spanset_copy + """ + inner_copy = spanset_copy(self._inner) + return self.__class__(_inner=inner_copy) + + @classmethod + def from_wkb(cls: Type[Self], wkb: bytes) -> Self: + """ + Returns a `SpanSet` from its WKB representation. + + Args: + wkb: The WKB string. + + Returns: + A new :class:`SpanSet` instance + + MEOS Functions: + spanset_from_wkb + """ + result = spanset_from_wkb(wkb) + return cls(_inner=result) + + @classmethod + def from_hexwkb(cls: Type[Self], hexwkb: str) -> Self: + """ + Returns a `SpanSet` from its WKB representation in hex-encoded ASCII. + Args: + hexwkb: WKB representation in hex-encoded ASCII + + Returns: + A new :class:`SpanSet` instance + + MEOS Functions: + spanset_from_hexwkb + """ + result = spanset_from_hexwkb(hexwkb) + return cls(_inner=result) + + # ------------------------- Output ---------------------------------------- + @abstractmethod + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + """ + raise NotImplementedError() + + def __repr__(self): + """ + Return the string representation of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + periodset_out + """ + return (f'{self.__class__.__name__}' + f'({self})') + + def as_wkb(self) -> bytes: + """ + Returns the WKB representation of ``self``. + + Returns: + A :class:`str` object with the WKB representation of ``self``. + + MEOS Functions: + spanset_as_wkb + """ + return spanset_as_wkb(self._inner, 4) + + def as_hexwkb(self) -> str: + """ + Returns the WKB representation of ``self`` in hex-encoded ASCII. + Returns: + A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. + + MEOS Functions: + spanset_as_hexwkb + """ + return spanset_as_hexwkb(self._inner, -1)[0] + + # ------------------------- Conversions ----------------------------------- + @abstractmethod + def to_span(self) -> Span: + """ + Returns a span that encompasses ``self``. + + Returns: + A new :class:`Span` instance + + MEOS Functions: + spanset_span + """ + return spanset_span(self._inner) + + # ------------------------- Accessors ------------------------------------- + + @abstractmethod + def start_element(self) -> T: + raise NotImplementedError() + + @abstractmethod + def end_element(self) -> T: + raise NotImplementedError() + + @abstractmethod + def element_n(self, n: int) -> T: + raise NotImplementedError() + + @abstractmethod + def elements(self) -> List[T]: + raise NotImplementedError() + + def num_spans(self) -> int: + """ + Returns the number of spans in ``self``. + Returns: + An :class:`int` + + MEOS Functions: + spanset_num_spans + """ + return spanset_num_spans(self._inner) + + @abstractmethod + def start_span(self) -> Span: + """ + Returns the first span in ``self``. + Returns: + A :class:`Span` instance + + MEOS Functions: + spanset_start_span + """ + return spanset_start_span(self._inner) + + @abstractmethod + def end_span(self) -> Span: + """ + Returns the last span in ``self``. + Returns: + A :class:`Span` instance + + MEOS Functions: + spanset_end_span + """ + return spanset_end_span(self._inner) + + @abstractmethod + def span_n(self, n: int) -> Span: + """ + Returns the n-th span in ``self``. + Returns: + A :class:`Span` instance + + MEOS Functions: + spanset_span_n + """ + return spanset_span_n(self._inner, n + 1) + + @abstractmethod + def spans(self) -> List[Span]: + """ + Returns the list of periods in ``self``. + Returns: + A :class:`list[Period]` instance + + MEOS Functions: + spanset_spans + """ + return spanset_spans(self._inner) + + def __hash__(self) -> int: + """ + Return the hash representation of ``self``. + + Returns: + A new :class:`int` instance + + MEOS Functions: + spanset_hash + """ + return spanset_hash(self._inner) + + # ------------------------- Transformations ------------------------------- + + # ------------------------- Topological Operations ------------------------ + def is_adjacent(self, other) -> bool: + """ + Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them + contains it. + + Args: + other: object to compare with + + Returns: + True if adjacent, False otherwise + + MEOS Functions: + adjacent_spanset_span, adjacent_spanset_spanset + """ + from .set import Set + from .span import Span + if isinstance(other, Set): + return self.is_adjacent(other.to_spanset()) + elif isinstance(other, Span): + return adjacent_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return adjacent_spanset_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_contained_in(self, container) -> bool: + """ + Returns whether ``self`` is contained in ``container``. + + Args: + container: temporal object to compare with + + Returns: + True if contained, False otherwise + + MEOS Functions: + contained_spanset_span, contained_spanset_spanset + """ + from .set import Set + from .span import Span + if isinstance(container, Set): + return self.is_contained_in(container.to_spanset()) + elif isinstance(container, Span): + return contained_spanset_span(self._inner, container._inner) + elif isinstance(container, SpanSet): + return contained_spanset_spanset(self._inner, container._inner) + else: + raise TypeError(f'Operation not supported with type {container.__class__}') + + def contains(self, content) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_spanset_span, contains_spanset_spanset, + """ + from .set import Set + from .span import Span + if isinstance(content, Set): + return contains_spanset_spanset(self._inner, set_to_spanset(content._inner)) + elif isinstance(content, Span): + return contains_spanset_span(self._inner, content._inner) + elif isinstance(content, SpanSet): + return contains_spanset_spanset(self._inner, content._inner) + else: + raise TypeError(f'Operation not supported with type {content.__class__}') + + def __contains__(self, item): + """ + Returns whether ``self`` contains ``content``. + + Args: + item: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_spanset_span, contains_spanset_spanset + """ + return self.contains(item) + + def overlaps(self, other) -> bool: + """ + Returns whether ``self`` overlaps ``other``. That is, both share at least an instant + + Args: + other: temporal object to compare with + + Returns: + True if overlaps, False otherwise + + MEOS Functions: + overlaps_spanset_span, overlaps_spanset_spanset + """ + from .set import Set + from .span import Span + if isinstance(other, Set): + return self.overlaps(other.to_spanset()) + elif isinstance(other, Span): + return overlaps_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return overlaps_spanset_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_same(self, other) -> bool: + """ + Returns whether the bounding span of `self` is the same as the bounding span of `other`. + + Args: + other: A time or temporal object to compare to `self`. + + Returns: + True if same, False otherwise. + + See Also: + :meth:`Period.is_same` + """ + return self.to_span().is_same(other) + + # ------------------------- Position Operations --------------------------- + def is_left(self, other) -> bool: + """ + Returns whether ``self`` is strictly to the left of ``other``. That is, ``self`` ends before ``other`` starts. + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + before_periodset_timestamp, left_spanset_span, left_spanset_spanset + """ + from .set import Set + from .span import Span + if isinstance(other, Set): + return self.is_left(other.to_spanset()) + elif isinstance(other, Span): + return left_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return left_spanset_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_over_or_left(self, other) -> bool: + """ + Returns whether ``self`` is to the left of ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or + at the same time). + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overleft_spanset_span, overleft_spanset_spanset + """ + from .set import Set + from .span import Span + if isinstance(other, Set): + return overleft_spanset_span(self._inner, set_span(other._inner)) + elif isinstance(other, Span): + return overleft_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return overleft_spanset_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_over_or_right(self, other) -> bool: + """ + Returns whether ``self`` is to the right of ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts + (or at the same time). + + Args: + other: temporal object to compare with + + Returns: + True if overlapping or after, False otherwise + + MEOS Functions: + overright_spanset_span, overright_spanset_spanset + """ + from .set import Set + from .span import Span + if isinstance(other, Set): + return self.is_over_or_right(other.to_spanset()) + elif isinstance(other, Span): + return overright_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return overright_spanset_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def is_right(self, other) -> bool: + """ + Returns whether ``self`` is strictly to the right of ``other``.That is, ``self`` starts after ``other`` ends. + + Args: + other: temporal object to compare with + + Returns: + True if after, False otherwise + + MEOS Functions: + right_spanset_span, right_spanset_spanset + """ + from .set import Set + from .span import Span + if isinstance(other, Set): + return self.is_right(other.to_spanset()) + elif isinstance(other, Span): + return right_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return right_spanset_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Distance Operations --------------------------- + def distance(self, other) -> float: + """ + Returns the distance between ``self`` and ``other``. + + Args: + other: object to compare with + + Returns: + A :class:`float` instance + + MEOS Functions: + """ + from .set import Set + from .span import Span + if isinstance(other, Set): + return self.distance(other.to_spanset()) + elif isinstance(other, Span): + return distance_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return distance_spanset_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + # ------------------------- Set Operations -------------------------------- + @abstractmethod + def intersection(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: object to intersect with + + Returns: + A collection instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_spanset_spanset, intersection_spanset_span + """ + from .set import Set + from .span import Span + if isinstance(other, Set): + return intersection_spanset_spanset(self._inner, set_to_spanset(other._inner)) + elif isinstance(other, Span): + return intersection_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return intersection_spanset_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __mul__(self, other): + """ + Returns the temporal intersection of ``self`` and ``other``. + + Args: + other: temporal object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_periodset_timestamp, intersection_spanset_spanset, intersection_spanset_span + """ + return self.intersection(other) + + @abstractmethod + def minus(self, other): + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + minus_spanset_span, minus_spanset_spanset + """ + from .set import Set + from .span import Span + if isinstance(other, Set): + return self.minus(other.to_spanset()) + elif isinstance(other, Span): + return minus_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return minus_spanset_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __sub__(self, other): + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + minus_spanset_span, minus_spanset_spanset, minus_periodset_timestamp + """ + return self.minus(other) + + @abstractmethod + def union(self, other): + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + union_periodset_timestamp, union_spanset_spanset, union_spanset_span + """ + from .set import Set + from .span import Span + if isinstance(other, Set): + return self.union(other.to_spanset()) + elif isinstance(other, Span): + return union_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return union_spanset_spanset(self._inner, other._inner) + + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __add__(self, other): + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + union_periodset_timestamp, union_spanset_spanset, union_spanset_span + """ + return self.union(other) + + # ------------------------- Comparisons ----------------------------------- + def __eq__(self, other): + """ + Return whether ``self`` and ``other`` are equal. + + Args: + other: temporal object to compare with + + Returns: + True if equal, False otherwise + + MEOS Functions: + spanset_eq + """ + if isinstance(other, self.__class__): + return spanset_eq(self._inner, other._inner) + return False + + def __ne__(self, other): + """ + Return whether ``self`` and ``other`` are not equal. + + Args: + other: temporal object to compare with + + Returns: + True if not equal, False otherwise + + MEOS Functions: + spanset_ne + """ + if isinstance(other, self.__class__): + return spanset_ne(self._inner, other._inner) + return True + + def __lt__(self, other): + """ + Return whether ``self`` is less than ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if less than, False otherwise + + MEOS Functions: + spanset_lt + """ + if isinstance(other, self.__class__): + return spanset_lt(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __le__(self, other): + """ + Return whether ``self`` is less than or equal to ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if less than or equal, False otherwise + + MEOS Functions: + spanset_le + """ + if isinstance(other, self.__class__): + return spanset_le(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __gt__(self, other): + """ + Return whether ``self`` is greater than ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if greater than, False otherwise + + MEOS Functions: + spanset_gt + """ + if isinstance(other, self.__class__): + return spanset_gt(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __ge__(self, other): + """ + Return whether ``self`` is greater than or equal to ``other``. + + Args: + other: temporal object to compare with + + Returns: + True if greater than or equal, False otherwise + + MEOS Functions: + spanset_ge + """ + if isinstance(other, self.__class__): + return spanset_ge(self._inner, other._inner) + raise TypeError(f'Operation not supported with type {other.__class__}') diff --git a/pymeos/pymeos/collections/time/period.py b/pymeos/pymeos/collections/time/period.py index 95b83817..5d7b51bb 100644 --- a/pymeos/pymeos/collections/time/period.py +++ b/pymeos/pymeos/collections/time/period.py @@ -6,8 +6,8 @@ from dateutil.parser import parse from pymeos_cffi import * +from .time_collection import TimeCollection from ..base.span import Span -from ..base.spanset import SpanSet if TYPE_CHECKING: from ...temporal import Temporal @@ -17,7 +17,7 @@ from .timestampset import TimestampSet -class Period(Span[datetime]): +class Period(Span[datetime], TimeCollection): """ Class for representing sets of contiguous timestamps between a lower and an upper bound. The bounds may be inclusive or not. @@ -505,10 +505,10 @@ def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: """ from ...temporal import Temporal from ...boxes import Box - if isinstance(other, Temporal): - return self.distance(other.period()) - elif isinstance(other, datetime): + if isinstance(other, datetime): return timedelta(seconds=distance_period_timestamp(self._inner, datetime_to_timestamptz(other))) + elif isinstance(other, Temporal): + return self.distance(other.period()) elif isinstance(other, get_args(Box)): return self.distance(other.to_period()) else: diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py index 4552962a..a4f95b2c 100644 --- a/pymeos/pymeos/collections/time/periodset.py +++ b/pymeos/pymeos/collections/time/periodset.py @@ -1,7 +1,858 @@ -from datetime import datetime +from __future__ import annotations + +from datetime import timedelta, datetime +from typing import Optional, Union, List, overload, get_args +from typing import TYPE_CHECKING + +from pymeos_cffi import * + +from .time_collection import TimeCollection + +if TYPE_CHECKING: + from ...temporal import Temporal + from ...boxes import Box + from .period import Period + from .timestampset import TimestampSet + from .time import Time from ..base.spanset import SpanSet -class PeriodSet(SpanSet[datetime]): - pass +class PeriodSet(SpanSet[datetime], TimeCollection): + """ + Class for representing lists of disjoint periods. + + ``PeriodSet`` objects can be created with a single argument of type string + as in MobilityDB. + + >>> PeriodSet(string='{[2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01], [2019-09-11 00:00:00+01, 2019-09-12 00:00:00+01]}') + + Another possibility is to give a list specifying the composing + periods, which can be instances of ``str`` or ``Period``. The composing + periods must be given in increasing order. + + >>> PeriodSet(period_list=['[2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01]', '[2019-09-11 00:00:00+01, 2019-09-12 00:00:00+01]']) + >>> PeriodSet(period_list=[Period('[2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01]'), Period('[2019-09-11 00:00:00+01, 2019-09-12 00:00:00+01]')]) + + """ + + __slots__ = ['_inner'] + + _parse_function = periodset_in + _parse_value_function = lambda period: period_in(period)[0] if isinstance(period, str) else period._inner[0] + + # ------------------------- Output ---------------------------------------- + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + periodset_out + """ + return periodset_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + + def to_span(self) -> Period: + """ + Returns a period that encompasses ``self``. + + Returns: + A new :class:`Period` instance + + MEOS Functions: + spanset_span + """ + from .period import Period + return Period(_inner=super().to_span()) + + def to_period(self) -> Period: + """ + Returns a period that encompasses ``self``. + + Returns: + A new :class:`Period` instance + + MEOS Functions: + spanset_span + """ + return self.to_span() + + # ------------------------- Accessors ------------------------------------- + def duration(self, ignore_gaps: Optional[bool] = False) -> timedelta: + """ + Returns the duration of the periodset. By default, i.e., when the + second argument is False, the function takes into account the gaps within, + i.e., returns the sum of the durations of the periods within. + Otherwise, the function returns the duration of the periodset ignoring + any gap, i.e., the duration from the lower bound of the first period to + the upper bound of the last period. + + Parameters: + ignore_gaps: Whether to take into account potential time gaps in the periodset. + + Returns: + A :class:`datetime.timedelta` instance representing the duration of the periodset + + MEOS Functions: + periodset_duration + """ + return interval_to_timedelta(periodset_duration(self._inner, ignore_gaps)) + + def num_timestamps(self) -> int: + """ + Returns the number of timestamps in ``self``. + Returns: + An :class:`int` + + MEOS Functions: + periodset_num_timestamps + """ + return periodset_num_timestamps(self._inner) + + def start_element(self) -> datetime: + """ + Returns the first timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + periodset_start_timestamp + """ + return timestamptz_to_datetime(periodset_start_timestamp(self._inner)) + + def start_timestamp(self) -> datetime: + """ + Returns the first timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + periodset_start_timestamp + """ + return self.start_element() + + def end_element(self) -> datetime: + """ + Returns the last timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + periodset_end_timestamp + """ + return timestamptz_to_datetime(periodset_end_timestamp(self._inner)) + + def end_timestamp(self) -> datetime: + """ + Returns the last timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + periodset_end_timestamp + """ + return self.end_element() + + def element_n(self, n: int) -> datetime: + """ + Returns the n-th timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + periodset_timestamp_n + """ + return timestamptz_to_datetime(periodset_timestamp_n(self._inner, n + 1)) + + def timestamp_n(self, n: int) -> datetime: + """ + Returns the n-th timestamp in ``self``. + Returns: + A :class:`datetime` instance + + MEOS Functions: + periodset_timestamp_n + """ + return self.element_n(n) + + def elements(self) -> List[datetime]: + """ + Returns the list of distinct timestamps in ``self``. + Returns: + A :class:`list[datetime]` instance + + MEOS Functions: + periodset_timestamps + """ + ts, count = periodset_timestamps(self._inner) + return [timestamptz_to_datetime(ts[i]) for i in range(count)] + + def timestamps(self) -> List[datetime]: + """ + Returns the list of distinct timestamps in ``self``. + Returns: + A :class:`list[datetime]` instance + + MEOS Functions: + periodset_timestamps + """ + return self.elements() + + def num_spans(self) -> int: + """ + Returns the number of periods in ``self``. + Returns: + An :class:`int` + + MEOS Functions: + spanset_num_spans + """ + return spanset_num_spans(self._inner) + + def num_periods(self) -> int: + """ + Returns the number of periods in ``self``. + Returns: + An :class:`int` + + MEOS Functions: + spanset_num_spans + """ + return self.num_spans() + + def start_span(self) -> Period: + """ + Returns the first period in ``self``. + Returns: + A :class:`Period` instance + + MEOS Functions: + spanset_start_span + """ + from .period import Period + return Period(_inner=super().start_span()) + + def start_period(self) -> Period: + """ + Returns the first period in ``self``. + Returns: + A :class:`Period` instance + + MEOS Functions: + spanset_start_span + """ + return self.start_span() + + def end_span(self) -> Period: + """ + Returns the last period in ``self``. + Returns: + A :class:`Period` instance + + MEOS Functions: + spanset_end_span + """ + from .period import Period + return Period(_inner=super().end_span()) + + def end_period(self) -> Period: + """ + Returns the last period in ``self``. + Returns: + A :class:`Period` instance + + MEOS Functions: + spanset_end_span + """ + return self.end_span() + + def span_n(self, n: int) -> Period: + """ + Returns the n-th period in ``self``. + Returns: + A :class:`Period` instance + + MEOS Functions: + spanset_span_n + """ + from .period import Period + return Period(_inner=super().span_n(n)) + + def period_n(self, n: int) -> Period: + """ + Returns the n-th period in ``self``. + Returns: + A :class:`Period` instance + + MEOS Functions: + spanset_span_n + """ + return self.span_n(n) + + def spans(self) -> List[Period]: + """ + Returns the list of periods in ``self``. + Returns: + A :class:`list[Period]` instance + + MEOS Functions: + spanset_spans + """ + from .period import Period + ps = super().spans() + return [Period(_inner=ps[i]) for i in range(self.num_spans())] + + def periods(self) -> List[Period]: + """ + Returns the list of periods in ``self``. + Returns: + A :class:`list[Period]` instance + + MEOS Functions: + spanset_spans + """ + return self.spans() + + # ------------------------- Transformations ------------------------------- + def shift(self, delta: timedelta) -> PeriodSet: + """ + Returns a new periodset that is the result of shifting ``self`` by ``delta`` + + Examples: + >>> Period('[2000-01-01, 2000-01-10]').shift(timedelta(days=2)) + >>> 'Period([2000-01-03 00:00:00+01, 2000-01-12 00:00:00+01])' + + Args: + delta: :class:`datetime.timedelta` instance to shift + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + periodset_shift_tscale + """ + return self.shift_tscale(shift=delta) + + def tscale(self, duration: timedelta) -> PeriodSet: + """ + Returns a new periodset that starts as ``self`` but has duration ``duration`` + + Examples: + >>> Period('[2000-01-01, 2000-01-10]').tscale(timedelta(days=2)) + >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' + + Args: + duration: :class:`datetime.timedelta` instance representing the duration of the new period + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + periodset_shift_tscale + """ + return self.shift_tscale(duration=duration) + + def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> PeriodSet: + """ + Returns a new periodset that starts at ``self`` shifted by ``shift`` and has duration ``duration`` + + Examples: + >>> Period('[2000-01-01, 2000-01-10]').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) + >>> 'Period([2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01])' + + Args: + shift: :class:`datetime.timedelta` instance to shift + duration: :class:`datetime.timedelta` instance representing the duration of the new period + + Returns: + A new :class:`PeriodSet` instance + + MEOS Functions: + periodset_shift_tscale + """ + assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' + ps = periodset_shift_tscale( + self._inner, + timedelta_to_interval(shift) if shift else None, + timedelta_to_interval(duration) if duration else None + ) + return PeriodSet(_inner=ps) + + # ------------------------- Topological Operations ------------------------ + def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them + contains it. + + Examples: + >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_adjacent(PeriodSet('{[2012-01-02, 2012-01-03]}')) + >>> True + >>> PeriodSet('{[2012-01-01, 2012-01-02]}').is_adjacent(PeriodSet('{[2012-01-02, 2012-01-03]}')) + >>> False # Both contain bound + >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_adjacent(PeriodSet('{[(2012-01-02, 2012-01-03]]}')) + >>> False # Neither contain bound + + Args: + other: temporal object to compare with + + Returns: + True if adjacent, False otherwise + + MEOS Functions: + adjacent_spanset_span, adjacent_spanset_spanset, adjacent_periodset_timestamp, + adjacent_periodset_timestampset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return adjacent_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + elif isinstance(other, Temporal): + return self.is_adjacent(other.period()) + elif isinstance(other, get_args(Box)): + return self.is_adjacent(other.to_period()) + else: + return super().is_adjacent(other) + + def is_contained_in(self, container: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is temporally contained in ``container``. + + Examples: + >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_contained_in(Period('{[2012-01-01, 2012-01-04]}')) + >>> True + >>> PeriodSet('{(2012-01-01, 2012-01-02)}').is_contained_in(Period('{[2012-01-01, 2012-01-02]}')) + >>> True + >>> PeriodSet('{[2012-01-01, 2012-01-02]}').is_contained_in(Period('{(2012-01-01, 2012-01-02)}')) + >>> False + + Args: + container: temporal object to compare with + + Returns: + True if contained, False otherwise + + MEOS Functions: + contained_spanset_span, contained_spanset_spanset, contained_periodset_temporal + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(container, datetime): + return contained_spanset_span(self._inner, timestamp_to_period(datetime_to_timestamptz(container))) + elif isinstance(container, Temporal): + return self.is_contained_in(container.period()) + elif isinstance(container, get_args(Box)): + return self.is_contained_in(container.to_period()) + else: + return super().is_contained_in(container) + + def contains(self, content: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` temporally contains ``content``. + + Examples: + >>> PeriodSet('{[2012-01-01, 2012-01-04]}').contains(PeriodSet('{[2012-01-02, 2012-01-03]}')) + >>> True + >>> PeriodSet('{[2012-01-01, 2012-01-02]}').contains(PeriodSet('{(2012-01-01, 2012-01-02)}')) + >>> True + >>> PeriodSet('{(2012-01-01, 2012-01-02)}').contains(PeriodSet('{[2012-01-01, 2012-01-02]}')) + >>> False + + Args: + content: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_spanset_span, contains_spanset_spanset, contains_periodset_timestamp + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(content, datetime): + return contains_periodset_timestamp(self._inner, datetime_to_timestamptz(content)) + elif isinstance(content, Temporal): + return self.contains(content.period()) + elif isinstance(content, get_args(Box)): + return self.contains(content.to_period()) + else: + return super().contains(content) + + def __contains__(self, item): + """ + Returns whether ``self`` temporally contains ``content``. + + Examples: + >>> PeriodSet('{[2012-01-01, 2012-01-04]}').contains(PeriodSet('{[2012-01-02, 2012-01-03]}')) + >>> True + >>> PeriodSet('{[2012-01-01, 2012-01-02]}').contains(PeriodSet('{(2012-01-01, 2012-01-02)}')) + >>> True + >>> PeriodSet('{(2012-01-01, 2012-01-02)}').contains(PeriodSet('{[2012-01-01, 2012-01-02]}')) + >>> False + + Args: + item: temporal object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_spanset_span, contains_spanset_spanset, contains_periodset_timestamp, + contains_periodset_timestampset, contains_periodset_temporal + """ + return self.contains(item) + + def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant + + Examples: + >>> PeriodSet('{[2012-01-01, 2012-01-02]}').overlaps(PeriodSet('{[2012-01-02, 2012-01-03]}')) + >>> True + >>> PeriodSet('{[2012-01-01, 2012-01-02)}').overlaps(PeriodSet('{[2012-01-02, 2012-01-03]}')) + >>> False + >>> PeriodSet('{[2012-01-01, 2012-01-02)}').overlaps(PeriodSet('{(2012-01-02, 2012-01-03]}')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if overlaps, False otherwise + + MEOS Functions: + overlaps_spanset_span, overlaps_spanset_spanset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return overlaps_spanset_span(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) + elif isinstance(other, Temporal): + return self.overlaps(other.period()) + elif isinstance(other, get_args(Box)): + return self.overlaps(other.to_period()) + else: + return super().overlaps(other) + + def is_same(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether the bounding period of `self` is the same as the bounding period of `other`. + + Args: + other: A time or temporal object to compare to `self`. + + Returns: + True if same, False otherwise. + + See Also: + :meth:`Period.is_same` + """ + return self.to_period().is_same(other) + + # ------------------------- Position Operations --------------------------- + def is_left(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + + Examples: + >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_left(PeriodSet('{[2012-01-02, 2012-01-03]}')) + >>> True + >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_left(PeriodSet('{(2012-01-02, 2012-01-03]}')) + >>> True + >>> PeriodSet('{[2012-01-01, 2012-01-02]}').is_left(PeriodSet('{[2012-01-02, 2012-01-03]}')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + before_periodset_timestamp, left_spanset_span, left_spanset_spanset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return before_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + elif isinstance(other, Temporal): + return self.is_left(other.period()) + elif isinstance(other, get_args(Box)): + return self.is_left(other.to_period()) + else: + return super().is_left(other) + + def is_over_or_left(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or + at the same time). + + Examples: + >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_over_or_left(PeriodSet('{[2012-01-02, 2012-01-03]}')) + >>> True + >>> PeriodSet('{[2012-01-01, 2012-01-02]}').is_over_or_left(PeriodSet('{[2012-01-02, 2012-01-03]}')) + >>> True + >>> PeriodSet('{[2012-01-03, 2012-01-05]}').is_over_or_left(PeriodSet('{[2012-01-01, 2012-01-04]}')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overleft_spanset_span, overleft_spanset_spanset, overbefore_periodset_timestamp, + overbefore_periodset_timestampset, overbefore_periodset_temporal + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return overbefore_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + elif isinstance(other, Temporal): + return self.is_over_or_left(other.period()) + elif isinstance(other, get_args(Box)): + return self.is_over_or_left(other.to_period()) + else: + return super().is_over_or_left(other) + + def is_over_or_right(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts + (or at the same time). + + Examples: + >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_over_or_right(PeriodSet('{[2012-01-01, 2012-01-02)}')) + >>> True + >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_over_or_right(PeriodSet('{[2012-01-01, 2012-01-02]}')) + >>> True + >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_over_or_right(PeriodSet('{[2012-01-01, 2012-01-03]}')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if overlapping or after, False otherwise + + MEOS Functions: + overright_spanset_span, overright_spanset_spanset, overafter_periodset_timestamp, + overafter_periodset_timestampset, + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return overafter_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + elif isinstance(other, Temporal): + return self.is_over_or_right(other.period()) + elif isinstance(other, get_args(Box)): + return self.is_over_or_right(other.to_period()) + else: + return super().is_over_or_right(other) + + def is_right(self, other: Union[Time, Box, Temporal]) -> bool: + """ + Returns whether ``self`` is strictly after ``other``.That is, ``self`` starts after ``other`` ends. + + Examples: + >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_right(PeriodSet('{[2012-01-01, 2012-01-02)}')) + >>> True + >>> PeriodSet('{(2012-01-02, 2012-01-03]}').is_right(PeriodSet('{[2012-01-01, 2012-01-02)}')) + >>> True + >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_right(PeriodSet('{[2012-01-01, 2012-01-02]}')) + >>> False + + Args: + other: temporal object to compare with + + Returns: + True if after, False otherwise + + MEOS Functions: + right_spanset_span, right_spanset_spanset, overbefore_timestamp_periodset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return overbefore_timestamp_periodset(datetime_to_timestamptz(other), self._inner) + elif isinstance(other, Temporal): + return self.is_right(other.period()) + elif isinstance(other, get_args(Box)): + return self.is_right(other.to_period()) + else: + return super().is_right(other) + + # ------------------------- Distance Operations --------------------------- + def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: + """ + Returns the temporal distance between ``self`` and ``other``. + + Args: + other: temporal object to compare with + + Returns: + A :class:`datetime.timedelta` instance + + MEOS Functions: + distance_periodset_period, distance_periodset_periodset, distance_periodset_timestamp, + distance_periodset_timestampset + """ + from ...temporal import Temporal + from ...boxes import Box + if isinstance(other, datetime): + return timedelta(seconds=distance_periodset_timestamp(self._inner, datetime_to_timestamptz(other))) + if isinstance(other, Temporal): + return self.distance(other.period()) + elif isinstance(other, get_args(Box)): + return self.distance(other.to_period()) + else: + return timedelta(seconds=super().distance(other)) + + # ------------------------- Set Operations -------------------------------- + @overload + def intersection(self, other: Period) -> PeriodSet: + ... + + @overload + def intersection(self, other: PeriodSet) -> PeriodSet: + ... + + @overload + def intersection(self, other: datetime) -> datetime: + ... + + @overload + def intersection(self, other: TimestampSet) -> TimestampSet: + ... + + def intersection(self, other: Time) -> Union[PeriodSet, datetime, TimestampSet]: + """ + Returns the temporal intersection of ``self`` and ``other``. + + Args: + other: temporal object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_periodset_timestamp, intersection_spanset_spanset, intersection_spanset_span + """ + from .period import Period + from .timestampset import TimestampSet + if isinstance(other, datetime): + result = intersection_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + return timestamptz_to_datetime(result) if result is not None else None + elif isinstance(other, TimestampSet): + result = super().intersection(other) + return TimestampSet(_inner=result) if result is not None else None + elif isinstance(other, Period): + result = super().intersection(other) + return PeriodSet(_inner=result) if result is not None else None + elif isinstance(other, PeriodSet): + result = super().intersection(other) + return PeriodSet(_inner=result) if result is not None else None + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __mul__(self, other): + """ + Returns the temporal intersection of ``self`` and ``other``. + + Args: + other: temporal object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_periodset_timestamp, intersection_spanset_spanset, intersection_spanset_span + """ + return self.intersection(other) + + def minus(self, other: Time) -> PeriodSet: + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + minus_spanset_span, minus_spanset_spanset, minus_periodset_timestamp + """ + if isinstance(other, datetime): + result = minus_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + else: + result = super().minus(other) + return PeriodSet(_inner=result) if result is not None else None + + def __sub__(self, other): + """ + Returns the temporal difference of ``self`` and ``other``. + + Args: + other: temporal object to diff with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + minus_spanset_span, minus_spanset_spanset, minus_periodset_timestamp + """ + return self.minus(other) + + def union(self, other: Time) -> PeriodSet: + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + union_periodset_timestamp, union_spanset_spanset, union_spanset_span + """ + if isinstance(other, datetime): + result = union_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + else: + result = super().union(other) + return PeriodSet(_inner=result) if result is not None else None + + def __add__(self, other): + """ + Returns the temporal union of ``self`` and ``other``. + + Args: + other: temporal object to merge with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + union_periodset_timestamp, union_spanset_spanset, union_spanset_span + """ + return self.union(other) + + # ------------------------- Plot Operations ------------------------------- + def plot(self, *args, **kwargs): + from ...plotters import TimePlotter + return TimePlotter.plot_periodset(self, *args, **kwargs) + + # ------------------------- Database Operations --------------------------- + @staticmethod + def read_from_cursor(value, _=None): + """ + Reads a :class:`PeriodSet` from a database cursor. Used when automatically loading objects from the database. + Users should use the class constructor instead. + """ + if not value: + return None + return PeriodSet(string=value) diff --git a/pymeos/pymeos/collections/time/time.py b/pymeos/pymeos/collections/time/time.py index 336a665d..c6ee788b 100644 --- a/pymeos/pymeos/collections/time/time.py +++ b/pymeos/pymeos/collections/time/time.py @@ -1,9 +1,9 @@ +from datetime import datetime from typing import Union from .period import Period from .periodset import PeriodSet from .timestampset import TimestampSet -from datetime import datetime Time = Union[datetime, TimestampSet, Period, PeriodSet] """ @@ -13,4 +13,4 @@ - :class:`~pymeos.time.timestampset.TimestampSet` for sets of timestamps - :class:`~pymeos.time.period.Period` for periods of time - :class:`~pymeos.time.periodset.PeriodSet` for sets of periods of time -""" \ No newline at end of file +""" diff --git a/pymeos/pymeos/collections/time/time_collection.py b/pymeos/pymeos/collections/time/time_collection.py new file mode 100644 index 00000000..c663db1c --- /dev/null +++ b/pymeos/pymeos/collections/time/time_collection.py @@ -0,0 +1,22 @@ +from abc import ABC, abstractmethod +from datetime import datetime +from typing import TypeVar + +from ..base.collection import Collection + +Self = TypeVar('Self', bound='Set[Any]') + + +class TimeCollection(Collection[datetime], ABC): + + def is_before(self, other) -> bool: + return self.is_left(other) + + def is_over_or_before(self, other) -> bool: + return self.is_over_or_left(other) + + def is_over_or_after(self, other) -> bool: + return self.is_over_or_right(other) + + def is_after(self, other) -> bool: + return self.is_right(other) diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py index ba934094..dc09f740 100644 --- a/pymeos/pymeos/collections/time/timestampset.py +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -6,6 +6,7 @@ from dateutil.parser import parse from pymeos_cffi import * +from .time_collection import TimeCollection from ..base import Set if TYPE_CHECKING: @@ -16,7 +17,7 @@ from ...boxes import Box -class TimestampSet(Set[datetime]): +class TimestampSet(Set[datetime], TimeCollection): """ Class for representing lists of distinct timestamp values. From 244e2fdc739842ee80bd4bd322f61c1418f8d9c7 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Sun, 10 Sep 2023 13:13:07 +0200 Subject: [PATCH 023/101] Fix issues --- pymeos/pymeos/aggregators/aggregator.py | 2 +- .../pymeos/aggregators/general_aggregators.py | 2 +- pymeos/pymeos/aggregators/time_aggregators.py | 2 +- pymeos/pymeos/boxes/stbox.py | 2 +- pymeos/pymeos/boxes/tbox.py | 2 +- pymeos/pymeos/collections/__init__.py | 2 +- pymeos/pymeos/collections/base/set.py | 3 ++- pymeos/pymeos/collections/base/span.py | 2 +- pymeos/pymeos/collections/base/spanset.py | 9 +++++++-- pymeos/pymeos/collections/time/__init__.py | 3 ++- pymeos/pymeos/collections/time/period.py | 16 ++++++++-------- pymeos/pymeos/collections/time/periodset.py | 14 +++++++++++++- pymeos/pymeos/collections/time/timestampset.py | 10 ++++++---- pymeos/pymeos/main/tbool.py | 2 +- pymeos/pymeos/main/tfloat.py | 2 +- pymeos/pymeos/main/tint.py | 2 +- pymeos/pymeos/main/tnumber.py | 2 +- pymeos/pymeos/main/tpoint.py | 2 +- pymeos/pymeos/main/ttext.py | 2 +- pymeos/pymeos/{time => old_time}/__init__.py | 0 pymeos/pymeos/{time => old_time}/period.py | 0 pymeos/pymeos/{time => old_time}/periodset.py | 0 pymeos/pymeos/{time => old_time}/time.py | 0 .../pymeos/{time => old_time}/timestampset.py | 0 pymeos/pymeos/plotters/time_plotter.py | 2 +- pymeos/pymeos/temporal/temporal.py | 2 +- pymeos/tests/time/timestampset_test.py | 18 +++++++++--------- 27 files changed, 62 insertions(+), 41 deletions(-) rename pymeos/pymeos/{time => old_time}/__init__.py (100%) rename pymeos/pymeos/{time => old_time}/period.py (100%) rename pymeos/pymeos/{time => old_time}/periodset.py (100%) rename pymeos/pymeos/{time => old_time}/time.py (100%) rename pymeos/pymeos/{time => old_time}/timestampset.py (100%) diff --git a/pymeos/pymeos/aggregators/aggregator.py b/pymeos/pymeos/aggregators/aggregator.py index 5d9f3d67..9f208018 100644 --- a/pymeos/pymeos/aggregators/aggregator.py +++ b/pymeos/pymeos/aggregators/aggregator.py @@ -9,7 +9,7 @@ from ..boxes import Box from ..factory import _TemporalFactory from ..temporal import Temporal -from ..time import Time +from ..collections import Time ResultType = TypeVar('ResultType', bound=Union[Temporal, Time, Box]) SourceType = TypeVar('SourceType', bound=Union[Temporal, Time, Box]) diff --git a/pymeos/pymeos/aggregators/general_aggregators.py b/pymeos/pymeos/aggregators/general_aggregators.py index 6bede171..80c68c29 100644 --- a/pymeos/pymeos/aggregators/general_aggregators.py +++ b/pymeos/pymeos/aggregators/general_aggregators.py @@ -7,7 +7,7 @@ from ..boxes import Box from ..main import TIntSeq, TIntSeqSet from ..temporal import Temporal, TInterpolation -from ..time import Time, TimestampSet, Period, PeriodSet +from ..collections import Time, TimestampSet, Period, PeriodSet class TemporalInstantCountAggregator(BaseAggregator[ diff --git a/pymeos/pymeos/aggregators/time_aggregators.py b/pymeos/pymeos/aggregators/time_aggregators.py index 27093dd5..6e8336b7 100644 --- a/pymeos/pymeos/aggregators/time_aggregators.py +++ b/pymeos/pymeos/aggregators/time_aggregators.py @@ -4,7 +4,7 @@ from pymeos_cffi import * from .aggregator import BaseAggregator -from ..time import TimestampSet, Period, PeriodSet +from ..collections import TimestampSet, Period, PeriodSet class TimeInstantaneousUnionAggregator(BaseAggregator[Union[datetime, TimestampSet], TimestampSet]): diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index c346eadd..ab7d41da 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -8,7 +8,7 @@ from ..main import TPoint from ..temporal import Temporal -from ..time import * +from ..collections import * Geometry = Union[pg.Geometry, shp.BaseGeometry] diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index dcc5a831..4bff0578 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -6,7 +6,7 @@ from spans import intrange, floatrange from ..main import TNumber -from ..time import * +from ..collections import * class TBox: diff --git a/pymeos/pymeos/collections/__init__.py b/pymeos/pymeos/collections/__init__.py index 9ba1196e..45d99bcf 100644 --- a/pymeos/pymeos/collections/__init__.py +++ b/pymeos/pymeos/collections/__init__.py @@ -3,5 +3,5 @@ __all__ = [ 'Set', 'Span', 'SpanSet', - 'Time', 'TimestampSet', 'Period', 'PeriodSet', + 'Time', 'TimestampSet', 'Period', 'PeriodSet', 'datetime', 'timedelta' ] diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py index feabd6da..aecf8e79 100644 --- a/pymeos/pymeos/collections/base/set.py +++ b/pymeos/pymeos/collections/base/set.py @@ -208,7 +208,8 @@ def element_n(self, n: int) -> T: MEOS Functions: timestampset_timestamp_n """ - raise NotImplementedError() + if n < 0 or n >= self.num_elements(): + raise IndexError(f'Index {n} out of bounds') @abstractmethod def elements(self) -> List[T]: diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index 4e241b66..0151b529 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -505,7 +505,7 @@ def intersection(self, other): from .set import Set from .spanset import SpanSet if isinstance(other, Set): - return self.intersection(set_to_spanset(other._inner)) + return intersection_spanset_span(set_to_spanset(other._inner), self._inner) elif isinstance(other, Span): return intersection_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): diff --git a/pymeos/pymeos/collections/base/spanset.py b/pymeos/pymeos/collections/base/spanset.py index 2a8cf79f..d6b5abc9 100644 --- a/pymeos/pymeos/collections/base/spanset.py +++ b/pymeos/pymeos/collections/base/spanset.py @@ -159,12 +159,17 @@ def end_element(self) -> T: @abstractmethod def element_n(self, n: int) -> T: - raise NotImplementedError() + if n < 0 or n >= self.num_elements(): + raise IndexError(f'Index {n} out of bounds') @abstractmethod def elements(self) -> List[T]: raise NotImplementedError() + @abstractmethod + def num_elements(self) -> int: + raise NotImplementedError() + def num_spans(self) -> int: """ Returns the number of spans in ``self``. @@ -481,7 +486,7 @@ def distance(self, other) -> float: from .set import Set from .span import Span if isinstance(other, Set): - return self.distance(other.to_spanset()) + return distance_spanset_spanset(self._inner, set_to_spanset(other._inner)) elif isinstance(other, Span): return distance_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): diff --git a/pymeos/pymeos/collections/time/__init__.py b/pymeos/pymeos/collections/time/__init__.py index 83534d48..433fc4de 100644 --- a/pymeos/pymeos/collections/time/__init__.py +++ b/pymeos/pymeos/collections/time/__init__.py @@ -2,5 +2,6 @@ from .period import Period from .periodset import PeriodSet from .time import Time +from datetime import datetime, timedelta -__all__ = ['Time', 'TimestampSet', 'Period', 'PeriodSet'] +__all__ = ['Time', 'TimestampSet', 'Period', 'PeriodSet', 'datetime', 'timedelta'] diff --git a/pymeos/pymeos/collections/time/period.py b/pymeos/pymeos/collections/time/period.py index 5d7b51bb..9224d5a6 100644 --- a/pymeos/pymeos/collections/time/period.py +++ b/pymeos/pymeos/collections/time/period.py @@ -262,7 +262,7 @@ def is_contained_in(self, container: Union[Period, PeriodSet, Box, Temporal]) -> elif isinstance(container, Box): return self.is_contained_in(container.to_period()) else: - super().is_contained_in(container) + return super().is_contained_in(container) def contains(self, content: Union[Time, Box, Temporal]) -> bool: """ @@ -295,7 +295,7 @@ def contains(self, content: Union[Time, Box, Temporal]) -> bool: elif isinstance(content, get_args(Box)): return self.contains(content.to_period()) else: - super().contains(content) + return super().contains(content) def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: """ @@ -328,7 +328,7 @@ def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, get_args(Box)): return self.overlaps(other.to_period()) else: - super().overlaps(other) + return super().overlaps(other) def is_same(self, other: Union[Time, Box, Temporal]) -> bool: """ @@ -352,7 +352,7 @@ def is_same(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, datetime): return span_eq(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) else: - super().is_same(other) + return super().is_same(other) # ------------------------- Position Operations --------------------------- def is_left(self, other: Union[Time, Box, Temporal]) -> bool: @@ -386,7 +386,7 @@ def is_left(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, get_args(Box)): return self.is_left(other.to_period()) else: - super().is_left(other) + return super().is_left(other) def is_over_or_left(self, other: Union[Time, Box, Temporal]) -> bool: """ @@ -420,7 +420,7 @@ def is_over_or_left(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, get_args(Box)): return self.is_over_or_left(other.to_period()) else: - super().is_over_or_left(other) + return super().is_over_or_left(other) def is_right(self, other: Union[Time, Box, Temporal]) -> bool: """ @@ -453,7 +453,7 @@ def is_right(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, get_args(Box)): return self.is_right(other.to_period()) else: - super().is_right(other) + return super().is_right(other) def is_over_or_right(self, other: Union[Time, Box, Temporal]) -> bool: """ @@ -487,7 +487,7 @@ def is_over_or_right(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, get_args(Box)): return self.is_over_or_right(other.to_period()) else: - super().is_over_or_right(other) + return super().is_over_or_right(other) # ------------------------- Distance Operations --------------------------- def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py index a4f95b2c..abab4f48 100644 --- a/pymeos/pymeos/collections/time/periodset.py +++ b/pymeos/pymeos/collections/time/periodset.py @@ -102,7 +102,7 @@ def duration(self, ignore_gaps: Optional[bool] = False) -> timedelta: """ return interval_to_timedelta(periodset_duration(self._inner, ignore_gaps)) - def num_timestamps(self) -> int: + def num_elements(self) -> int: """ Returns the number of timestamps in ``self``. Returns: @@ -113,6 +113,17 @@ def num_timestamps(self) -> int: """ return periodset_num_timestamps(self._inner) + def num_timestamps(self) -> int: + """ + Returns the number of timestamps in ``self``. + Returns: + An :class:`int` + + MEOS Functions: + periodset_num_timestamps + """ + return self.num_elements() + def start_element(self) -> datetime: """ Returns the first timestamp in ``self``. @@ -166,6 +177,7 @@ def element_n(self, n: int) -> datetime: MEOS Functions: periodset_timestamp_n """ + super().element_n(n) return timestamptz_to_datetime(periodset_timestamp_n(self._inner, n + 1)) def timestamp_n(self, n: int) -> datetime: diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py index dc09f740..efe6d5eb 100644 --- a/pymeos/pymeos/collections/time/timestampset.py +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -92,6 +92,7 @@ def to_span(self) -> Period: MEOS Functions: set_span """ + from .period import Period return Period(_inner=super().to_span()) def to_period(self) -> Period: @@ -151,6 +152,7 @@ def element_n(self, n: int) -> datetime: MEOS Functions: timestampset_timestamp_n """ + super().element_n(n) return timestamptz_to_datetime(timestampset_timestamp_n(self._inner, n + 1)) def elements(self) -> List[datetime]: @@ -290,7 +292,7 @@ def is_contained_in(self, container: Union[Period, PeriodSet, TimestampSet, Temp elif isinstance(container, Box): return self.is_contained_in(container.to_period()) else: - super().is_contained_in(container) + return super().is_contained_in(container) def contains(self, content: Union[datetime, TimestampSet, Temporal]) -> bool: """ @@ -455,7 +457,7 @@ def is_over_or_left(self, other: Union[Time, Temporal, Box]) -> bool: elif isinstance(other, get_args(Box)): return self.to_period().is_over_or_left(other.to_period()) else: - super().is_over_or_left(other) + return super().is_over_or_left(other) def is_over_or_right(self, other: Union[Time, Temporal, Box]) -> bool: """ @@ -488,7 +490,7 @@ def is_over_or_right(self, other: Union[Time, Temporal, Box]) -> bool: elif isinstance(other, get_args(Box)): return self.to_period().is_over_or_right(other) else: - super().is_over_or_right(other) + return super().is_over_or_right(other) def is_right(self, other: Union[Time, Temporal, Box]) -> bool: """ @@ -593,7 +595,7 @@ def intersection(self, other: Union[Time, Temporal]) -> Optional[Time]: elif isinstance(other, get_args(Box)): return self.intersection(other.to_period()) else: - super().intersection(other) + return super().intersection(other) @overload def minus(self, other: Union[datetime, TimestampSet]) -> Optional[TimestampSet]: diff --git a/pymeos/pymeos/main/tbool.py b/pymeos/pymeos/main/tbool.py index f4b063dd..131088f1 100644 --- a/pymeos/pymeos/main/tbool.py +++ b/pymeos/pymeos/main/tbool.py @@ -7,7 +7,7 @@ from pymeos_cffi import * from ..temporal import TInterpolation, Temporal, TInstant, TSequence, TSequenceSet -from ..time import * +from ..collections import * class TBool(Temporal[bool, 'TBool', 'TBoolInst', 'TBoolSeq', 'TBoolSeqSet'], ABC): diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index 83ac82d4..c194d311 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -9,7 +9,7 @@ from .tnumber import TNumber from ..temporal import TInterpolation, Temporal, TInstant, TSequence, TSequenceSet -from ..time import * +from ..collections import * if TYPE_CHECKING: from ..boxes import TBox diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index 355365c2..6b2e77ff 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -9,7 +9,7 @@ from .tnumber import TNumber from ..temporal import TInterpolation, Temporal, TInstant, TSequence, TSequenceSet -from ..time import * +from ..collections import * if TYPE_CHECKING: from ..boxes import TBox diff --git a/pymeos/pymeos/main/tnumber.py b/pymeos/pymeos/main/tnumber.py index 0246f903..d6b3f32c 100644 --- a/pymeos/pymeos/main/tnumber.py +++ b/pymeos/pymeos/main/tnumber.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from ..boxes import Box, TBox - from ..time import Time + from ..collections import Time from .tint import TInt from .tfloat import TFloat diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index b43ddcab..d79a9066 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -13,7 +13,7 @@ from .tbool import TBool from .tfloat import TFloat, TFloatSeqSet from ..temporal import Temporal, TInstant, TSequence, TSequenceSet, TInterpolation -from ..time import * +from ..collections import * if TYPE_CHECKING: from ..boxes import STBox, Box diff --git a/pymeos/pymeos/main/ttext.py b/pymeos/pymeos/main/ttext.py index 1cd8831a..57e68a6d 100644 --- a/pymeos/pymeos/main/ttext.py +++ b/pymeos/pymeos/main/ttext.py @@ -7,7 +7,7 @@ from pymeos_cffi import * from ..temporal import TInterpolation, Temporal, TInstant, TSequence, TSequenceSet -from ..time import * +from ..collections import * if TYPE_CHECKING: from .tbool import TBool diff --git a/pymeos/pymeos/time/__init__.py b/pymeos/pymeos/old_time/__init__.py similarity index 100% rename from pymeos/pymeos/time/__init__.py rename to pymeos/pymeos/old_time/__init__.py diff --git a/pymeos/pymeos/time/period.py b/pymeos/pymeos/old_time/period.py similarity index 100% rename from pymeos/pymeos/time/period.py rename to pymeos/pymeos/old_time/period.py diff --git a/pymeos/pymeos/time/periodset.py b/pymeos/pymeos/old_time/periodset.py similarity index 100% rename from pymeos/pymeos/time/periodset.py rename to pymeos/pymeos/old_time/periodset.py diff --git a/pymeos/pymeos/time/time.py b/pymeos/pymeos/old_time/time.py similarity index 100% rename from pymeos/pymeos/time/time.py rename to pymeos/pymeos/old_time/time.py diff --git a/pymeos/pymeos/time/timestampset.py b/pymeos/pymeos/old_time/timestampset.py similarity index 100% rename from pymeos/pymeos/time/timestampset.py rename to pymeos/pymeos/old_time/timestampset.py diff --git a/pymeos/pymeos/plotters/time_plotter.py b/pymeos/pymeos/plotters/time_plotter.py index 16bd72bd..10ac6241 100644 --- a/pymeos/pymeos/plotters/time_plotter.py +++ b/pymeos/pymeos/plotters/time_plotter.py @@ -2,7 +2,7 @@ from matplotlib import pyplot as plt -from ..time import TimestampSet, Period, PeriodSet +from ..collections import TimestampSet, Period, PeriodSet class TimePlotter: diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index 54328586..b4961ee8 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -7,7 +7,7 @@ from pymeos_cffi import * from .interpolation import TInterpolation -from ..time import * +from ..collections import * if TYPE_CHECKING: from .tinstant import TInstant diff --git a/pymeos/tests/time/timestampset_test.py b/pymeos/tests/time/timestampset_test.py index e91e0abd..699e8f40 100644 --- a/pymeos/tests/time/timestampset_test.py +++ b/pymeos/tests/time/timestampset_test.py @@ -14,8 +14,8 @@ class TestTimestampSet(TestPyMEOS): @staticmethod def assert_timestampset_equality(ts_set: TimestampSet, timestamps: List[datetime]): - assert ts_set.num_timestamps() == len(timestamps) - assert ts_set.timestamps() == timestamps + assert ts_set.num_elements() == len(timestamps) + assert ts_set.elements() == timestamps class TestTimestampSetConstructors(TestTimestampSet): @@ -76,26 +76,26 @@ def test_duration(self): assert self.ts_set.duration() == timedelta(days=2) def test_period(self): - assert self.ts_set.period() == Period('[2019-09-01 00:00:00+00, 2019-09-03 00:00:00+00]') + assert self.ts_set.to_period() == Period('[2019-09-01 00:00:00+00, 2019-09-03 00:00:00+00]') def test_num_timestamps(self): - assert self.ts_set.num_timestamps() == 3 + assert self.ts_set.num_elements() == 3 def test_start_timestamp(self): - assert self.ts_set.start_timestamp() == datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc) + assert self.ts_set.start_element() == datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc) def test_end_timestamp(self): - assert self.ts_set.end_timestamp() == datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc) + assert self.ts_set.end_element() == datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc) def test_timestamp_n(self): - assert self.ts_set.timestamp_n(1) == datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc) + assert self.ts_set.element_n(1) == datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc) def test_timestamp_n_out_of_range(self): with pytest.raises(IndexError): - self.ts_set.timestamp_n(3) + self.ts_set.element_n(3) def test_timestamps(self): - assert self.ts_set.timestamps() == [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), + assert self.ts_set.elements() == [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc), ] From 30b0e945cb9cd146292d2307ec8157d56d065b05 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Tue, 12 Sep 2023 10:06:51 +0200 Subject: [PATCH 024/101] Synchronize with MEOS API --- pymeos/pymeos/boxes/stbox.py | 22 +-- pymeos/pymeos/boxes/tbox.py | 24 +-- pymeos/pymeos/temporal/temporal.py | 18 +- pymeos/pymeos/time/period.py | 20 +-- pymeos/pymeos/time/periodset.py | 20 +-- pymeos/pymeos/time/timestampset.py | 20 +-- pymeos/tests/boxes/stbox_test.py | 12 +- pymeos/tests/boxes/tbox_test.py | 12 +- pymeos/tests/main/tbool_test.py | 24 +-- pymeos/tests/main/tfloat_test.py | 12 +- pymeos/tests/main/tgeogpoint_test.py | 12 +- pymeos/tests/main/tgeompoint_test.py | 12 +- pymeos/tests/main/tint_test.py | 12 +- pymeos/tests/main/ttext_test.py | 12 +- pymeos/tests/time/period_test.py | 8 +- pymeos/tests/time/periodset_test.py | 8 +- pymeos/tests/time/timestampset_test.py | 8 +- pymeos_cffi/pymeos_cffi/__init__.py | 30 +++- .../builder/build_pymeos_functions.py | 25 ++- pymeos_cffi/pymeos_cffi/builder/meos.h | 30 +++- pymeos_cffi/pymeos_cffi/functions.py | 154 +++++++++++++++--- 21 files changed, 314 insertions(+), 181 deletions(-) diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index 4d08d44a..8f0ce607 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -595,7 +595,7 @@ def expand(self, other: Union[int, float, timedelta]) -> STBox: raise TypeError(f'Operation not supported with type {other.__class__}') return STBox(_inner=result) - def shift(self, delta: timedelta) -> STBox: + def shift_time(self, delta: timedelta) -> STBox: """ Returns a new `STBox` with the time dimension shifted by `delta`. @@ -606,14 +606,14 @@ def shift(self, delta: timedelta) -> STBox: A new :class:`STBox` instance MEOS Functions: - stbox_shift_tscale + stbox_shift_scale_time See Also: :meth:`STBox.shift` """ - return self.shift_tscale(shift=delta) + return self.shift_scale_time(shift=delta) - def tscale(self, duration: timedelta) -> STBox: + def scale_time(self, duration: timedelta) -> STBox: """ Returns a new `STBox` with the time dimension having duration `duration`. @@ -625,14 +625,14 @@ def tscale(self, duration: timedelta) -> STBox: A new :class:`STBox` instance MEOS Functions: - period_shift_tscale + period_shift_scale See Also: - :meth:`STBox.tscale` + :meth:`STBox.scale` """ - return self.shift_tscale(duration=duration) + return self.shift_scale_time(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, + def shift_scale_time(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> STBox: """ Returns a new `STBox` with the time dimension shifted by `shift` and @@ -646,14 +646,14 @@ def shift_tscale(self, shift: Optional[timedelta] = None, A new :class:`STBox` instance MEOS Functions: - stbox_shift_tscale + stbox_shift_scale_time See Also: - :meth:`Period.shift_tscale` + :meth:`Period.shift_scale` """ assert shift is not None or duration is not None, \ 'shift and scale deltas must not be both None' - result = stbox_shift_tscale( + result = stbox_shift_scale_time( self._inner, timedelta_to_interval(shift) if shift else None, timedelta_to_interval(duration) if duration else None diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index 216141b6..c7f76164 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -467,7 +467,7 @@ def expand(self, other: Union[int, float, timedelta]) -> TBox: raise TypeError(f'Operation not supported with type {other.__class__}') return TBox(_inner=result) - def shift(self, delta: timedelta) -> TBox: + def shift_time(self, delta: timedelta) -> TBox: """ Returns a new `TBox` with the time dimension shifted by `delta`. @@ -478,14 +478,14 @@ def shift(self, delta: timedelta) -> TBox: A new :class:`TBox` instance MEOS Functions: - period_shift_tscale + period_shift_scale See Also: :meth:`Period.shift` """ - return self.shift_tscale(shift=delta) + return self.shift_scale_time(shift=delta) - def tscale(self, duration: timedelta) -> TBox: + def scale_time(self, duration: timedelta) -> TBox: """ Returns a new `TBox` with the time dimension having duration `duration`. @@ -496,14 +496,14 @@ def tscale(self, duration: timedelta) -> TBox: A new :class:`TBox` instance MEOS Functions: - period_shift_tscale + period_shift_scale See Also: - :meth:`Period.tscale` + :meth:`Period.scale` """ - return self.shift_tscale(duration=duration) + return self.shift_scale_time(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, + def shift_scale_time(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TBox: """ Returns a new TBox with the temporal span shifted by `shift` and @@ -511,7 +511,7 @@ def shift_tscale(self, shift: Optional[timedelta] = None, Examples: >>> tbox = TBox('TBoxInt XT([0, 10),[2020-06-01, 2020-06-05])') - >>> tbox.shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) + >>> tbox.shift_scale_time(shift=timedelta(days=2), duration=timedelta(days=4)) >>> 'TBOXINT XT([0, 10),[2020-06-03 00:00:00+02, 2020-06-07 00:00:00+02])' Args: @@ -524,14 +524,14 @@ def shift_tscale(self, shift: Optional[timedelta] = None, A new :class:`TBox` instance MEOS Functions: - period_shift_tscale + period_shift_scale See Also: - :meth:`Period.shift_tscale` + :meth:`Period.shift_scale` """ assert shift is not None or duration is not None, \ 'shift and duration deltas must not be both None' - result = tbox_shift_tscale( + result = tbox_shift_scale_time( self._inner, timedelta_to_interval(shift) if shift else None, timedelta_to_interval(duration) if duration else None diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index ea2ce368..9e4e441d 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -495,7 +495,7 @@ def set_interpolation(self: Self, interpolation: TInterpolation) -> Self: new_temp = temporal_set_interp(self._inner, interpolation) return Temporal._factory(new_temp) - def shift(self, delta: timedelta) -> Period: + def shift_time(self, delta: timedelta) -> Period: """ Returns a new :class:`Temporal` with the temporal dimension shifted by ``delta``. @@ -504,12 +504,12 @@ def shift(self, delta: timedelta) -> Period: delta: :class:`datetime.timedelta` instance to shift MEOS Functions: - temporal_shift + temporal_shift_time """ - shifted = temporal_shift(self._inner, timedelta_to_interval(delta)) + shifted = temporal_shift_time(self._inner, timedelta_to_interval(delta)) return Temporal._factory(shifted) - def tscale(self, duration: timedelta) -> Period: + def scale_time(self, duration: timedelta) -> Period: """ Returns a new :class:`Temporal` scaled so the temporal dimension has duration ``duration``. @@ -519,12 +519,12 @@ def tscale(self, duration: timedelta) -> Period: duration of the new temporal MEOS Functions: - temporal_tscale + temporal_scale_time """ - scaled = temporal_tscale(self._inner, timedelta_to_interval(duration)) + scaled = temporal_scale_time(self._inner, timedelta_to_interval(duration)) return Temporal._factory(scaled) - def shift_tscale(self, shift: Optional[timedelta] = None, + def shift_scale_time(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> Self: """ Returns a new :class:`Temporal` with the time dimension shifted by @@ -537,11 +537,11 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration of the new temporal MEOS Functions: - temporal_shift_tscale + temporal_shift_scale_time """ assert shift is not None or duration is not None, \ 'shift and duration must not be both None' - scaled = temporal_shift_tscale( + scaled = temporal_shift_scale_time( self._inner, timedelta_to_interval(shift) if shift else None, timedelta_to_interval(duration) if duration else None diff --git a/pymeos/pymeos/time/period.py b/pymeos/pymeos/time/period.py index a7f1c382..38ead158 100644 --- a/pymeos/pymeos/time/period.py +++ b/pymeos/pymeos/time/period.py @@ -274,17 +274,17 @@ def shift(self, delta: timedelta) -> Period: A new :class:`Period` instance MEOS Functions: - period_shift_tscale + period_shift_scale """ - return self.shift_tscale(shift=delta) + return self.shift_scale(shift=delta) - def tscale(self, duration: timedelta) -> Period: + def scale(self, duration: timedelta) -> Period: """ Returns a new period that starts as ``self`` but has duration ``duration`` Examples: - >>> Period('[2000-01-01, 2000-01-10]').tscale(timedelta(days=2)) + >>> Period('[2000-01-01, 2000-01-10]').scale(timedelta(days=2)) >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' Args: @@ -295,18 +295,18 @@ def tscale(self, duration: timedelta) -> Period: A new :class:`Period` instance MEOS Functions: - period_shift_tscale + period_shift_scale """ - return self.shift_tscale(duration=duration) + return self.shift_scale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, + def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> Period: """ Returns a new period that starts at ``self`` shifted by ``shift`` and has duration ``duration`` Examples: - >>> Period('[2000-01-01, 2000-01-10]').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) + >>> Period('[2000-01-01, 2000-01-10]').shift_scale(shift=timedelta(days=2), duration=timedelta(days=4)) >>> 'Period([2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01])' Args: @@ -318,11 +318,11 @@ def shift_tscale(self, shift: Optional[timedelta] = None, A new :class:`Period` instance MEOS Functions: - period_shift_tscale + period_shift_scale """ assert shift is not None or duration is not None, \ 'shift and scale deltas must not be both None' - modified = period_shift_tscale( + modified = period_shift_scale( self._inner, timedelta_to_interval(shift) if shift else None, timedelta_to_interval(duration) if duration else None, diff --git a/pymeos/pymeos/time/periodset.py b/pymeos/pymeos/time/periodset.py index 7b20bf4b..37d49f9f 100644 --- a/pymeos/pymeos/time/periodset.py +++ b/pymeos/pymeos/time/periodset.py @@ -338,16 +338,16 @@ def shift(self, delta: timedelta) -> PeriodSet: A new :class:`PeriodSet` instance MEOS Functions: - periodset_shift_tscale + periodset_shift_scale """ - return self.shift_tscale(shift=delta) + return self.shift_scale(shift=delta) - def tscale(self, duration: timedelta) -> PeriodSet: + def scale(self, duration: timedelta) -> PeriodSet: """ Returns a new periodset that starts as ``self`` but has duration ``duration`` Examples: - >>> Period('[2000-01-01, 2000-01-10]').tscale(timedelta(days=2)) + >>> Period('[2000-01-01, 2000-01-10]').scale(timedelta(days=2)) >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' Args: @@ -358,18 +358,18 @@ def tscale(self, duration: timedelta) -> PeriodSet: A new :class:`PeriodSet` instance MEOS Functions: - periodset_shift_tscale + periodset_shift_scale """ - return self.shift_tscale(duration=duration) + return self.shift_scale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, + def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> PeriodSet: """ Returns a new periodset that starts at ``self`` shifted by ``shift`` and has duration ``duration`` Examples: - >>> Period('[2000-01-01, 2000-01-10]').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) + >>> Period('[2000-01-01, 2000-01-10]').shift_scale(shift=timedelta(days=2), duration=timedelta(days=4)) >>> 'Period([2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01])' Args: @@ -381,11 +381,11 @@ def shift_tscale(self, shift: Optional[timedelta] = None, A new :class:`PeriodSet` instance MEOS Functions: - periodset_shift_tscale + periodset_shift_scale """ assert shift is not None or duration is not None, \ 'shift and scale deltas must not be both None' - ps = periodset_shift_tscale( + ps = periodset_shift_scale( self._inner, timedelta_to_interval(shift) if shift else None, timedelta_to_interval(duration) if duration else None diff --git a/pymeos/pymeos/time/timestampset.py b/pymeos/pymeos/time/timestampset.py index 519f0505..c6b7582b 100644 --- a/pymeos/pymeos/time/timestampset.py +++ b/pymeos/pymeos/time/timestampset.py @@ -286,17 +286,17 @@ def shift(self, delta: timedelta) -> TimestampSet: A new :class:`PeriodSet` instance MEOS Functions: - timestampset_shift_tscale + timestampset_shift_scale """ - return self.shift_tscale(shift=delta) + return self.shift_scale(shift=delta) - def tscale(self, duration: timedelta) -> TimestampSet: + def scale(self, duration: timedelta) -> TimestampSet: """ Returns a new TimestampSet that with the scaled so that the span of ``self`` is ``duration``. Examples: - >>> TimestampSet('{2000-01-01, 2000-01-10}').tscale(timedelta(days=2)) + >>> TimestampSet('{2000-01-01, 2000-01-10}').scale(timedelta(days=2)) >>> 'TimestampSet({2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01})' Args: @@ -306,18 +306,18 @@ def tscale(self, duration: timedelta) -> TimestampSet: A new :class:`PeriodSet` instance MEOS Functions: - timestampset_shift_tscale + timestampset_shift_scale """ - return self.shift_tscale(duration=duration) + return self.shift_scale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, + def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TimestampSet: """ Returns a new TimestampSet that is the result of shifting and scaling ``self``. Examples: - >>> TimestampSet('{2000-01-01, 2000-01-10}').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) + >>> TimestampSet('{2000-01-01, 2000-01-10}').shift_scale(shift=timedelta(days=2), duration=timedelta(days=4)) >>> 'TimestampSet({2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01})' Args: @@ -329,11 +329,11 @@ def shift_tscale(self, shift: Optional[timedelta] = None, A new :class:`PeriodSet` instance MEOS Functions: - timestampset_shift_tscale + timestampset_shift_scale """ assert shift is not None or duration is not None, \ 'shift and scale deltas must not be both None' - tss = timestampset_shift_tscale( + tss = timestampset_shift_scale( self._inner, timedelta_to_interval(shift) if shift else None, timedelta_to_interval(duration) if duration else None diff --git a/pymeos/tests/boxes/stbox_test.py b/pymeos/tests/boxes/stbox_test.py index cca13a7f..acad9f3d 100644 --- a/pymeos/tests/boxes/stbox_test.py +++ b/pymeos/tests/boxes/stbox_test.py @@ -574,8 +574,8 @@ def test_expand_time(self, stbox, expected): ], ids=['positive days', 'negative days', 'positive hours', 'negative hours'] ) - def test_shift(self, stbox, delta, expected): - assert stbox.shift(delta) == expected + def test_shift_time(self, stbox, delta, expected): + assert stbox.shift_time(delta) == expected @pytest.mark.parametrize( 'stbox, delta, expected', @@ -586,11 +586,11 @@ def test_shift(self, stbox, delta, expected): ], ids=['positive days', 'positive hours'] ) - def test_tscale(self, stbox, delta, expected): - assert stbox.tscale(delta) == expected + def test_scale_time(self, stbox, delta, expected): + assert stbox.scale_time(delta) == expected - def test_shift_tscale(self): - assert self.stbt.shift_tscale(timedelta(days=4), timedelta(hours=4)) == \ + def test_shift_scale_time(self): + assert self.stbt.shift_scale_time(timedelta(days=4), timedelta(hours=4)) == \ STBox('STBOX T([2019-09-05,2019-09-05 04:00:00])') @pytest.mark.parametrize( diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index a1e65bf1..3e82a704 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -391,8 +391,8 @@ def test_expand_time(self, tbox, expected): ], ids=['positive days', 'negative days', 'positive hours', 'negative hours'] ) - def test_shift(self, tbox, delta, expected): - assert tbox.shift(delta) == expected + def test_shift_time(self, tbox, delta, expected): + assert tbox.shift_time(delta) == expected @pytest.mark.parametrize( 'tbox, delta, expected', @@ -403,11 +403,11 @@ def test_shift(self, tbox, delta, expected): ], ids=['positive days', 'positive hours'] ) - def test_tscale(self, tbox, delta, expected): - assert tbox.tscale(delta) == expected + def test_scale_time(self, tbox, delta, expected): + assert tbox.scale_time(delta) == expected - def test_shift_tscale(self): - assert self.tbt.shift_tscale(timedelta(days=4), timedelta(hours=4)) == \ + def test_shift_scale_time(self): + assert self.tbt.shift_scale_time(timedelta(days=4), timedelta(hours=4)) == \ TBox('TBOX T([2019-09-05,2019-09-05 04:00:00])') @pytest.mark.parametrize( diff --git a/pymeos/tests/main/tbool_test.py b/pymeos/tests/main/tbool_test.py index c8da98fc..d4605492 100644 --- a/pymeos/tests/main/tbool_test.py +++ b/pymeos/tests/main/tbool_test.py @@ -826,8 +826,8 @@ def test_to_sequenceset(self, temporal, interpolation, expected): 'Sequence Set positive days', 'Sequence Set negative days', 'Sequence Set positive hours', 'Sequence Set negative hours'] ) - def test_shift(self, tbool, delta, expected): - assert tbool.shift(delta) == expected + def test_shift_time(self, tbool, delta, expected): + assert tbool.shift_time(delta) == expected @pytest.mark.parametrize( 'tbool, delta, expected', @@ -848,11 +848,11 @@ def test_shift(self, tbool, delta, expected): 'Sequence positive days', 'Sequence positive hours', 'Sequence Set positive days', 'Sequence Set positive hours'] ) - def test_scale(self, tbool, delta, expected): - assert tbool.tscale(delta) == expected + def test_scale_time(self, tbool, delta, expected): + assert tbool.scale_time(delta) == expected - def test_shift_tscale(self): - assert self.tbss.shift_tscale(timedelta(days=4), timedelta(hours=2)) == \ + def test_shift_scale_time(self): + assert self.tbss.shift_scale_time(timedelta(days=4), timedelta(hours=2)) == \ TBoolSeqSet('{[True@2019-09-05 00:00:00, False@2019-09-05 00:30:00],' '[True@2019-09-05 01:00:00, True@2019-09-05 02:00:00]}') @@ -983,8 +983,8 @@ class TestTBoolManipulationFunctions(TestTBool): ids=['Instant positive', 'Discrete Sequence positive', 'Sequence positive', 'SequenceSet positive', 'Instant negative', 'Discrete Sequence negative', 'Sequence negative', 'SequenceSet negative'], ) - def test_shift(self, temporal, shift, expected): - assert temporal.shift(shift) == expected + def test_shift_time(self, temporal, shift, expected): + assert temporal.shift_time(shift) == expected @pytest.mark.parametrize( 'temporal, scale, expected', @@ -997,8 +997,8 @@ def test_shift(self, temporal, shift, expected): ], ids=['Instant positive', 'Discrete Sequence positive', 'Sequence positive', 'SequenceSet positive'], ) - def test_tscale(self, temporal, scale, expected): - assert temporal.tscale(scale) == expected + def test_scale_time(self, temporal, scale, expected): + assert temporal.scale_time(scale) == expected @pytest.mark.parametrize( 'temporal, shift, scale, expected', @@ -1017,8 +1017,8 @@ def test_tscale(self, temporal, scale, expected): ids=['Instant positive', 'Discrete Sequence positive', 'Sequence positive', 'SequenceSet positive', 'Instant negative', 'Discrete Sequence negative', 'Sequence negative', 'SequenceSet negative'], ) - def test_shift_tscale(self, temporal, shift, scale, expected): - assert temporal.shift_tscale(shift, scale) == expected + def test_shift_scale_time(self, temporal, shift, scale, expected): + assert temporal.shift_scale_time(shift, scale) == expected class TestTBoolRestrictors(TestTBool): diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index 1838676e..34850d91 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -992,8 +992,8 @@ def test_set_interpolation(self, temporal, interpolation, expected): 'Sequence Set positive days', 'Sequence Set negative days', 'Sequence Set positive hours', 'Sequence Set negative hours'] ) - def test_shift(self, tfloat, delta, expected): - assert tfloat.shift(delta) == expected + def test_shift_time(self, tfloat, delta, expected): + assert tfloat.shift_time(delta) == expected @pytest.mark.parametrize( 'tfloat, delta, expected', @@ -1014,11 +1014,11 @@ def test_shift(self, tfloat, delta, expected): 'Sequence positive days', 'Sequence positive hours', 'Sequence Set positive days', 'Sequence Set positive hours'] ) - def test_scale(self, tfloat, delta, expected): - assert tfloat.tscale(delta) == expected + def test_scale_time(self, tfloat, delta, expected): + assert tfloat.scale_time(delta) == expected - def test_shift_tscale(self): - assert self.tfss.shift_tscale(timedelta(days=4), timedelta(hours=2)) == \ + def test_shift_scale_time(self): + assert self.tfss.shift_scale_time(timedelta(days=4), timedelta(hours=2)) == \ TFloatSeqSet('{[1.5@2019-09-05 00:00:00, 2.5@2019-09-05 00:30:00],' '[1.5@2019-09-05 01:00:00, 1.5@2019-09-05 02:00:00]}') diff --git a/pymeos/tests/main/tgeogpoint_test.py b/pymeos/tests/main/tgeogpoint_test.py index 7187e545..4b232b1e 100644 --- a/pymeos/tests/main/tgeogpoint_test.py +++ b/pymeos/tests/main/tgeogpoint_test.py @@ -1174,8 +1174,8 @@ def test_set_interpolation(self, temporal, interpolation, expected): 'Sequence Set posi(tpve days', 'Sequence Set nega(tpve days', 'Sequence Set posi(tpve hours', 'Sequence Set nega(tpve hours'] ) - def test_shift(self, tpoint, delta, expected): - assert tpoint.shift(delta) == expected + def test_shift_time(self, tpoint, delta, expected): + assert tpoint.shift_time(delta) == expected @pytest.mark.parametrize( 'tpoint, delta, expected', @@ -1196,11 +1196,11 @@ def test_shift(self, tpoint, delta, expected): 'Sequence posi(tpve days', 'Sequence posi(tpve hours', 'Sequence Set posi(tpve days', 'Sequence Set posi(tpve hours'] ) - def test_scale(self, tpoint, delta, expected): - assert tpoint.tscale(delta) == expected + def test_scale_time(self, tpoint, delta, expected): + assert tpoint.scale_time(delta) == expected - def test_shift_tscale(self): - assert self.tpss.shift_tscale(timedelta(days=4), timedelta(hours=2)) == \ + def test_shift_scale_time(self): + assert self.tpss.shift_scale_time(timedelta(days=4), timedelta(hours=2)) == \ TGeogPointSeqSet('{[Point(1 1)@2019-09-05 00:00:00, Point(2 2)@2019-09-05 00:30:00],' '[Point(1 1)@2019-09-05 01:00:00, Point(1 1)@2019-09-05 02:00:00]}') diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index dc33cd8a..8592b2e3 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -1226,8 +1226,8 @@ def test_set_interpolation(self, temporal, interpolation, expected): 'Sequence Set posi(tpve days', 'Sequence Set nega(tpve days', 'Sequence Set posi(tpve hours', 'Sequence Set nega(tpve hours'] ) - def test_shift(self, tpoint, delta, expected): - assert tpoint.shift(delta) == expected + def test_shift_time(self, tpoint, delta, expected): + assert tpoint.shift_time(delta) == expected @pytest.mark.parametrize( 'tpoint, delta, expected', @@ -1248,11 +1248,11 @@ def test_shift(self, tpoint, delta, expected): 'Sequence posi(tpve days', 'Sequence posi(tpve hours', 'Sequence Set posi(tpve days', 'Sequence Set posi(tpve hours'] ) - def test_scale(self, tpoint, delta, expected): - assert tpoint.tscale(delta) == expected + def test_scale_time(self, tpoint, delta, expected): + assert tpoint.scale_time(delta) == expected - def test_shift_tscale(self): - assert self.tpss.shift_tscale(timedelta(days=4), timedelta(hours=2)) == \ + def test_shift_scale_time(self): + assert self.tpss.shift_scale_time(timedelta(days=4), timedelta(hours=2)) == \ TGeomPointSeqSet('{[Point(1 1)@2019-09-05 00:00:00, Point(2 2)@2019-09-05 00:30:00],' '[Point(1 1)@2019-09-05 01:00:00, Point(1 1)@2019-09-05 02:00:00]}') diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index ae376e97..6b1a33d8 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -949,8 +949,8 @@ def test_set_interpolation(self, temporal, interpolation, expected): 'Sequence Set positive days', 'Sequence Set negative days', 'Sequence Set positive hours', 'Sequence Set negative hours'] ) - def test_shift(self, tint, delta, expected): - assert tint.shift(delta) == expected + def test_shift_time(self, tint, delta, expected): + assert tint.shift_time(delta) == expected @pytest.mark.parametrize( 'tint, delta, expected', @@ -971,11 +971,11 @@ def test_shift(self, tint, delta, expected): 'Sequence positive days', 'Sequence positive hours', 'Sequence Set positive days', 'Sequence Set positive hours'] ) - def test_scale(self, tint, delta, expected): - assert tint.tscale(delta) == expected + def test_scale_time(self, tint, delta, expected): + assert tint.scale_time(delta) == expected - def test_shift_tscale(self): - assert self.tiss.shift_tscale(timedelta(days=4), timedelta(hours=2)) == \ + def test_shift_scale_time(self): + assert self.tiss.shift_scale_time(timedelta(days=4), timedelta(hours=2)) == \ TIntSeqSet('{[1@2019-09-05 00:00:00, 2@2019-09-05 00:30:00],' '[1@2019-09-05 01:00:00, 1@2019-09-05 02:00:00]}') diff --git a/pymeos/tests/main/ttext_test.py b/pymeos/tests/main/ttext_test.py index 67b5fca2..ea7d57c2 100644 --- a/pymeos/tests/main/ttext_test.py +++ b/pymeos/tests/main/ttext_test.py @@ -825,8 +825,8 @@ def test_to_sequenceset(self, temporal, interpolation, expected): 'Sequence Set positive days', 'Sequence Set negative days', 'Sequence Set positive hours', 'Sequence Set negative hours'] ) - def test_shift(self, ttext, delta, expected): - assert ttext.shift(delta) == expected + def test_shift_time(self, ttext, delta, expected): + assert ttext.shift_time(delta) == expected @pytest.mark.parametrize( 'ttext, delta, expected', @@ -847,11 +847,11 @@ def test_shift(self, ttext, delta, expected): 'Sequence positive days', 'Sequence positive hours', 'Sequence Set positive days', 'Sequence Set positive hours'] ) - def test_scale(self, ttext, delta, expected): - assert ttext.tscale(delta) == expected + def test_scale_time(self, ttext, delta, expected): + assert ttext.scale_time(delta) == expected - def test_shift_tscale(self): - assert self.ttss.shift_tscale(timedelta(days=4), timedelta(hours=2)) == \ + def test_shift_scale(self): + assert self.ttss.shift_scale_time(timedelta(days=4), timedelta(hours=2)) == \ TTextSeqSet('{[AAA@2019-09-05 00:00:00, BBB@2019-09-05 00:30:00],' '[AAA@2019-09-05 01:00:00, AAA@2019-09-05 02:00:00]}') diff --git a/pymeos/tests/time/period_test.py b/pymeos/tests/time/period_test.py index fe7c488b..33ca907f 100644 --- a/pymeos/tests/time/period_test.py +++ b/pymeos/tests/time/period_test.py @@ -173,12 +173,12 @@ def test_shift(self, delta, result): ], ids=['days', 'hours'] ) - def test_tscale(self, delta, result): - scaled = self.period.tscale(delta) + def test_scale(self, delta, result): + scaled = self.period.scale(delta) self.assert_period_equality(scaled, *result) - def test_shift_tscale(self): - shifted_scaled = self.period.shift_tscale(timedelta(days=4), timedelta(hours=4)) + def test_shift_scale(self): + shifted_scaled = self.period.shift_scale(timedelta(days=4), timedelta(hours=4)) self.assert_period_equality(shifted_scaled, datetime(2019, 9, 12, 0, tzinfo=timezone.utc), datetime(2019, 9, 12, 4, tzinfo=timezone.utc), False, False) diff --git a/pymeos/tests/time/periodset_test.py b/pymeos/tests/time/periodset_test.py index e0dee8d7..03417a28 100644 --- a/pymeos/tests/time/periodset_test.py +++ b/pymeos/tests/time/periodset_test.py @@ -153,12 +153,12 @@ def test_shift(self, delta, result): ], ids=['days', 'hours'] ) - def test_tscale(self, delta, result): - scaled = self.periodset.tscale(delta) + def test_scale(self, delta, result): + scaled = self.periodset.scale(delta) self.assert_periodset_equality(scaled, result) - def test_shift_tscale(self): - shifted_scaled = self.periodset.shift_tscale(timedelta(days=4), timedelta(hours=6)) + def test_shift_scale(self): + shifted_scaled = self.periodset.shift_scale(timedelta(days=4), timedelta(hours=6)) self.assert_periodset_equality(shifted_scaled, [Period('[2019-09-05 00:00:00+0, 2019-09-05 02:00:00+0]'), Period('[2019-09-05 04:00:00+0, 2019-09-05 06:00:00+0]')]) diff --git a/pymeos/tests/time/timestampset_test.py b/pymeos/tests/time/timestampset_test.py index 19d1435b..72af670c 100644 --- a/pymeos/tests/time/timestampset_test.py +++ b/pymeos/tests/time/timestampset_test.py @@ -316,12 +316,12 @@ def test_shift(self, delta, result): ], ids=['days', 'hours'] ) - def test_tscale(self, delta, result): - scaled = self.timestampset.tscale(delta) + def test_scale(self, delta, result): + scaled = self.timestampset.scale(delta) self.assert_timestampset_equality(scaled, result) - def test_shift_tscale(self): - shifted_scaled = self.timestampset.shift_tscale(timedelta(days=4), timedelta(hours=3)) + def test_shift_scale(self): + shifted_scaled = self.timestampset.shift_scale(timedelta(days=4), timedelta(hours=3)) self.assert_timestampset_equality(shifted_scaled, [datetime(2020, 1, 5, tzinfo=timezone.utc), datetime(2020, 1, 5, 1, tzinfo=timezone.utc), diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index f9272ef5..cb3bcde3 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -241,20 +241,26 @@ 'timestampset_start_timestamp', 'timestampset_timestamp_n', 'timestampset_values', + 'bigintset_shift_scale', 'floatset_round', + 'floatset_shift_scale', 'floatspan_intspan', 'floatspan_round', 'floatspanset_intspanset', 'floatspanset_round', 'geoset_round', + 'intset_shift_scale', 'intspan_floatspan', 'intspanset_floatspanset', - 'period_shift_tscale', + 'numset_shift_scale', + 'numspan_shift_scale', + 'numspanset_shift_scale', + 'period_shift_scale', 'period_tprecision', - 'periodset_shift_tscale', + 'periodset_shift_scale', 'periodset_tprecision', 'timestamp_tprecision', - 'timestampset_shift_tscale', + 'timestampset_shift_scale', 'adjacent_bigintspan_bigint', 'adjacent_bigintspanset_bigint', 'adjacent_floatspan_float', @@ -554,11 +560,13 @@ 'stbox_get_space', 'stbox_round', 'stbox_set_srid', - 'stbox_shift_tscale', + 'stbox_shift_scale_time', 'tbox_expand_value', 'tbox_expand_time', 'tbox_round', - 'tbox_shift_tscale', + 'tbox_shift_scale_int', + 'tbox_shift_scale_float', + 'tbox_shift_scale_time', 'contains_tbox_tbox', 'contained_tbox_tbox', 'overlaps_tbox_tbox', @@ -713,14 +721,20 @@ 'ttext_start_value', 'ttext_values', 'temporal_set_interp', - 'temporal_shift', - 'temporal_shift_tscale', + 'tfloat_scale_value', + 'tfloat_shift_scale_value', + 'tfloat_shift_value', + 'tint_scale_value', + 'tint_shift_scale_value', + 'tint_shift_value', + 'temporal_scale_time', + 'temporal_shift_scale_time', + 'temporal_shift_time', 'temporal_to_tinstant', 'temporal_to_tsequence', 'temporal_to_tsequenceset', 'temporal_tprecision', 'temporal_tsample', - 'temporal_tscale', 'tbool_at_value', 'tbool_minus_value', 'tbool_value_at_timestamp', diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index a912bc78..fffbc758 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -115,28 +115,27 @@ def __init__(self, ctype: str, ptype: str, conversion: Optional[str]) -> None: ('temporal_append_tinstant', 'maxt'), ('temporal_as_mfjson', 'srs'), ('gserialized_as_geojson', 'srs'), - ('period_shift_tscale', 'shift'), - ('period_shift_tscale', 'duration'), - ('timestampset_shift_tscale', 'shift'), - ('timestampset_shift_tscale', 'duration'), - ('periodset_shift_tscale', 'shift'), - ('periodset_shift_tscale', 'duration'), - ('temporal_shift_tscale', 'shift'), - ('temporal_shift_tscale', 'duration'), - ('temporal_shift_tscale', 'shift'), + ('period_shift_scale', 'shift'), + ('period_shift_scale', 'duration'), + ('timestampset_shift_scale', 'shift'), + ('timestampset_shift_scale', 'duration'), + ('periodset_shift_scale', 'shift'), + ('periodset_shift_scale', 'duration'), + ('temporal_shift_scale_time', 'shift'), + ('temporal_shift_scale_time', 'duration'), ('tbox_make', 'p'), ('tbox_make', 's'), ('stbox_make', 'p'), - ('stbox_shift_tscale', 'shift'), - ('stbox_shift_tscale', 'duration'), + ('stbox_shift_scale_time', 'shift'), + ('stbox_shift_scale_time', 'duration'), ('temporal_tcount_transfn', 'state'), ('temporal_extent_transfn', 'p'), ('tnumber_extent_transfn', 'box'), ('tpoint_extent_transfn', 'box'), ('tbool_tand_transfn', 'state'), ('tbool_tor_transfn', 'state'), - ('tbox_shift_tscale', 'shift'), - ('tbox_shift_tscale', 'duration'), + ('tbox_shift_scale_time', 'shift'), + ('tbox_shift_scale_time', 'duration'), ('tint_tmin_transfn', 'state'), ('tfloat_tmin_transfn', 'state'), ('tint_tmax_transfn', 'state'), diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 9058cc3a..6664c41c 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -962,20 +962,26 @@ extern TimestampTz *timestampset_values(const Set *ts); +extern Set *bigintset_shift_scale(const Set *s, int64 shift, int64 width, bool hasshift, bool haswidth); extern Set *floatset_round(const Set *s, int maxdd); +extern Set *floatset_shift_scale(const Set *s, double shift, double width, bool hasshift, bool haswidth); extern Span *floatspan_intspan(const Span *s); extern Span *floatspan_round(const Span *s, int maxdd); extern SpanSet *floatspanset_intspanset(const SpanSet *ss); extern SpanSet *floatspanset_round(const SpanSet *ss, int maxdd); extern Set *geoset_round(const Set *s, int maxdd); +extern Set *intset_shift_scale(const Set *s, int shift, int width, bool hasshift, bool haswidth); extern Span *intspan_floatspan(const Span *s); extern SpanSet *intspanset_floatspanset(const SpanSet *ss); -extern Span *period_shift_tscale(const Span *p, const Interval *shift, const Interval *duration); +extern Set *numset_shift_scale(const Set *s, Datum shift, Datum width, bool hasshift, bool haswidth); +extern Span *numspan_shift_scale(const Span *s, Datum shift, Datum width, bool hasshift, bool haswidth); +extern SpanSet *numspanset_shift_scale(const SpanSet *ss, Datum shift, Datum width, bool hasshift, bool haswidth); +extern Span *period_shift_scale(const Span *p, const Interval *shift, const Interval *duration); extern Span *period_tprecision(const Span *s, const Interval *duration, TimestampTz torigin); -extern SpanSet *periodset_shift_tscale(const SpanSet *ps, const Interval *shift, const Interval *duration); +extern SpanSet *periodset_shift_scale(const SpanSet *ss, const Interval *shift, const Interval *duration); extern SpanSet *periodset_tprecision(const SpanSet *ss, const Interval *duration, TimestampTz torigin); extern TimestampTz timestamp_tprecision(TimestampTz t, const Interval *duration, TimestampTz torigin); -extern Set *timestampset_shift_tscale(const Set *ts, const Interval *shift, const Interval *duration); +extern Set *timestampset_shift_scale(const Set *ts, const Interval *shift, const Interval *duration); /***************************************************************************** * Bounding box functions for set and span types @@ -1335,11 +1341,13 @@ extern STBox *stbox_expand_time(const STBox *box, const Interval *interval); extern STBox *stbox_get_space(const STBox *box); extern STBox *stbox_round(const STBox *box, int maxdd); extern STBox *stbox_set_srid(const STBox *box, int32 srid); -extern STBox *stbox_shift_tscale(const STBox *box, const Interval *shift, const Interval *duration); +extern STBox *stbox_shift_scale_time(const STBox *box, const Interval *shift, const Interval *duration); extern TBox *tbox_expand_value(const TBox *box, const double d); extern TBox *tbox_expand_time(const TBox *box, const Interval *interval); extern TBox *tbox_round(const TBox *box, int maxdd); -extern TBox *tbox_shift_tscale(const TBox *box, const Interval *shift, const Interval *duration); +extern TBox *tbox_shift_scale_int(const TBox *box, int shift, int width, bool hasshift, bool haswidth); +extern TBox *tbox_shift_scale_float(const TBox *box, double shift, double width, bool hasshift, bool haswidth); +extern TBox *tbox_shift_scale_time(const TBox *box, const Interval *shift, const Interval *duration); @@ -1546,14 +1554,20 @@ extern text **ttext_values(const Temporal *temp, int *count); extern Temporal *temporal_set_interp(const Temporal *temp, interpType interp); -extern Temporal *temporal_shift(const Temporal *temp, const Interval *shift); -extern Temporal *temporal_shift_tscale(const Temporal *temp, const Interval *shift, const Interval *duration); +extern Temporal *tfloat_scale_value(const Temporal *temp, double width); +extern Temporal *tfloat_shift_scale_value(const Temporal *temp, double shift, double width); +extern Temporal *tfloat_shift_value(const Temporal *temp, double shift); +extern Temporal *tint_scale_value(const Temporal *temp, int width); +extern Temporal *tint_shift_scale_value(const Temporal *temp, int shift, int width); +extern Temporal *tint_shift_value(const Temporal *temp, int shift); +extern Temporal *temporal_scale_time(const Temporal *temp, const Interval *duration); +extern Temporal *temporal_shift_scale_time(const Temporal *temp, const Interval *shift, const Interval *duration); +extern Temporal *temporal_shift_time(const Temporal *temp, const Interval *shift); extern Temporal *temporal_to_tinstant(const Temporal *temp); extern Temporal *temporal_to_tsequence(const Temporal *temp, interpType interp); extern Temporal *temporal_to_tsequenceset(const Temporal *temp, interpType interp); extern Temporal *temporal_tprecision(const Temporal *temp, const Interval *duration, TimestampTz origin); extern Temporal *temporal_tsample(const Temporal *temp, const Interval *duration, TimestampTz origin); -extern Temporal *temporal_tscale(const Temporal *temp, const Interval *duration); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 42b17c2c..40606031 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -1590,6 +1590,15 @@ def timestampset_values(ts: 'const Set *') -> 'TimestampTz *': return result if result != _ffi.NULL else None +def bigintset_shift_scale(s: 'const Set *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + shift_converted = _ffi.cast('int64', shift) + width_converted = _ffi.cast('int64', width) + result = _lib.bigintset_shift_scale(s_converted, shift_converted, width_converted, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + def floatset_round(s: 'const Set *', maxdd: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) result = _lib.floatset_round(s_converted, maxdd) @@ -1597,6 +1606,13 @@ def floatset_round(s: 'const Set *', maxdd: int) -> 'Set *': return result if result != _ffi.NULL else None +def floatset_shift_scale(s: 'const Set *', shift: float, width: float, hasshift: bool, haswidth: bool) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.floatset_shift_scale(s_converted, shift, width, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + def floatspan_intspan(s: 'const Span *') -> 'Span *': s_converted = _ffi.cast('const Span *', s) result = _lib.floatspan_intspan(s_converted) @@ -1632,6 +1648,13 @@ def geoset_round(s: 'const Set *', maxdd: int) -> 'Set *': return result if result != _ffi.NULL else None +def intset_shift_scale(s: 'const Set *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.intset_shift_scale(s_converted, shift, width, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + def intspan_floatspan(s: 'const Span *') -> 'Span *': s_converted = _ffi.cast('const Span *', s) result = _lib.intspan_floatspan(s_converted) @@ -1646,11 +1669,38 @@ def intspanset_floatspanset(ss: 'const SpanSet *') -> 'SpanSet *': return result if result != _ffi.NULL else None -def period_shift_tscale(p: 'const Span *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'Span *': +def numset_shift_scale(s: 'const Set *', shift: 'Datum', width: 'Datum', hasshift: bool, haswidth: bool) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + shift_converted = _ffi.cast('Datum', shift) + width_converted = _ffi.cast('Datum', width) + result = _lib.numset_shift_scale(s_converted, shift_converted, width_converted, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + +def numspan_shift_scale(s: 'const Span *', shift: 'Datum', width: 'Datum', hasshift: bool, haswidth: bool) -> 'Span *': + s_converted = _ffi.cast('const Span *', s) + shift_converted = _ffi.cast('Datum', shift) + width_converted = _ffi.cast('Datum', width) + result = _lib.numspan_shift_scale(s_converted, shift_converted, width_converted, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + +def numspanset_shift_scale(ss: 'const SpanSet *', shift: 'Datum', width: 'Datum', hasshift: bool, haswidth: bool) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + shift_converted = _ffi.cast('Datum', shift) + width_converted = _ffi.cast('Datum', width) + result = _lib.numspanset_shift_scale(ss_converted, shift_converted, width_converted, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + +def period_shift_scale(p: 'const Span *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'Span *': p_converted = _ffi.cast('const Span *', p) shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL - result = _lib.period_shift_tscale(p_converted, shift_converted, duration_converted) + result = _lib.period_shift_scale(p_converted, shift_converted, duration_converted) _check_error() return result if result != _ffi.NULL else None @@ -1664,11 +1714,11 @@ def period_tprecision(s: 'const Span *', duration: 'const Interval *', torigin: return result if result != _ffi.NULL else None -def periodset_shift_tscale(ps: 'const SpanSet *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'SpanSet *': - ps_converted = _ffi.cast('const SpanSet *', ps) +def periodset_shift_scale(ss: 'const SpanSet *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL - result = _lib.periodset_shift_tscale(ps_converted, shift_converted, duration_converted) + result = _lib.periodset_shift_scale(ss_converted, shift_converted, duration_converted) _check_error() return result if result != _ffi.NULL else None @@ -1691,11 +1741,11 @@ def timestamp_tprecision(t: int, duration: 'const Interval *', torigin: int) -> return result if result != _ffi.NULL else None -def timestampset_shift_tscale(ts: 'const Set *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'Set *': +def timestampset_shift_scale(ts: 'const Set *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'Set *': ts_converted = _ffi.cast('const Set *', ts) shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL - result = _lib.timestampset_shift_tscale(ts_converted, shift_converted, duration_converted) + result = _lib.timestampset_shift_scale(ts_converted, shift_converted, duration_converted) _check_error() return result if result != _ffi.NULL else None @@ -4108,11 +4158,11 @@ def stbox_set_srid(box: 'const STBox *', srid: int) -> 'STBox *': return result if result != _ffi.NULL else None -def stbox_shift_tscale(box: 'const STBox *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'STBox *': +def stbox_shift_scale_time(box: 'const STBox *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'STBox *': box_converted = _ffi.cast('const STBox *', box) shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL - result = _lib.stbox_shift_tscale(box_converted, shift_converted, duration_converted) + result = _lib.stbox_shift_scale_time(box_converted, shift_converted, duration_converted) _check_error() return result if result != _ffi.NULL else None @@ -4140,11 +4190,25 @@ def tbox_round(box: 'const TBox *', maxdd: int) -> 'TBox *': return result if result != _ffi.NULL else None -def tbox_shift_tscale(box: 'const TBox *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'TBox *': +def tbox_shift_scale_int(box: 'const TBox *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'TBox *': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_shift_scale_int(box_converted, shift, width, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + +def tbox_shift_scale_float(box: 'const TBox *', shift: float, width: float, hasshift: bool, haswidth: bool) -> 'TBox *': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_shift_scale_float(box_converted, shift, width, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + +def tbox_shift_scale_time(box: 'const TBox *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL - result = _lib.tbox_shift_tscale(box_converted, shift_converted, duration_converted) + result = _lib.tbox_shift_scale_time(box_converted, shift_converted, duration_converted) _check_error() return result if result != _ffi.NULL else None @@ -5339,19 +5403,69 @@ def temporal_set_interp(temp: 'const Temporal *', interp: 'interpType') -> 'Temp return result if result != _ffi.NULL else None -def temporal_shift(temp: 'const Temporal *', shift: 'const Interval *') -> 'Temporal *': +def tfloat_scale_value(temp: 'const Temporal *', width: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - shift_converted = _ffi.cast('const Interval *', shift) - result = _lib.temporal_shift(temp_converted, shift_converted) + result = _lib.tfloat_scale_value(temp_converted, width) _check_error() return result if result != _ffi.NULL else None -def temporal_shift_tscale(temp: 'const Temporal *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'Temporal *': +def tfloat_shift_scale_value(temp: 'const Temporal *', shift: float, width: float) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_shift_scale_value(temp_converted, shift, width) + _check_error() + return result if result != _ffi.NULL else None + + +def tfloat_shift_value(temp: 'const Temporal *', shift: float) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_shift_value(temp_converted, shift) + _check_error() + return result if result != _ffi.NULL else None + + +def tint_scale_value(temp: 'const Temporal *', width: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_scale_value(temp_converted, width) + _check_error() + return result if result != _ffi.NULL else None + + +def tint_shift_scale_value(temp: 'const Temporal *', shift: int, width: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_shift_scale_value(temp_converted, shift, width) + _check_error() + return result if result != _ffi.NULL else None + + +def tint_shift_value(temp: 'const Temporal *', shift: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_shift_value(temp_converted, shift) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_scale_time(temp: 'const Temporal *', duration: 'const Interval *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + duration_converted = _ffi.cast('const Interval *', duration) + result = _lib.temporal_scale_time(temp_converted, duration_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_shift_scale_time(temp: 'const Temporal *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL - result = _lib.temporal_shift_tscale(temp_converted, shift_converted, duration_converted) + result = _lib.temporal_shift_scale_time(temp_converted, shift_converted, duration_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_shift_time(temp: 'const Temporal *', shift: 'const Interval *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + shift_converted = _ffi.cast('const Interval *', shift) + result = _lib.temporal_shift_time(temp_converted, shift_converted) _check_error() return result if result != _ffi.NULL else None @@ -5397,14 +5511,6 @@ def temporal_tsample(temp: 'const Temporal *', duration: 'const Interval *', ori return result if result != _ffi.NULL else None -def temporal_tscale(temp: 'const Temporal *', duration: 'const Interval *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - duration_converted = _ffi.cast('const Interval *', duration) - result = _lib.temporal_tscale(temp_converted, duration_converted) - _check_error() - return result if result != _ffi.NULL else None - - def tbool_at_value(temp: 'const Temporal *', b: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) result = _lib.tbool_at_value(temp_converted, b) From eb1568491e10b0811571c8c89671dd9a32a6098c Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Tue, 12 Sep 2023 15:50:11 +0200 Subject: [PATCH 025/101] Synchronize with MEOS API --- pymeos/pymeos/boxes/tbox.py | 78 ++++++++++++++++++++++++++++++ pymeos/pymeos/main/tnumber.py | 71 +++++++++++++++++++++++++++ pymeos/pymeos/temporal/temporal.py | 4 +- pymeos/tests/boxes/tbox_test.py | 38 +++++++++++++++ pymeos/tests/main/tfloat_test.py | 57 ++++++++++++++++++++++ pymeos/tests/main/tint_test.py | 57 ++++++++++++++++++++++ 6 files changed, 303 insertions(+), 2 deletions(-) diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index c7f76164..a9f217b5 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -467,6 +467,24 @@ def expand(self, other: Union[int, float, timedelta]) -> TBox: raise TypeError(f'Operation not supported with type {other.__class__}') return TBox(_inner=result) + def shift_value(self, delta: Union[int, float]) -> TBox: + """ + Returns a new `TBox` with the value dimension shifted by `delta`. + + Args: + delta: value to shift + + Returns: + A new :class:`TBox` instance + + MEOS Functions: + span_shift_scale + + See Also: + :meth:`Span.shift` + """ + return self.shift_scale_value(shift=delta) + def shift_time(self, delta: timedelta) -> TBox: """ Returns a new `TBox` with the time dimension shifted by `delta`. @@ -485,6 +503,24 @@ def shift_time(self, delta: timedelta) -> TBox: """ return self.shift_scale_time(shift=delta) + def scale_value(self, width: Union[int, float]) -> TBox: + """ + Returns a new `TBox` with the value dimension having width `width`. + + Args: + width: value of the new width + + Returns: + A new :class:`TBox` instance + + MEOS Functions: + span_shift_scale + + See Also: + :meth:`Span.scale` + """ + return self.shift_scale_value(width=width) + def scale_time(self, duration: timedelta) -> TBox: """ Returns a new `TBox` with the time dimension having duration `duration`. @@ -503,6 +539,48 @@ def scale_time(self, duration: timedelta) -> TBox: """ return self.shift_scale_time(duration=duration) + def shift_scale_value(self, shift: Optional[Union[int, float]] = None, + width: Optional[Union[int, float]] = None) -> TBox: + """ + Returns a new TBox with the value span shifted by `shift` and + width `width`. + + Examples: + >>> tbox = TBox('TBoxInt XT([0, 5),[2020-06-01, 2020-06-02])') + >>> tbox.shift_scale_value(shift=2, width=4) + >>> 'TBOXINT XT([2, 7),[2020-06-01 00:00:00+02, 2020-06-02 00:00:00+02])' + + Args: + shift: :value to shift the start of the value span + width: value representing the width of the value span + + Returns: + A new :class:`TBox` instance + + MEOS Functions: + span_shift_scale + + See Also: + :meth:`Span.shift_scale` + """ + assert shift is not None or width is not None, \ + 'shift and width deltas must not be both None' + hasshift = shift is not None + haswidth = width is not None + if (shift is None or isinstance(shift, int)) and \ + (width is None or isinstance(width, int)) : + result = tbox_shift_scale_int(self._inner, + shift if shift else 0, width if width else 0, + hasshift, haswidth) + elif (shift is None or isinstance(shift, float)) and \ + (width is None or isinstance(width, float)) : + result = tbox_shift_scale_float(self._inner, + shift if shift else 0.0, width if width else 0.0, + hasshift, haswidth) + else: + raise TypeError(f'Operation not supported with type {self.__class__}') + return TBox(_inner=result) + def shift_scale_time(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TBox: """ diff --git a/pymeos/pymeos/main/tnumber.py b/pymeos/pymeos/main/tnumber.py index 5ac26164..6c1fcda5 100644 --- a/pymeos/pymeos/main/tnumber.py +++ b/pymeos/pymeos/main/tnumber.py @@ -61,6 +61,77 @@ def time_weighted_average(self) -> float: """ return tnumber_twavg(self._inner) + # ------------------------- Transformations ------------------------------- + def shift_value(self, delta: Union[int, float]) -> Self: + """ + Returns a new :class:`TNumber` with the value dimension shifted by + ``delta``. + + Args: + delta: value to shift + + MEOS Functions: + tint_shift_value, tfloat_shift_value + """ + from .tint import TInt + from .tfloat import TFloat + if isinstance(self, TInt): + shifted = tint_shift_value(self._inner, int(delta)) + elif isinstance(self, TFloat): + shifted = tfloat_shift_value(self._inner, float(delta)) + else: + raise TypeError(f'Operation not supported with type {self.__class__}') + return Temporal._factory(shifted) + + def scale_value(self, width: Union[int, float]) -> Self: + """ + Returns a new :class:`TNumber` scaled so the value dimension has + width ``width``. + + Args: + width: value representing the width of the new temporal number + + MEOS Functions: + tint_scale_value, tfloat_scale_value + """ + from .tint import TInt + from .tfloat import TFloat + if isinstance(self, TInt): + scaled = tint_scale_value(self._inner, int(width)) + elif isinstance(self, TFloat): + scaled = tfloat_scale_value(self._inner, float(width)) + else: + raise TypeError(f'Operation not supported with type {self.__class__}') + return Temporal._factory(scaled) + + def shift_scale_value(self, shift: Union[int, float] = None, + width: Union[int, float] = None) -> Self: + """ + Returns a new :class:`TNumber` with the value dimension shifted by + ``shift`` and scaled so the value dimension has width ``width``. + + Args: + shift: value to shift + width: value representing the width of the new temporal number + + MEOS Functions: + tint_shift_scale_value, tfloat_shift_scale_value + """ + from .tint import TInt + from .tfloat import TFloat + assert shift is not None or width is not None, \ + 'shift and width must not be both None' + + if isinstance(self, TInt): + scaled = tint_shift_scale_value(self._inner, + int(shift) if shift else None, int(width) if width else None) + elif isinstance(self, TFloat): + scaled = tfloat_shift_scale_value(self._inner, + float(shift) if shift else None, float(width) if width else None) + else: + raise TypeError(f'Operation not supported with type {self.__class__}') + return Temporal._factory(scaled) + # ------------------------- Restrictions ---------------------------------- def at(self, other: Union[intrange, floatrange, List[intrange], List[floatrange], TBox, Time]) -> TG: diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index 9e4e441d..cdf3d679 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -495,7 +495,7 @@ def set_interpolation(self: Self, interpolation: TInterpolation) -> Self: new_temp = temporal_set_interp(self._inner, interpolation) return Temporal._factory(new_temp) - def shift_time(self, delta: timedelta) -> Period: + def shift_time(self, delta: timedelta) -> Self: """ Returns a new :class:`Temporal` with the temporal dimension shifted by ``delta``. @@ -509,7 +509,7 @@ def shift_time(self, delta: timedelta) -> Period: shifted = temporal_shift_time(self._inner, timedelta_to_interval(delta)) return Temporal._factory(shifted) - def scale_time(self, duration: timedelta) -> Period: + def scale_time(self, duration: timedelta) -> Self: """ Returns a new :class:`Temporal` scaled so the temporal dimension has duration ``duration``. diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index 3e82a704..83237658 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -378,6 +378,20 @@ def test_expand_time(self, tbox, expected): assert isinstance(tb, TBox) assert tb == expected + @pytest.mark.parametrize( + 'tbox, delta, expected', + [(tbfx, 2.0, TBox('TBOXFLOAT X([3,4])')), + (tbfx, -2.0, TBox('TBOXFLOAT X([-1,0])')), + (tbfxt, 2.0, TBox('TBOXFLOAT XT([3,4],[2019-09-01, 2019-09-02])')), + (tbfxt, -2.0, TBox('TBOXFLOAT XT([-1,0],[2019-09-01, 2019-09-02])')), + ], + ids=['TBox T positive', 'TBox T negative', + 'TBoxFloat XT positive', 'TBoxFloat XT negative' + ] + ) + def test_shift_value(self, tbox, delta, expected): + assert tbox.shift_value(delta) == expected + @pytest.mark.parametrize( 'tbox, delta, expected', [(tbt, timedelta(days=4), @@ -394,6 +408,16 @@ def test_expand_time(self, tbox, expected): def test_shift_time(self, tbox, delta, expected): assert tbox.shift_time(delta) == expected + @pytest.mark.parametrize( + 'tbox, delta, expected', + [(tbfx, 4.0, TBox('TBOXFLOAT X([1,5])')), + (tbfxt, 4.0, TBox('TBOXFLOAT XT([1,5],[2019-09-01, 2019-09-02])')), + ], + ids=['TBox T', 'TBoxFloat XT'] + ) + def test_scale_value(self, tbox, delta, expected): + assert tbox.scale_value(delta) == expected + @pytest.mark.parametrize( 'tbox, delta, expected', [(tbt, timedelta(days=4), @@ -406,6 +430,20 @@ def test_shift_time(self, tbox, delta, expected): def test_scale_time(self, tbox, delta, expected): assert tbox.scale_time(delta) == expected + @pytest.mark.parametrize( + 'tbox, delta, width, expected', + [(tbfx, 2.0, 4.0, TBox('TBOXFLOAT X([3,7])')), + (tbfx, -2.0, 4.0, TBox('TBOXFLOAT X([-1,3])')), + (tbfxt, 2.0, 4.0, TBox('TBOXFLOAT XT([3,7],[2019-09-01, 2019-09-02])')), + (tbfxt, -2.0, 4.0, TBox('TBOXFLOAT XT([-1,3],[2019-09-01, 2019-09-02])')), + ], + ids=['TBox T positive', 'TBox T negative', + 'TBoxFloat XT positive', 'TBoxFloat XT negative' + ] + ) + def test_shift_scale_value(self, tbox, delta, width, expected): + assert tbox.shift_scale_value(delta, width) == expected + def test_shift_scale_time(self): assert self.tbt.shift_scale_time(timedelta(days=4), timedelta(hours=4)) == \ TBox('TBOX T([2019-09-05,2019-09-05 04:00:00])') diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index 34850d91..f8dec0fa 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -958,6 +958,63 @@ def test_to_sequenceset(self, temporal, interpolation, expected): def test_set_interpolation(self, temporal, interpolation, expected): assert temporal.set_interpolation(interpolation) == expected + @pytest.mark.parametrize( + 'tfloat, delta, expected', + [(tfi, 2, TFloatInst('3.5@2019-09-01')), + (tfi, -2, TFloatInst('-0.5@2019-09-01')), + (tfds, 2, TFloatSeq('{3.5@2019-09-01, 4.5@2019-09-02}')), + (tfds, -2, TFloatSeq('{-0.5@2019-09-01, 0.5@2019-09-02}')), + (tfs, 2, TFloatSeq('[3.5@2019-09-01, 4.5@2019-09-02]')), + (tfs, -2, TFloatSeq('[-0.5@2019-09-01, 0.5@2019-09-02]')), + (tfss, 2, TFloatSeqSet('{[3.5@2019-09-01, 4.5@2019-09-02],' + '[3.5@2019-09-03, 3.5@2019-09-05]}')), + (tfss, -2, TFloatSeqSet('{[-0.5@2019-09-01, 0.5@2019-09-02],' + '[-0.5@2019-09-03, -0.5@2019-09-05]}')), + ], + ids=['Instant positive', 'Instant negative', + 'Discrete Sequence positive', 'Discrete Sequence negative', + 'Sequence positive', 'Sequence negative', + 'Sequence Set positive', 'Sequence Set negative', + ] + ) + def test_shift_value(self, tfloat, delta, expected): + assert tfloat.shift_value(delta) == expected + + @pytest.mark.parametrize( + 'tfloat, width, expected', + [(tfi, 4, TFloatInst('1.5@2019-09-01')), + (tfds, 4, TFloatSeq('{1.5@2019-09-01, 5.5@2019-09-02}')), + (tfs, 4, TFloatSeq('[1.5@2019-09-01, 5.5@2019-09-02]')), + (tfss, 4, TFloatSeqSet('{[1.5@2019-09-01, 5.5@2019-09-02],' + '[1.5@2019-09-03, 1.5@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set',] + ) + def test_scale_value(self, tfloat, width, expected): + assert tfloat.scale_value(width) == expected + + @pytest.mark.parametrize( + 'tfloat, delta, width, expected', + [(tfi, 2, 3, TFloatInst('3.5@2019-09-01')), + (tfi, -2, 3, TFloatInst('-0.5@2019-09-01')), + (tfds, 2, 3, TFloatSeq('{3.5@2019-09-01, 6.5@2019-09-02}')), + (tfds, -2, 3, TFloatSeq('{-0.5@2019-09-01, 2.5@2019-09-02}')), + (tfs, 2, 3, TFloatSeq('[3.5@2019-09-01, 6.5@2019-09-02]')), + (tfs, -2, 3, TFloatSeq('[-0.5@2019-09-01, 2.5@2019-09-02]')), + (tfss, 2, 3, TFloatSeqSet('{[3.5@2019-09-01, 6.5@2019-09-02],' + '[3.5@2019-09-03, 3.5@2019-09-05]}')), + (tfss, -2, 3, TFloatSeqSet('{[-0.5@2019-09-01, 2.5@2019-09-02],' + '[-0.5@2019-09-03, -0.5@2019-09-05]}')), + ], + ids=['Instant positive', 'Instant negative', + 'Discrete Sequence positive', 'Discrete Sequence negative', + 'Sequence positive', 'Sequence negative', + 'Sequence Set positive', 'Sequence Set negative', + ] + ) + def test_shift_scale_value(self, tfloat, delta, width, expected): + assert tfloat.shift_scale_value(delta, width) == expected + @pytest.mark.parametrize( 'tfloat, delta, expected', [(tfi, timedelta(days=4), TFloatInst('1.5@2019-09-05')), diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index 6b1a33d8..6cf88e65 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -915,6 +915,63 @@ def test_to_sequenceset(self, temporal, interpolation, expected): def test_set_interpolation(self, temporal, interpolation, expected): assert temporal.set_interpolation(interpolation) == expected + @pytest.mark.parametrize( + 'tint, delta, expected', + [(tii, 2, TIntInst('3@2019-09-01')), + (tii, -2, TIntInst('-1@2019-09-01')), + (tids, 2, TIntSeq('{3@2019-09-01, 4@2019-09-02}')), + (tids, -2, TIntSeq('{-1@2019-09-01, 0@2019-09-02}')), + (tis, 2, TIntSeq('[3@2019-09-01, 4@2019-09-02]')), + (tis, -2, TIntSeq('[-1@2019-09-01, 0@2019-09-02]')), + (tiss, 2, TIntSeqSet('{[3@2019-09-01, 4@2019-09-02],' + '[3@2019-09-03, 3@2019-09-05]}')), + (tiss, -2, TIntSeqSet('{[-1@2019-09-01, 0@2019-09-02],' + '[-1@2019-09-03, -1@2019-09-05]}')), + ], + ids=['Instant positive', 'Instant negative', + 'Discrete Sequence positive', 'Discrete Sequence negative', + 'Sequence positive', 'Sequence negative', + 'Sequence Set positive', 'Sequence Set negative', + ] + ) + def test_shift_value(self, tint, delta, expected): + assert tint.shift_value(delta) == expected + + @pytest.mark.parametrize( + 'tint, width, expected', + [(tii, 3, TIntInst('1@2019-09-01')), + (tids, 3, TIntSeq('{1@2019-09-01, 4@2019-09-02}')), + (tis, 3, TIntSeq('[1@2019-09-01, 4@2019-09-02]')), + (tiss, 3, TIntSeqSet('{[1@2019-09-01, 4@2019-09-02],' + '[1@2019-09-03, 1@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set',] + ) + def test_scale_value(self, tint, width, expected): + assert tint.scale_value(width) == expected + + @pytest.mark.parametrize( + 'tint, delta, width, expected', + [(tii, 2, 3, TIntInst('3@2019-09-01')), + (tii, -2, 3, TIntInst('-1@2019-09-01')), + (tids, 2, 3, TIntSeq('{3@2019-09-01, 6@2019-09-02}')), + (tids, -2, 3, TIntSeq('{-1@2019-09-01, 2@2019-09-02}')), + (tis, 2, 3, TIntSeq('[3@2019-09-01, 6@2019-09-02]')), + (tis, -2, 3, TIntSeq('[-1@2019-09-01, 2@2019-09-02]')), + (tiss, 2, 3, TIntSeqSet('{[3@2019-09-01, 6@2019-09-02],' + '[3@2019-09-03, 3@2019-09-05]}')), + (tiss, -2, 3, TIntSeqSet('{[-1@2019-09-01, 2@2019-09-02],' + '[-1@2019-09-03, -1@2019-09-05]}')), + ], + ids=['Instant positive', 'Instant negative', + 'Discrete Sequence positive', 'Discrete Sequence negative', + 'Sequence positive', 'Sequence negative', + 'Sequence Set positive', 'Sequence Set negative', + ] + ) + def test_shift_scale_value(self, tint, delta, width, expected): + assert tint.shift_scale_value(delta, width) == expected + @pytest.mark.parametrize( 'tint, delta, expected', [(tii, timedelta(days=4), TIntInst('1@2019-09-05')), From eada6b39e5e3a733cf741b05375fb6213904aa0e Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 19 Sep 2023 14:16:28 +0200 Subject: [PATCH 026/101] Remove old time classes. --- pymeos/pymeos/old_time/__init__.py | 7 - pymeos/pymeos/old_time/period.py | 1017 ----------------------- pymeos/pymeos/old_time/periodset.py | 1039 ------------------------ pymeos/pymeos/old_time/time.py | 16 - pymeos/pymeos/old_time/timestampset.py | 980 ---------------------- pymeos/pymeos/temporal/temporal.py | 8 +- 6 files changed, 4 insertions(+), 3063 deletions(-) delete mode 100644 pymeos/pymeos/old_time/__init__.py delete mode 100644 pymeos/pymeos/old_time/period.py delete mode 100644 pymeos/pymeos/old_time/periodset.py delete mode 100644 pymeos/pymeos/old_time/time.py delete mode 100644 pymeos/pymeos/old_time/timestampset.py diff --git a/pymeos/pymeos/old_time/__init__.py b/pymeos/pymeos/old_time/__init__.py deleted file mode 100644 index d2dd1f70..00000000 --- a/pymeos/pymeos/old_time/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .period import Period -from .periodset import PeriodSet -from .time import Time -from .timestampset import TimestampSet -from datetime import datetime, timedelta - -__all__ = ['Time', 'datetime', 'TimestampSet', 'Period', 'PeriodSet', 'timedelta'] diff --git a/pymeos/pymeos/old_time/period.py b/pymeos/pymeos/old_time/period.py deleted file mode 100644 index 9f6e4ba0..00000000 --- a/pymeos/pymeos/old_time/period.py +++ /dev/null @@ -1,1017 +0,0 @@ -from __future__ import annotations - -from datetime import datetime, timedelta -from typing import Optional, Union, overload, TYPE_CHECKING, get_args - -from dateutil.parser import parse -from pymeos_cffi import * - -if TYPE_CHECKING: - from ..temporal import Temporal - from ..boxes import Box - from .periodset import PeriodSet - from .timestampset import TimestampSet - from .time import Time - - -class Period: - """ - Class for representing sets of contiguous timestamps between a lower and - an upper bound. The bounds may be inclusive or not. - - ``Period`` objects can be created with a single argument of type string - as in MobilityDB. - - >>> Period('(2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01)') - - Another possibility is to provide the ``lower`` and ``upper`` named parameters (of type str or datetime), and - optionally indicate whether the bounds are inclusive or exclusive (by default, the lower bound is inclusive and the - upper is exclusive): - - >>> Period(lower='2019-09-08 00:00:00+01', upper='2019-09-10 00:00:00+01') - >>> Period(lower='2019-09-08 00:00:00+01', upper='2019-09-10 00:00:00+01', lower_inc=False, upper_inc=True) - >>> Period(lower=parse('2019-09-08 00:00:00+01'), upper=parse('2019-09-10 00:00:00+01'), upper_inc=True) - """ - - __slots__ = ['_inner'] - - # ------------------------- Constructors ---------------------------------- - def __init__(self, string: Optional[str] = None, *, - lower: Optional[Union[str, datetime]] = None, - upper: Optional[Union[str, datetime]] = None, - lower_inc: bool = True, - upper_inc: bool = False, - _inner=None): - super().__init__() - assert (_inner is not None) or ((string is not None) != (lower is not None and upper is not None)), \ - "Either string must be not None or both lower and upper must be not" - if _inner is not None: - self._inner = _inner - elif string is not None: - self._inner = period_in(string) - else: - lower_ts = pg_timestamptz_in(lower, -1) if isinstance(lower, str) else datetime_to_timestamptz(lower) - upper_ts = pg_timestamptz_in(upper, -1) if isinstance(upper, str) else datetime_to_timestamptz(upper) - self._inner = period_make(lower_ts, upper_ts, lower_inc, upper_inc) - - def __copy__(self): - """ - Return a copy of ``self``. - - Returns: - A new :class:`Period` instance - - MEOS Functions: - span_copy - """ - inner_copy = span_copy(self._inner) - return Period(_inner=inner_copy) - - @staticmethod - def from_wkb(wkb: bytes) -> Period: - """ - Returns a `Period` from its WKB representation. - - Args: - wkb: The WKB string. - - Returns: - A new :class:`Period` instance - - MEOS Functions: - span_from_wkb - """ - result = span_from_wkb(wkb) - return Period(_inner=result) - - @staticmethod - def from_hexwkb(hexwkb: str) -> Period: - """ - Returns a `Period` from its WKB representation in hex-encoded ASCII. - - Args: - hexwkb: WKB representation in hex-encoded ASCII - - Returns: - A new :class:`Period` instance - - MEOS Functions: - span_from_hexwkb - """ - result = span_from_hexwkb(hexwkb) - return Period(_inner=result) - - # ------------------------- Output ---------------------------------------- - def __str__(self): - """ - Return the string representation of the content of ``self``. - - Returns: - A new :class:`str` instance - - MEOS Functions: - period_out - """ - return period_out(self._inner) - - def __repr__(self): - """ - Return the string representation of ``self``. - - Returns: - A new :class:`str` instance - - MEOS Functions: - period_out - """ - return (f'{self.__class__.__name__}' - f'({self})') - - def as_wkb(self) -> bytes: - """ - Returns the WKB representation of ``self``. - - Returns: - A :class:`str` object with the WKB representation of ``self``. - - MEOS Functions: - span_as_wkb - """ - return span_as_wkb(self._inner, 4) - - def as_hexwkb(self) -> str: - """ - Returns the WKB representation of ``self`` in hex-encoded ASCII. - - Returns: - A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. - - MEOS Functions: - span_as_hexwkb - """ - return span_as_hexwkb(self._inner, -1)[0] - - # ------------------------- Conversions ----------------------------------- - def to_periodset(self) -> PeriodSet: - """ - Returns a period set containing ``self``. - - Returns: - A new :class:`PeriodSet` instance - - MEOS Functions: - span_to_spanset - """ - from .periodset import PeriodSet - return PeriodSet(_inner=span_to_spanset(self._inner)) - - # ------------------------- Accessors ------------------------------------- - def lower(self) -> datetime: - """ - Returns the lower bound of a period - - Returns: - The lower bound of the period as a :class:`datetime.datetime` - - MEOS Functions: - period_lower - """ - return timestamptz_to_datetime(period_lower(self._inner)) - - def upper(self) -> datetime: - """ - Returns the upper bound of a period - - Returns: - The upper bound of the period as a :class:`datetime.datetime` - - MEOS Functions: - period_upper - """ - return timestamptz_to_datetime(period_upper(self._inner)) - - def lower_inc(self) -> bool: - """ - Returns whether the lower bound of the period is inclusive or not - - Returns: - True if the lower bound of the period is inclusive and False otherwise - - MEOS Functions: - span_lower_inc - """ - return span_lower_inc(self._inner) - - def upper_inc(self) -> bool: - """ - Returns whether the upper bound of the period is inclusive or not - - Returns: - True if the upper bound of the period is inclusive and False otherwise - - MEOS Functions: - span_upper_inc - """ - return span_upper_inc(self._inner) - - def duration(self) -> timedelta: - """ - Returns the duration of the period. - - Returns: - A :class:`datetime.timedelta` instance representing the duration of the period - - MEOS Functions: - period_duration - """ - return interval_to_timedelta(period_duration(self._inner)) - - def duration_in_seconds(self) -> float: - """ - Returns the duration of the period. - - Returns: - Returns a `float` representing the duration of the period in seconds - - MEOS Functions: - span_width - """ - return span_width(self._inner) - - def __hash__(self) -> int: - """ - Return the hash representation of ``self``. - - Returns: - A new :class:`int` instance - - MEOS Functions: - span_hash - """ - return span_hash(self._inner) - - # ------------------------- Transformations ------------------------------- - def shift(self, delta: timedelta) -> Period: - """ - Returns a new period that is the result of shifting ``self`` by ``delta`` - - Examples: - >>> Period('[2000-01-01, 2000-01-10]').shift(timedelta(days=2)) - >>> 'Period([2000-01-03 00:00:00+01, 2000-01-12 00:00:00+01])' - - Args: - delta: :class:`datetime.timedelta` instance to shift - - Returns: - A new :class:`Period` instance - - MEOS Functions: - period_shift_tscale - """ - return self.shift_tscale(shift=delta) - - def tscale(self, duration: timedelta) -> Period: - """ - Returns a new period that starts as ``self`` but has duration ``duration`` - - Examples: - >>> Period('[2000-01-01, 2000-01-10]').tscale(timedelta(days=2)) - >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' - - Args: - duration: :class:`datetime.timedelta` instance representing the duration of the new period - - Returns: - A new :class:`Period` instance - - MEOS Functions: - period_shift_tscale - """ - return self.shift_tscale(duration=duration) - - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> Period: - """ - Returns a new period that starts at ``self`` shifted by ``shift`` and has duration ``duration`` - - Examples: - >>> Period('[2000-01-01, 2000-01-10]').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) - >>> 'Period([2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01])' - - Args: - shift: :class:`datetime.timedelta` instance to shift - duration: :class:`datetime.timedelta` instance representing the duration of the new period - - Returns: - A new :class:`Period` instance - - MEOS Functions: - period_shift_tscale - """ - assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' - modified = period_shift_tscale( - self._inner, - timedelta_to_interval(shift) if shift else None, - timedelta_to_interval(duration) if duration else None, - ) - return Period(_inner=modified) - - # def expand(self, other: Period) -> Period: - # """ - # Returns a new period that includes both ``self`` and ``other`` - - # Examples: - # >>> Period('[2000-01-01, 2000-01-04)').expand(Period('[2000-01-05, 2000-01-10]')) - # >>> 'Period([2000-01-01 00:00:00+01, 2000-01-10 00:00:00+01])' - - # Args: - # other: :class:`Period` instance to expand the period - - # Returns: - # A new :class:`Period` instance - - # MEOS Functions: - # span_expand - # """ - # copy = span_copy(self._inner) - # span_expand(other._inner, copy) - # return Period(_inner=copy) - - # ------------------------- Topological Operations ------------------------ - def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them - contains it. - - Examples: - >>> Period('[2012-01-01, 2012-01-02)').is_adjacent(Period('[2012-01-02, 2012-01-03]')) - >>> True - >>> Period('[2012-01-01, 2012-01-02]').is_adjacent(Period('[2012-01-02, 2012-01-03]')) - >>> False # Both contain bound - >>> Period('[2012-01-01, 2012-01-02)').is_adjacent(Period('(2012-01-02, 2012-01-03]')) - >>> False # Neither contain bound - - Args: - other: temporal object to compare with - - Returns: - True if adjacent, False otherwise - - MEOS Functions: - adjacent_span_span, adjacent_span_spanset, adjacent_period_timestamp, - adjacent_period_timestampset, adjacent_period_temporal - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return adjacent_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return adjacent_spanset_span(other._inner, self._inner) - elif isinstance(other, datetime): - return adjacent_period_timestamp(self._inner, datetime_to_timestamptz(other)) - elif isinstance(other, TimestampSet): - return adjacent_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Temporal): - return adjacent_span_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return adjacent_span_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_contained_in(self, container: Union[Period, PeriodSet, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is temporally contained in ``container``. - - Examples: - >>> Period('[2012-01-02, 2012-01-03]').is_contained_in(Period('[2012-01-01, 2012-01-04]')) - >>> True - >>> Period('(2012-01-01, 2012-01-02)').is_contained_in(Period('[2012-01-01, 2012-01-02]')) - >>> True - >>> Period('[2012-01-01, 2012-01-02]').is_contained_in(Period('(2012-01-01, 2012-01-02)')) - >>> False - - Args: - container: temporal object to compare with - - Returns: - True if contained, False otherwise - - MEOS Functions: - contained_span_span, contained_span_spanset, contained_period_temporal - """ - from .periodset import PeriodSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(container, Period): - return contained_span_span(self._inner, container._inner) - elif isinstance(container, PeriodSet): - return contained_span_spanset(self._inner, container._inner) - elif isinstance(container, Temporal): - return contained_span_span(self._inner, temporal_to_period(container._inner)) - elif isinstance(container, Box): - return contained_span_span(self._inner, container.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {container.__class__}') - - def contains(self, content: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` temporally contains ``content``. - - Examples: - >>> Period('[2012-01-01, 2012-01-04]').contains(Period('[2012-01-02, 2012-01-03]')) - >>> True - >>> Period('[2012-01-01, 2012-01-02]').contains(Period('(2012-01-01, 2012-01-02)')) - >>> True - >>> Period('(2012-01-01, 2012-01-02)').contains(Period('[2012-01-01, 2012-01-02]')) - >>> False - - Args: - content: temporal object to compare with - - Returns: - True if contains, False otherwise - - MEOS Functions: - contains_span_span, contains_span_spanset, contains_period_timestamp, - contains_period_timestampset, contains_period_temporal - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(content, Period): - return contains_span_span(self._inner, content._inner) - elif isinstance(content, PeriodSet): - return contains_span_spanset(self._inner, content._inner) - elif isinstance(content, datetime): - return contains_period_timestamp(self._inner, datetime_to_timestamptz(content)) - elif isinstance(content, TimestampSet): - return contains_span_span(self._inner, set_span(content._inner)) - elif isinstance(content, Temporal): - return contains_span_span(self._inner, temporal_to_period(content._inner)) - elif isinstance(content, get_args(Box)): - return contains_span_span(self._inner, content.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {content.__class__}') - - def __contains__(self, item): - """ - Return whether ``self`` temporally contains ``item``. - - Examples: - >>> Period('[2012-01-02, 2012-01-03]') in Period('[2012-01-01, 2012-01-04]') - >>> True - >>> Period('(2012-01-01, 2012-01-02)') in Period('[2012-01-01, 2012-01-02]') - >>> True - >>> Period('[2012-01-01, 2012-01-02]') in Period('(2012-01-01, 2012-01-02)') - >>> False - - Args: - item: temporal object to compare with - - Returns: - True if contains, False otherwise - - MEOS Functions: - contains_span_span, contains_span_spanset, contains_period_timestamp, - contains_period_timestampset, contains_period_temporal - """ - return self.contains(item) - - def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant - - Examples: - >>> Period('[2012-01-01, 2012-01-02]').overlaps(Period('[2012-01-02, 2012-01-03]')) - >>> True - >>> Period('[2012-01-01, 2012-01-02)').overlaps(Period('[2012-01-02, 2012-01-03]')) - >>> False - >>> Period('[2012-01-01, 2012-01-02)').overlaps(Period('(2012-01-02, 2012-01-03]')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if overlaps, False otherwise - - MEOS Functions: - overlaps_span_span, overlaps_span_spanset, overlaps_period_timestampset, - overlaps_period_temporal - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return overlaps_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return overlaps_spanset_span(other._inner, self._inner) - elif isinstance(other, datetime): - return overlaps_span_span(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return overlaps_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Temporal): - return overlaps_span_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return overlaps_span_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_same(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` and the bounding period of ``other`` is the same. - - Args: - other: temporal object to compare with - - Returns: - True if equal, False otherwise - - MEOS Functions: - same_period_temporal - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Temporal): - return span_eq(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return span_eq(self._inner, other.to_period()._inner) - elif isinstance(other, Period): - return span_eq(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return span_eq(self._inner, spanset_span(other._inner)) - elif isinstance(other, datetime): - return span_eq(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return span_eq(self._inner, set_span(other._inner)) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - # ------------------------- Position Operations --------------------------- - def is_before(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. - - Examples: - >>> Period('[2012-01-01, 2012-01-02)').is_before(Period('[2012-01-02, 2012-01-03]')) - >>> True - >>> Period('[2012-01-01, 2012-01-02)').is_before(Period('(2012-01-02, 2012-01-03]')) - >>> True - >>> Period('[2012-01-01, 2012-01-02]').is_before(Period('[2012-01-02, 2012-01-03]')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if before, False otherwise - - MEOS Functions: - left_span_span, left_span_spanset, before_period_timestamp, - before_period_timestampset, before_period_temporal - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return left_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return left_span_spanset(self._inner, other._inner) - elif isinstance(other, datetime): - return overafter_timestamp_period(datetime_to_timestamptz(other), self._inner) - if isinstance(other, TimestampSet): - return left_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Temporal): - return left_span_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return left_span_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). - - Examples: - >>> Period('[2012-01-01, 2012-01-02)').is_over_or_before(Period('[2012-01-02, 2012-01-03]')) - >>> True - >>> Period('[2012-01-01, 2012-01-02]').is_over_or_before(Period('[2012-01-02, 2012-01-03]')) - >>> True - >>> Period('[2012-01-03, 2012-01-05]').is_over_or_before(Period('[2012-01-01, 2012-01-04]')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if before, False otherwise - - MEOS Functions: - overleft_span_span, overleft_span_spanset, overbefore_period_timestamp, - overbefore_period_timestampset, overbefore_period_temporal - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return overleft_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return overleft_span_spanset(self._inner, other._inner) - elif isinstance(other, datetime): - return overbefore_period_timestamp(self._inner, datetime_to_timestamptz(other)) - if isinstance(other, TimestampSet): - return overleft_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Temporal): - return overleft_span_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return overleft_span_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_after(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. - - Examples: - >>> Period('[2012-01-02, 2012-01-03]').is_after(Period('[2012-01-01, 2012-01-02)')) - >>> True - >>> Period('(2012-01-02, 2012-01-03]').is_after(Period('[2012-01-01, 2012-01-02)')) - >>> True - >>> Period('[2012-01-02, 2012-01-03]').is_after(Period('[2012-01-01, 2012-01-02]')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if after, False otherwise - - MEOS Functions: - right_span_span, right_span_spanset, after_period_timestamp, - after_period_timestampset, after_period_temporal - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return right_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return right_span_spanset(self._inner, other._inner) - elif isinstance(other, datetime): - return overbefore_timestamp_period(datetime_to_timestamptz(other), self._inner) - if isinstance(other, TimestampSet): - return right_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Temporal): - return right_span_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return right_span_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). - - Examples: - >>> Period('[2012-01-02, 2012-01-03]').is_over_or_after(Period('[2012-01-01, 2012-01-02)')) - >>> True - >>> Period('[2012-01-02, 2012-01-03]').is_over_or_after(Period('[2012-01-01, 2012-01-02]')) - >>> True - >>> Period('[2012-01-02, 2012-01-03]').is_over_or_after(Period('[2012-01-01, 2012-01-03]')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if overlapping or after, False otherwise - - MEOS Functions: - overright_span_span, overright_span_spanset, overafter_period_timestamp, - overafter_period_timestampset, overafter_period_temporal - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return overright_span_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return overright_span_spanset(self._inner, other._inner) - elif isinstance(other, datetime): - return overafter_period_timestamp(self._inner, datetime_to_timestamptz(other)) - if isinstance(other, TimestampSet): - return overright_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Temporal): - return overright_span_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return overright_span_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - # ------------------------- Distance Operations --------------------------- - def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: - """ - Returns the temporal distance between ``self`` and ``other``. - - Args: - other: temporal object to compare with - - Returns: - A :class:`datetime.timedelta` instance - - MEOS Functions: - distance_span_span, distance_spanset_span, distance_period_timestamp - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Temporal): - return timedelta(seconds=distance_span_span(self._inner, temporal_to_period(other._inner))) - elif isinstance(other, Period): - return timedelta(seconds=distance_span_span(self._inner, other._inner)) - elif isinstance(other, PeriodSet): - return timedelta(seconds=distance_spanset_span(other._inner, self._inner)) - elif isinstance(other, datetime): - return timedelta(seconds=distance_period_timestamp(self._inner, datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return timedelta(seconds=distance_span_span(self._inner, set_span(other._inner))) - elif isinstance(other, get_args(Box)): - return timedelta(seconds=distance_span_span(self._inner, other.to_period()._inner)) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - # ------------------------- Set Operations -------------------------------- - @overload - def intersection(self, other: datetime) -> Optional[datetime]: - ... - - @overload - def intersection(self, other: Period) -> Optional[Period]: - ... - - @overload - def intersection(self, other: Union[TimestampSet, PeriodSet]) -> Optional[PeriodSet]: - ... - - def intersection(self, other: Time) -> Optional[Time]: - """ - Returns the temporal intersection of ``self`` and ``other``. - - Args: - other: temporal object to intersect with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - intersection_span_span, intersection_spanset_span, intersection_period_timestamp - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - if isinstance(other, datetime): - result = intersection_period_timestamp(self._inner, datetime_to_timestamptz(other)) - return timestamptz_to_datetime(result) if result is not None else None - elif isinstance(other, TimestampSet): - result = intersection_spanset_span(set_to_spanset(other._inner), self._inner) - return TimestampSet(_inner=result) if result is not None else None - elif isinstance(other, Period): - result = intersection_span_span(self._inner, other._inner) - return Period(_inner=result) if result is not None else None - elif isinstance(other, PeriodSet): - result = intersection_spanset_span(other._inner, self._inner) - return PeriodSet(_inner=result) if result is not None else None - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __mul__(self, other): - """ - Returns the temporal intersection of ``self`` and ``other``. - - Args: - other: temporal object to intersect with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - intersection_span_span, intersection_spanset_span, intersection_period_timestamp - """ - return self.intersection(other) - - def minus(self, other: Time) -> PeriodSet: - """ - Returns the temporal difference of ``self`` and ``other``. - - Args: - other: temporal object to diff with - - Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - minus_period_timestamp, minus_span_spanset, minus_span_span - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - if isinstance(other, datetime): - result = minus_period_timestamp(self._inner, datetime_to_timestamptz(other)) - return PeriodSet(_inner=result) if result is not None else None - elif isinstance(other, TimestampSet): - result = minus_span_spanset(self._inner, set_to_spanset(other._inner)) - return PeriodSet(_inner=result) if result is not None else None - elif isinstance(other, Period): - result = minus_span_span(self._inner, other._inner) - return PeriodSet(_inner=result) if result is not None else None - elif isinstance(other, PeriodSet): - result = minus_span_spanset(self._inner, other._inner) - return PeriodSet(_inner=result) if result is not None else None - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __sub__(self, other): - """ - Returns the temporal difference of ``self`` and ``other``. - - Args: - other: temporal object to diff with - - Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - minus_period_timestamp, minus_span_spanset, minus_span_span - """ - return self.minus(other) - - def union(self, other: Time) -> PeriodSet: - """ - Returns the temporal union of ``self`` and ``other``. - - Args: - other: temporal object to merge with - - Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - union_period_timestamp, union_spanset_span, union_span_span - """ - from .periodset import PeriodSet - from .timestampset import TimestampSet - if isinstance(other, datetime): - return PeriodSet(_inner=union_period_timestamp(self._inner, datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return PeriodSet(_inner=union_spanset_span(set_to_spanset(other._inner), self._inner)) - if isinstance(other, Period): - return PeriodSet(_inner=union_span_span(self._inner, other._inner)) - elif isinstance(other, PeriodSet): - return PeriodSet(_inner=union_spanset_span(other._inner, self._inner)) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __add__(self, other): - """ - Returns the temporal union of ``self`` and ``other``. - - Args: - other: temporal object to merge with - - Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - union_period_timestamp, union_spanset_span, union_span_span - """ - return self.union(other) - - # ------------------------- Comparisons ----------------------------------- - def __eq__(self, other): - """ - Return whether ``self`` and ``other`` are equal. - - Args: - other: temporal object to compare with - - Returns: - True if equal, False otherwise - - MEOS Functions: - span_eq - """ - if isinstance(other, self.__class__): - return span_eq(self._inner, other._inner) - return False - - def __ne__(self, other): - """ - Return whether ``self`` and ``other`` are not equal. - - Args: - other: temporal object to compare with - - Returns: - True if not equal, False otherwise - - MEOS Functions: - span_neq - """ - if isinstance(other, self.__class__): - return span_ne(self._inner, other._inner) - return True - - def __lt__(self, other): - """ - Return whether ``self`` is less than ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if less than, False otherwise - - MEOS Functions: - span_lt - """ - if isinstance(other, self.__class__): - return span_lt(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __le__(self, other): - """ - Return whether ``self`` is less than or equal to ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if less than or equal, False otherwise - - MEOS Functions: - span_le - """ - if isinstance(other, self.__class__): - return span_le(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __gt__(self, other): - """ - Return whether ``self`` is greater than ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if greater than, False otherwise - - MEOS Functions: - span_gt - """ - if isinstance(other, self.__class__): - return span_gt(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __ge__(self, other): - """ - Return whether ``self`` is greater than or equal to ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if greater than or equal, False otherwise - - MEOS Functions: - span_ge - """ - if isinstance(other, self.__class__): - return span_ge(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - # ------------------------- Plot Operations ------------------------------- - def plot(self, *args, **kwargs): - from ..plotters import TimePlotter - return TimePlotter.plot_period(self, *args, **kwargs) - - # ------------------------- Database Operations --------------------------- - @staticmethod - def read_from_cursor(value, _=None): - """ - Reads a :class:`Period` from a database cursor. Used when automatically loading objects from the database. - Users should use the class constructor instead. - """ - if not value: - return None - return Period(string=value) - diff --git a/pymeos/pymeos/old_time/periodset.py b/pymeos/pymeos/old_time/periodset.py deleted file mode 100644 index 3101d6ff..00000000 --- a/pymeos/pymeos/old_time/periodset.py +++ /dev/null @@ -1,1039 +0,0 @@ -from __future__ import annotations - -from datetime import timedelta, datetime -from typing import Optional, Union, List, overload, get_args -from typing import TYPE_CHECKING - -from pymeos_cffi import * - -if TYPE_CHECKING: - from ..temporal import Temporal - from ..boxes import Box - from .period import Period - from .timestampset import TimestampSet - from .time import Time - - -class PeriodSet: - """ - Class for representing lists of disjoint periods. - - ``PeriodSet`` objects can be created with a single argument of type string - as in MobilityDB. - - >>> PeriodSet(string='{[2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01], [2019-09-11 00:00:00+01, 2019-09-12 00:00:00+01]}') - - Another possibility is to give a list specifying the composing - periods, which can be instances of ``str`` or ``Period``. The composing - periods must be given in increasing order. - - >>> PeriodSet(period_list=['[2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01]', '[2019-09-11 00:00:00+01, 2019-09-12 00:00:00+01]']) - >>> PeriodSet(period_list=[Period('[2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01]'), Period('[2019-09-11 00:00:00+01, 2019-09-12 00:00:00+01]')]) - - """ - - __slots__ = ['_inner'] - - # ------------------------- Constructors ---------------------------------- - def __init__(self, string: Optional[str] = None, *, period_list: Optional[List[Union[str, Period]]] = None, - normalize: bool = True, _inner=None): - super().__init__() - assert (_inner is not None) or ((string is not None) != (period_list is not None)), \ - "Either string must be not None or period_list must be not" - if _inner is not None: - self._inner = _inner - elif string is not None: - self._inner = periodset_in(string) - else: - periods = [period_in(period)[0] if isinstance(period, str) else period._inner[0] for period in period_list] - self._inner = spanset_make(periods, normalize) - - def __copy__(self): - """ - Return a copy of ``self``. - - Returns: - A new :class:`Period` instance - - MEOS Functions: - spanset_copy - """ - inner_copy = spanset_copy(self._inner) - return PeriodSet(_inner=inner_copy) - - @staticmethod - def from_wkb(wkb: bytes) -> Period: - """ - Returns a `PeriodSet` from its WKB representation. - - Args: - wkb: The WKB string. - - Returns: - A new :class:`PeriodSet` instance - - MEOS Functions: - spanset_from_wkb - """ - result = spanset_from_wkb(wkb) - return PeriodSet(_inner=result) - - @staticmethod - def from_hexwkb(hexwkb: str) -> PeriodSet: - """ - Returns a `PeriodSet` from its WKB representation in hex-encoded ASCII. - Args: - hexwkb: WKB representation in hex-encoded ASCII - - Returns: - A new :class:`PeriodSet` instance - - MEOS Functions: - spanset_from_hexwkb - """ - result = spanset_from_hexwkb(hexwkb) - return PeriodSet(_inner=result) - - # ------------------------- Output ---------------------------------------- - def __str__(self): - """ - Return the string representation of the content of ``self``. - - Returns: - A new :class:`str` instance - - MEOS Functions: - periodset_out - """ - return periodset_out(self._inner) - - def __repr__(self): - """ - Return the string representation of ``self``. - - Returns: - A new :class:`str` instance - - MEOS Functions: - periodset_out - """ - return (f'{self.__class__.__name__}' - f'({self})') - - def as_wkb(self) -> bytes: - """ - Returns the WKB representation of ``self``. - - Returns: - A :class:`str` object with the WKB representation of ``self``. - - MEOS Functions: - spanset_as_wkb - """ - return spanset_as_wkb(self._inner, 4) - - def as_hexwkb(self) -> str: - """ - Returns the WKB representation of ``self`` in hex-encoded ASCII. - Returns: - A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. - - MEOS Functions: - spanset_as_hexwkb - """ - return spanset_as_hexwkb(self._inner, -1)[0] - - # ------------------------- Conversions ----------------------------------- - def to_period(self) -> Period: - """ - Returns a period that encompasses ``self``. - - Returns: - A new :class:`Period` instance - - MEOS Functions: - spanset_span - """ - from .period import Period - return Period(_inner=spanset_span(self._inner)) - - # ------------------------- Accessors ------------------------------------- - def duration(self, ignore_gaps: Optional[bool] = False) -> timedelta: - """ - Returns the duration of the periodset. By default, i.e., when the - second argument is False, the function takes into account the gaps within, - i.e., returns the sum of the durations of the periods within. - Otherwise, the function returns the duration of the periodset ignoring - any gap, i.e., the duration from the lower bound of the first period to - the upper bound of the last period. - - Parameters: - ignore_gaps: Whether to take into account potential time gaps in the periodset. - - Returns: - A :class:`datetime.timedelta` instance representing the duration of the periodset - - MEOS Functions: - periodset_duration - """ - return interval_to_timedelta(periodset_duration(self._inner, ignore_gaps)) - - def num_timestamps(self) -> int: - """ - Returns the number of timestamps in ``self``. - Returns: - An :class:`int` - - MEOS Functions: - periodset_num_timestamps - """ - return periodset_num_timestamps(self._inner) - - def start_timestamp(self) -> datetime: - """ - Returns the first timestamp in ``self``. - Returns: - A :class:`datetime` instance - - MEOS Functions: - periodset_start_timestamp - """ - return timestamptz_to_datetime(periodset_start_timestamp(self._inner)) - - def end_timestamp(self) -> datetime: - """ - Returns the last timestamp in ``self``. - Returns: - A :class:`datetime` instance - - MEOS Functions: - periodset_end_timestamp - """ - return timestamptz_to_datetime(periodset_end_timestamp(self._inner)) - - def timestamp_n(self, n: int) -> datetime: - """ - Returns the n-th timestamp in ``self``. - Returns: - A :class:`datetime` instance - - MEOS Functions: - periodset_timestamp_n - """ - result = periodset_timestamp_n(self._inner, n + 1) - if result is None: - raise IndexError(f"Index {n} out of range 0 - {self.num_timestamps() - 1}") - return timestamptz_to_datetime(result) - - def timestamps(self) -> List[datetime]: - """ - Returns the list of distinc timestamps in ``self``. - Returns: - A :class:`list[datetime]` instance - - MEOS Functions: - periodset_timestamps - """ - ts, count = periodset_timestamps(self._inner) - return [timestamptz_to_datetime(ts[i]) for i in range(count)] - - def num_periods(self) -> int: - """ - Returns the number of periods in ``self``. - Returns: - An :class:`int` - - MEOS Functions: - spanset_num_spans - """ - return spanset_num_spans(self._inner) - - def start_period(self) -> Period: - """ - Returns the first period in ``self``. - Returns: - A :class:`Period` instance - - MEOS Functions: - periodset_lower - """ - from .period import Period - return Period(_inner=spanset_start_span(self._inner)) - - def end_period(self) -> Period: - """ - Returns the last period in ``self``. - Returns: - A :class:`Period` instance - - MEOS Functions: - periodset_upper - """ - from .period import Period - return Period(_inner=spanset_end_span(self._inner)) - - def period_n(self, n: int) -> Period: - """ - Returns the n-th period in ``self``. - Returns: - A :class:`Period` instance - - MEOS Functions: - spanset_span_n - """ - from .period import Period - - result = spanset_span_n(self._inner, n + 1) - if result is None: - raise IndexError(f"Index {n} out of range 0 - {self.num_periods() - 1}") - return Period(_inner=result) - - def periods(self) -> List[Period]: - """ - Returns the list of periods in ``self``. - Returns: - A :class:`list[Period]` instance - - MEOS Functions: - spanset_spans - """ - from .period import Period - ps = spanset_spans(self._inner) - return [Period(_inner=ps[i]) for i in range(self.num_periods())] - - def __hash__(self) -> int: - """ - Return the hash representation of ``self``. - - Returns: - A new :class:`int` instance - - MEOS Functions: - spanset_hash - """ - return spanset_hash(self._inner) - - # ------------------------- Transformations ------------------------------- - def shift(self, delta: timedelta) -> PeriodSet: - """ - Returns a new periodset that is the result of shifting ``self`` by ``delta`` - - Examples: - >>> Period('[2000-01-01, 2000-01-10]').shift(timedelta(days=2)) - >>> 'Period([2000-01-03 00:00:00+01, 2000-01-12 00:00:00+01])' - - Args: - delta: :class:`datetime.timedelta` instance to shift - - Returns: - A new :class:`PeriodSet` instance - - MEOS Functions: - periodset_shift_tscale - """ - return self.shift_tscale(shift=delta) - - def tscale(self, duration: timedelta) -> PeriodSet: - """ - Returns a new periodset that starts as ``self`` but has duration ``duration`` - - Examples: - >>> Period('[2000-01-01, 2000-01-10]').tscale(timedelta(days=2)) - >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' - - Args: - duration: :class:`datetime.timedelta` instance representing the duration of the new period - - Returns: - A new :class:`PeriodSet` instance - - MEOS Functions: - periodset_shift_tscale - """ - return self.shift_tscale(duration=duration) - - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> PeriodSet: - """ - Returns a new periodset that starts at ``self`` shifted by ``shift`` and has duration ``duration`` - - Examples: - >>> Period('[2000-01-01, 2000-01-10]').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) - >>> 'Period([2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01])' - - Args: - shift: :class:`datetime.timedelta` instance to shift - duration: :class:`datetime.timedelta` instance representing the duration of the new period - - Returns: - A new :class:`PeriodSet` instance - - MEOS Functions: - periodset_shift_tscale - """ - assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' - ps = periodset_shift_tscale( - self._inner, - timedelta_to_interval(shift) if shift else None, - timedelta_to_interval(duration) if duration else None - ) - return PeriodSet(_inner=ps) - - # ------------------------- Topological Operations ------------------------ - def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them - contains it. - - Examples: - >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_adjacent(PeriodSet('{[2012-01-02, 2012-01-03]}')) - >>> True - >>> PeriodSet('{[2012-01-01, 2012-01-02]}').is_adjacent(PeriodSet('{[2012-01-02, 2012-01-03]}')) - >>> False # Both contain bound - >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_adjacent(PeriodSet('{[(2012-01-02, 2012-01-03]]}')) - >>> False # Neither contain bound - - Args: - other: temporal object to compare with - - Returns: - True if adjacent, False otherwise - - MEOS Functions: - adjacent_spanset_span, adjacent_spanset_spanset, adjacent_periodset_timestamp, - adjacent_periodset_timestampset - """ - from .period import Period - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return adjacent_spanset_span(self._inner, other._inner) - if isinstance(other, PeriodSet): - return adjacent_spanset_spanset(self._inner, other._inner) - elif isinstance(other, datetime): - return adjacent_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) - elif isinstance(other, TimestampSet): - return adjacent_spanset_spanset(self._inner, set_to_spanset(other._inner)) - elif isinstance(other, Temporal): - return adjacent_spanset_spanset(self._inner, temporal_time(other._inner)) - elif isinstance(other, get_args(Box)): - return adjacent_spanset_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_contained_in(self, container: Union[Period, PeriodSet, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is temporally contained in ``container``. - - Examples: - >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_contained_in(Period('{[2012-01-01, 2012-01-04]}')) - >>> True - >>> PeriodSet('{(2012-01-01, 2012-01-02)}').is_contained_in(Period('{[2012-01-01, 2012-01-02]}')) - >>> True - >>> PeriodSet('{[2012-01-01, 2012-01-02]}').is_contained_in(Period('{(2012-01-01, 2012-01-02)}')) - >>> False - - Args: - container: temporal object to compare with - - Returns: - True if contained, False otherwise - - MEOS Functions: - contained_spanset_span, contained_spanset_spanset, contained_periodset_temporal - """ - from .period import Period - from ..temporal import Temporal - from ..boxes import Box - if isinstance(container, Period): - return contained_spanset_span(self._inner, container._inner) - elif isinstance(container, PeriodSet): - return contained_spanset_spanset(self._inner, container._inner) - elif isinstance(container, Temporal): - return contained_spanset_spanset(self._inner, temporal_time(container._inner)) - elif isinstance(container, get_args(Box)): - return contained_spanset_span(self._inner, container.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {container.__class__}') - - def contains(self, content: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` temporally contains ``content``. - - Examples: - >>> PeriodSet('{[2012-01-01, 2012-01-04]}').contains(PeriodSet('{[2012-01-02, 2012-01-03]}')) - >>> True - >>> PeriodSet('{[2012-01-01, 2012-01-02]}').contains(PeriodSet('{(2012-01-01, 2012-01-02)}')) - >>> True - >>> PeriodSet('{(2012-01-01, 2012-01-02)}').contains(PeriodSet('{[2012-01-01, 2012-01-02]}')) - >>> False - - Args: - content: temporal object to compare with - - Returns: - True if contains, False otherwise - - MEOS Functions: - contains_spanset_span, contains_spanset_spanset, contains_periodset_timestamp - """ - from .period import Period - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(content, Period): - return contains_spanset_span(self._inner, content._inner) - if isinstance(content, PeriodSet): - return contains_spanset_spanset(self._inner, content._inner) - elif isinstance(content, datetime): - return contains_periodset_timestamp(self._inner, datetime_to_timestamptz(content)) - elif isinstance(content, TimestampSet): - return contains_spanset_spanset(self._inner, set_to_spanset(content._inner)) - elif isinstance(content, Temporal): - return contains_spanset_spanset(self._inner, temporal_time(content._inner)) - elif isinstance(content, get_args(Box)): - return contains_spanset_span(self._inner, content.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {content.__class__}') - - def __contains__(self, item): - """ - Returns whether ``self`` temporally contains ``content``. - - Examples: - >>> PeriodSet('{[2012-01-01, 2012-01-04]}').contains(PeriodSet('{[2012-01-02, 2012-01-03]}')) - >>> True - >>> PeriodSet('{[2012-01-01, 2012-01-02]}').contains(PeriodSet('{(2012-01-01, 2012-01-02)}')) - >>> True - >>> PeriodSet('{(2012-01-01, 2012-01-02)}').contains(PeriodSet('{[2012-01-01, 2012-01-02]}')) - >>> False - - Args: - item: temporal object to compare with - - Returns: - True if contains, False otherwise - - MEOS Functions: - contains_spanset_span, contains_spanset_spanset, contains_periodset_timestamp, - contains_periodset_timestampset, contains_periodset_temporal - """ - return self.contains(item) - - def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Box, Temporal]) -> bool: - """ - Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant - - Examples: - >>> PeriodSet('{[2012-01-01, 2012-01-02]}').overlaps(PeriodSet('{[2012-01-02, 2012-01-03]}')) - >>> True - >>> PeriodSet('{[2012-01-01, 2012-01-02)}').overlaps(PeriodSet('{[2012-01-02, 2012-01-03]}')) - >>> False - >>> PeriodSet('{[2012-01-01, 2012-01-02)}').overlaps(PeriodSet('{(2012-01-02, 2012-01-03]}')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if overlaps, False otherwise - - MEOS Functions: - overlaps_spanset_span, overlaps_spanset_spanset - """ - from .period import Period - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return overlaps_spanset_span(self._inner, other._inner) - if isinstance(other, PeriodSet): - return overlaps_spanset_spanset(self._inner, other._inner) - elif isinstance(other, TimestampSet): - return overlaps_spanset_span(self._inner, set_span(other._inner)) - elif isinstance(other, Temporal): - return overlaps_spanset_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return overlaps_spanset_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_same(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether the bounding period of `self` is the same as the bounding period of `other`. - - Args: - other: A time or temporal object to compare to `self`. - - Returns: - True if same, False otherwise. - - See Also: - :meth:`Period.is_same` - """ - return self.to_period().is_same(other) - - # ------------------------- Position Operations --------------------------- - def is_before(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. - - Examples: - >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_before(PeriodSet('{[2012-01-02, 2012-01-03]}')) - >>> True - >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_before(PeriodSet('{(2012-01-02, 2012-01-03]}')) - >>> True - >>> PeriodSet('{[2012-01-01, 2012-01-02]}').is_before(PeriodSet('{[2012-01-02, 2012-01-03]}')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if before, False otherwise - - MEOS Functions: - before_periodset_timestamp, left_spanset_span, left_spanset_spanset - """ - from .period import Period - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, datetime): - return before_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) - elif isinstance(other, TimestampSet): - return left_spanset_spanset(self._inner, set_to_spanset(other._inner)) - elif isinstance(other, Period): - return left_spanset_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return left_spanset_spanset(self._inner, other._inner) - elif isinstance(other, Temporal): - return left_spanset_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return left_spanset_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_over_or_before(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). - - Examples: - >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_over_or_before(PeriodSet('{[2012-01-02, 2012-01-03]}')) - >>> True - >>> PeriodSet('{[2012-01-01, 2012-01-02]}').is_over_or_before(PeriodSet('{[2012-01-02, 2012-01-03]}')) - >>> True - >>> PeriodSet('{[2012-01-03, 2012-01-05]}').is_over_or_before(PeriodSet('{[2012-01-01, 2012-01-04]}')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if before, False otherwise - - MEOS Functions: - overleft_spanset_span, overleft_spanset_spanset, overbefore_periodset_timestamp, - overbefore_periodset_timestampset, overbefore_periodset_temporal - """ - from .period import Period - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return overleft_spanset_span(self._inner, other._inner) - if isinstance(other, PeriodSet): - return overleft_spanset_spanset(self._inner, other._inner) - elif isinstance(other, datetime): - return overbefore_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) - if isinstance(other, TimestampSet): - return overleft_spanset_span(self._inner, set_span(other._inner)) - elif isinstance(other, Temporal): - return overleft_spanset_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return overleft_spanset_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_after(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is strictly after ``other``.That is, ``self`` starts after ``other`` ends. - - Examples: - >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_after(PeriodSet('{[2012-01-01, 2012-01-02)}')) - >>> True - >>> PeriodSet('{(2012-01-02, 2012-01-03]}').is_after(PeriodSet('{[2012-01-01, 2012-01-02)}')) - >>> True - >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_after(PeriodSet('{[2012-01-01, 2012-01-02]}')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if after, False otherwise - - MEOS Functions: - right_spanset_span, right_spanset_spanset, overbefore_timestamp_periodset - """ - from .period import Period - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, datetime): - return overbefore_timestamp_periodset(datetime_to_timestamptz(other), self._inner) - elif isinstance(other, TimestampSet): - return right_spanset_span(self._inner, set_span(other._inner)) - elif isinstance(other, Period): - return right_spanset_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return right_spanset_spanset(self._inner, other._inner) - elif isinstance(other, Temporal): - return right_spanset_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return right_spanset_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_over_or_after(self, other: Union[Time, Box, Temporal]) -> bool: - """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). - - Examples: - >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_over_or_after(PeriodSet('{[2012-01-01, 2012-01-02)}')) - >>> True - >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_over_or_after(PeriodSet('{[2012-01-01, 2012-01-02]}')) - >>> True - >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_over_or_after(PeriodSet('{[2012-01-01, 2012-01-03]}')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if overlapping or after, False otherwise - - MEOS Functions: - overright_spanset_span, overright_spanset_spanset, overafter_periodset_timestamp, - overafter_periodset_timestampset, - """ - from .period import Period - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, datetime): - return overafter_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) - elif isinstance(other, TimestampSet): - return overright_spanset_span(self._inner, set_span(other._inner)) - elif isinstance(other, Period): - return overright_spanset_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - return overright_spanset_spanset(self._inner, other._inner) - elif isinstance(other, Temporal): - return overright_spanset_span(self._inner, temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return overright_spanset_span(self._inner, other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - # ------------------------- Distance Operations --------------------------- - def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: - """ - Returns the temporal distance between ``self`` and ``other``. - - Args: - other: temporal object to compare with - - Returns: - A :class:`datetime.timedelta` instance - - MEOS Functions: - distance_periodset_period, distance_periodset_periodset, distance_periodset_timestamp, - distance_periodset_timestampset - """ - from .period import Period - from .timestampset import TimestampSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Temporal): - return timedelta(seconds=distance_spanset_span(self._inner, temporal_to_period(other._inner))) - elif isinstance(other, Period): - return timedelta(seconds=distance_spanset_span(self._inner, other._inner)) - elif isinstance(other, PeriodSet): - return timedelta(seconds=distance_spanset_spanset(self._inner, other._inner)) - elif isinstance(other, datetime): - return timedelta(seconds=distance_periodset_timestamp(self._inner, datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return timedelta(seconds=distance_spanset_span(self._inner, set_span(other._inner))) - elif isinstance(other, get_args(Box)): - return timedelta(seconds=distance_spanset_span(self._inner, other.to_period()._inner)) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - # ------------------------- Set Operations -------------------------------- - @overload - def intersection(self, other: Period) -> PeriodSet: - ... - - @overload - def intersection(self, other: PeriodSet) -> PeriodSet: - ... - - @overload - def intersection(self, other: datetime) -> datetime: - ... - - @overload - def intersection(self, other: TimestampSet) -> TimestampSet: - ... - - def intersection(self, other: Time) -> Union[PeriodSet, datetime, TimestampSet]: - """ - Returns the temporal intersection of ``self`` and ``other``. - - Args: - other: temporal object to intersect with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - intersection_periodset_timestamp, intersection_spanset_spanset, intersection_spanset_span - """ - from .period import Period - from .timestampset import TimestampSet - if isinstance(other, datetime): - result = intersection_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) - return timestamptz_to_datetime(result) if result is not None else None - elif isinstance(other, TimestampSet): - result = intersection_spanset_spanset(self._inner, set_to_spanset(other._inner)) - return TimestampSet(_inner=result) if result is not None else None - elif isinstance(other, Period): - result = intersection_spanset_span(self._inner, other._inner) - return PeriodSet(_inner=result) if result is not None else None - elif isinstance(other, PeriodSet): - result = intersection_spanset_spanset(self._inner, other._inner) - return PeriodSet(_inner=result) if result is not None else None - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __mul__(self, other): - """ - Returns the temporal intersection of ``self`` and ``other``. - - Args: - other: temporal object to intersect with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - intersection_periodset_timestamp, intersection_spanset_spanset, intersection_spanset_span - """ - return self.intersection(other) - - def minus(self, other: Time) -> PeriodSet: - """ - Returns the temporal difference of ``self`` and ``other``. - - Args: - other: temporal object to diff with - - Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - minus_spanset_span, minus_spanset_spanset, minus_periodset_timestamp - """ - from .period import Period - from .timestampset import TimestampSet - if isinstance(other, datetime): - result = minus_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) - elif isinstance(other, TimestampSet): - result = minus_spanset_spanset(self._inner, set_to_spanset(other._inner)) - elif isinstance(other, Period): - result = minus_spanset_span(self._inner, other._inner) - elif isinstance(other, PeriodSet): - result = minus_spanset_spanset(self._inner, other._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - return PeriodSet(_inner=result) if result is not None else None - - def __sub__(self, other): - """ - Returns the temporal difference of ``self`` and ``other``. - - Args: - other: temporal object to diff with - - Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - minus_spanset_span, minus_spanset_spanset, minus_periodset_timestamp - """ - return self.minus(other) - - def union(self, other: Time) -> PeriodSet: - """ - Returns the temporal union of ``self`` and ``other``. - - Args: - other: temporal object to merge with - - Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - union_periodset_timestamp, union_spanset_spanset, union_spanset_span - """ - from .period import Period - from .timestampset import TimestampSet - if isinstance(other, datetime): - return PeriodSet(_inner=union_periodset_timestamp(self._inner, datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return PeriodSet(_inner=union_spanset_spanset(self._inner, set_to_spanset(other._inner))) - elif isinstance(other, Period): - return PeriodSet(_inner=union_spanset_span(self._inner, other._inner)) - elif isinstance(other, PeriodSet): - return PeriodSet(_inner=union_spanset_spanset(self._inner, other._inner)) - - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __add__(self, other): - """ - Returns the temporal union of ``self`` and ``other``. - - Args: - other: temporal object to merge with - - Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - union_periodset_timestamp, union_spanset_spanset, union_spanset_span - """ - return self.union(other) - - # ------------------------- Comparisons ----------------------------------- - def __eq__(self, other): - """ - Return whether ``self`` and ``other`` are equal. - - Args: - other: temporal object to compare with - - Returns: - True if equal, False otherwise - - MEOS Functions: - spanset_eq - """ - if isinstance(other, self.__class__): - return spanset_eq(self._inner, other._inner) - return False - - def __ne__(self, other): - """ - Return whether ``self`` and ``other`` are not equal. - - Args: - other: temporal object to compare with - - Returns: - True if not equal, False otherwise - - MEOS Functions: - spanset_ne - """ - if isinstance(other, self.__class__): - return spanset_ne(self._inner, other._inner) - return True - - def __lt__(self, other): - """ - Return whether ``self`` is less than ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if less than, False otherwise - - MEOS Functions: - spanset_lt - """ - if isinstance(other, self.__class__): - return spanset_lt(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __le__(self, other): - """ - Return whether ``self`` is less than or equal to ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if less than or equal, False otherwise - - MEOS Functions: - spanset_le - """ - if isinstance(other, self.__class__): - return spanset_le(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __gt__(self, other): - """ - Return whether ``self`` is greater than ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if greater than, False otherwise - - MEOS Functions: - spanset_gt - """ - if isinstance(other, self.__class__): - return spanset_gt(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __ge__(self, other): - """ - Return whether ``self`` is greater than or equal to ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if greater than or equal, False otherwise - - MEOS Functions: - spanset_ge - """ - if isinstance(other, self.__class__): - return spanset_ge(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - # ------------------------- Plot Operations ------------------------------- - def plot(self, *args, **kwargs): - from ..plotters import TimePlotter - return TimePlotter.plot_periodset(self, *args, **kwargs) - - # ------------------------- Database Operations --------------------------- - @staticmethod - def read_from_cursor(value, _=None): - """ - Reads a :class:`PeriodSet` from a database cursor. Used when automatically loading objects from the database. - Users should use the class constructor instead. - """ - if not value: - return None - return PeriodSet(string=value) - diff --git a/pymeos/pymeos/old_time/time.py b/pymeos/pymeos/old_time/time.py deleted file mode 100644 index 336a665d..00000000 --- a/pymeos/pymeos/old_time/time.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import Union - -from .period import Period -from .periodset import PeriodSet -from .timestampset import TimestampSet -from datetime import datetime - -Time = Union[datetime, TimestampSet, Period, PeriodSet] -""" -Union type that includes all Time types in PyMEOS: - -- :class:`~datetime.datetime` for timestamps -- :class:`~pymeos.time.timestampset.TimestampSet` for sets of timestamps -- :class:`~pymeos.time.period.Period` for periods of time -- :class:`~pymeos.time.periodset.PeriodSet` for sets of periods of time -""" \ No newline at end of file diff --git a/pymeos/pymeos/old_time/timestampset.py b/pymeos/pymeos/old_time/timestampset.py deleted file mode 100644 index 4e1a1dd0..00000000 --- a/pymeos/pymeos/old_time/timestampset.py +++ /dev/null @@ -1,980 +0,0 @@ -from __future__ import annotations - -from datetime import datetime, timedelta -from typing import Optional, List, Union, TYPE_CHECKING, overload, get_args - -from dateutil.parser import parse -from pymeos_cffi import * - -if TYPE_CHECKING: - from ..temporal import Temporal - from .period import Period - from .periodset import PeriodSet - from .time import Time - from ..boxes import Box - - -class TimestampSet: - """ - Class for representing lists of distinct timestamp values. - - ``TimestampSet`` objects can be created with a single argument of type string - as in MobilityDB. - - >>> TimestampSet(string='{2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01, 2019-09-11 00:00:00+01}') - - Another possibility is to give a tuple or list of composing timestamps, - which can be instances of ``str`` or ``datetime``. The composing timestamps - must be given in increasing order. - - >>> TimestampSet(timestamp_list=['2019-09-08 00:00:00+01', '2019-09-10 00:00:00+01', '2019-09-11 00:00:00+01']) - >>> TimestampSet(timestamp_list=[parse('2019-09-08 00:00:00+01'), parse('2019-09-10 00:00:00+01'), parse('2019-09-11 00:00:00+01')]) - - """ - - __slots__ = ['_inner'] - - # ------------------------- Constructors ---------------------------------- - def __init__(self, string: Optional[str] = None, *, timestamp_list: Optional[List[Union[str, datetime]]] = None, - _inner=None): - super().__init__() - assert (_inner is not None) or ((string is not None) != (timestamp_list is not None)), \ - "Either string must be not None or timestamp_list must be not" - if _inner is not None: - self._inner = _inner - elif string is not None: - self._inner = timestampset_in(string) - else: - times = [pg_timestamp_in(ts, -1) if isinstance(ts, str) else datetime_to_timestamptz(ts) - for ts in timestamp_list] - self._inner = timestampset_make(times, len(times)) - - def __copy__(self): - """ - Return a copy of ``self``. - - Returns: - A new :class:`Period` instance - - MEOS Functions: - set_copy - """ - inner_copy = set_copy(self._inner) - return TimestampSet(_inner=inner_copy) - - @staticmethod - def from_wkb(wkb: bytes) -> TimestampSet: - """ - Returns a `TimestampSet` from its WKB representation. - Args: - wkb: WKB representation - - Returns: - A new :class:`TimestampSet` instance - - MEOS Functions: - set_from_wkb - """ - return TimestampSet(_inner=(set_from_wkb(wkb))) - - @staticmethod - def from_hexwkb(hexwkb: str) -> TimestampSet: - """ - Returns a `TimestampSet` from its WKB representation in hex-encoded ASCII. - Args: - hexwkb: WKB representation in hex-encoded ASCII - - Returns: - A new :class:`TimestampSet` instance - - MEOS Functions: - set_from_hexwkb - """ - return TimestampSet(_inner=(set_from_hexwkb(hexwkb))) - - # ------------------------- Output ---------------------------------------- - def __str__(self): - """ - Return the string representation of the content of ``self``. - - Returns: - A new :class:`str` instance - - MEOS Functions: - timestampset_out - """ - return timestampset_out(self._inner) - - def __repr__(self): - """ - Return the string representation of ``self``. - - Returns: - A new :class:`str` instance - - MEOS Functions: - set_out - """ - return (f'{self.__class__.__name__}' - f'({self})') - - def as_wkb(self) -> bytes: - """ - Returns the WKB representation of ``self``. - Returns: - A :class:`str` object with the WKB representation of ``self``. - - MEOS Functions: - set_as_wkb - """ - return set_as_wkb(self._inner, 4) - - def as_hexwkb(self) -> str: - """ - Returns the WKB representation of ``self`` in hex-encoded ASCII. - Returns: - A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. - - MEOS Functions: - set_as_hexwkb - """ - return set_as_hexwkb(self._inner, -1)[0] - - # ------------------------- Conversions ----------------------------------- - def to_periodset(self) -> PeriodSet: - """ - Returns a PeriodSet that contains a Period for each Timestamp in ``self``. - - Returns: - A new :class:`PeriodSet` instance - - MEOS Functions: - set_to_spanset - """ - from .periodset import PeriodSet - return PeriodSet(_inner=set_to_spanset(self._inner)) - - # ------------------------- Accessors ------------------------------------- - def duration(self) -> timedelta: - """ - Returns the duration of the time ignoring gaps, i.e. the duration from the - first timestamp to the last one. - - Returns: - A :class:`datetime.timedelta` instance representing the duration of the period - - MEOS Functions: - period_duration - """ - return interval_to_timedelta(period_duration(set_span(self._inner))) - - def period(self) -> Period: - """ - Returns a period that encompasses ``self``. - - Returns: - A new :class:`Period` instance - - MEOS Functions: - set_span - """ - from .period import Period - return Period(_inner=set_span(self._inner)) - - def num_timestamps(self) -> int: - """ - Returns the number of timestamps in ``self``. - Returns: - An :class:`int` - - MEOS Functions: - set_num_values - """ - return set_num_values(self._inner) - - def start_timestamp(self) -> datetime: - """ - Returns the first timestamp in ``self``. - Returns: - A :class:`datetime` instance - - MEOS Functions: - timestampset_start_timestamp - """ - return timestamptz_to_datetime(timestampset_start_timestamp(self._inner)) - - def end_timestamp(self) -> datetime: - """ - Returns the last timestamp in ``self``. - Returns: - A :class:`datetime` instance - - MEOS Functions: - timestampset_end_timestamp - """ - return timestamptz_to_datetime(timestampset_end_timestamp(self._inner)) - - def timestamp_n(self, n: int) -> datetime: - """ - Returns the n-th timestamp in ``self``. - Returns: - A :class:`datetime` instance - - MEOS Functions: - timestampset_timestamp_n - """ - result = timestampset_timestamp_n(self._inner, n + 1) - if result is None: - raise IndexError(f"Index {n} out of range 0 - {self.num_timestamps() - 1}") - return timestamptz_to_datetime(result) - - def timestamps(self) -> List[datetime]: - """ - Returns the list of distinct timestamps in ``self``. - Returns: - A :class:`list[datetime]` instance - - MEOS Functions: - timestampset_timestamps - """ - tss = timestampset_values(self._inner) - return [timestamptz_to_datetime(tss[i]) for i in range(self.num_timestamps())] - - def __hash__(self) -> int: - """ - Return the hash representation of ``self``. - - Returns: - A new :class:`int` instance - - MEOS Functions: - set_hash - """ - return set_hash(self._inner) - - # ------------------------- Transformations ------------------------------- - def shift(self, delta: timedelta) -> TimestampSet: - """ - Returns a new TimestampSet that is the result of shifting ``self`` by ``delta`` - - Examples: - >>> TimestampSet('{2000-01-01, 2000-01-10}').shift(timedelta(days=2)) - >>> 'TimestampSet({2000-01-03 00:00:00+01, 2000-01-12 00:00:00+01})' - - Args: - delta: :class:`datetime.timedelta` instance to shift - - Returns: - A new :class:`PeriodSet` instance - - MEOS Functions: - timestampset_shift_tscale - """ - return self.shift_tscale(shift=delta) - - def tscale(self, duration: timedelta) -> TimestampSet: - """ - Returns a new TimestampSet that with the scaled so that the span of ``self`` is ``duration``. - - Examples: - >>> TimestampSet('{2000-01-01, 2000-01-10}').tscale(timedelta(days=2)) - >>> 'TimestampSet({2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01})' - - Args: - duration: :class:`datetime.timedelta` instance representing the span of the new set - - Returns: - A new :class:`PeriodSet` instance - - MEOS Functions: - timestampset_shift_tscale - """ - return self.shift_tscale(duration=duration) - - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TimestampSet: - """ - Returns a new TimestampSet that is the result of shifting and scaling ``self``. - - Examples: - >>> TimestampSet('{2000-01-01, 2000-01-10}').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) - >>> 'TimestampSet({2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01})' - - Args: - shift: :class:`datetime.timedelta` instance to shift - duration: :class:`datetime.timedelta` instance representing the span of the new set - - Returns: - A new :class:`PeriodSet` instance - - MEOS Functions: - timestampset_shift_tscale - """ - assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' - tss = timestampset_shift_tscale( - self._inner, - timedelta_to_interval(shift) if shift else None, - timedelta_to_interval(duration) if duration else None - ) - return TimestampSet(_inner=tss) - - # ------------------------- Topological Operations ------------------------ - def is_adjacent(self, other: Union[Period, PeriodSet, Temporal, Box]) -> bool: - """ - Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them - contains it. - - Examples: - >>> TimestampSet('{2012-01-01, 2012-01-02}').is_adjacent(Period('[2012-01-02, 2012-01-03]')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').is_adjacent(Period('[2012-01-02, 2012-01-03]')) - >>> False # Both contain bound - >>> TimestampSet('{2012-01-01, 2012-01-02}').is_adjacent(Period('(2012-01-02, 2012-01-03]')) - >>> False # Neither contain bound - - Args: - other: temporal object to compare with - - Returns: - True if adjacent, False otherwise - - MEOS Functions: - adjacent_span_span, adjacent_spanset_span - """ - from .period import Period - from .periodset import PeriodSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, Period): - return adjacent_span_span(set_span(self._inner), other._inner) - elif isinstance(other, PeriodSet): - return adjacent_spanset_spanset(other._inner, set_to_spanset(self._inner)) - elif isinstance(other, Temporal): - return adjacent_span_span(set_span(self._inner), temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return adjacent_span_span(set_span(self._inner), other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_contained_in(self, container: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) -> bool: - """ - Returns whether ``self`` is temporally contained in ``container``. - - Examples: - >>> TimestampSet('{2012-01-02, 2012-01-03}').is_contained_in(Period('[2012-01-01, 2012-01-04]')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').is_contained_in(Period('[2012-01-01, 2012-01-02]')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').is_contained_in(Period('(2012-01-01, 2012-01-02)')) - >>> False - - Args: - container: temporal object to compare with - - Returns: - True if contained, False otherwise - - MEOS Functions: - contained_span_span, contained_span_spanset, contained_set_set, contained_spanset_spanset - """ - from .period import Period - from .periodset import PeriodSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(container, Period): - return contained_span_span(set_span(self._inner), container._inner) - elif isinstance(container, PeriodSet): - return contained_span_spanset(set_span(self._inner), container._inner) - elif isinstance(container, TimestampSet): - return contained_set_set(self._inner, container._inner) - elif isinstance(container, Temporal): - return contained_spanset_spanset(set_to_spanset(self._inner), temporal_time(container._inner)) - elif isinstance(container, Box): - return contained_span_span(set_span(self._inner), container.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {container.__class__}') - - def contains(self, content: Union[datetime, TimestampSet, Temporal]) -> bool: - """ - Returns whether ``self`` temporally contains ``content``. - - Examples: - >>> TimestampSet('{2012-01-01, 2012-01-04}').contains(parse('2012-01-01]')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').contains(TimestampSet('{2012-01-01}')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').contains(TimestampSet('{2012-01-01, 2012-01-03}')) - >>> False - - Args: - content: temporal object to compare with - - Returns: - True if contains, False otherwise - - MEOS Functions: - contains_timestampset_timestamp, contains_set_set, contains_spanset_spanset - """ - from ..temporal import Temporal - if isinstance(content, datetime): - return contains_timestampset_timestamp(self._inner, datetime_to_timestamptz(content)) - elif isinstance(content, TimestampSet): - return contains_set_set(self._inner, content._inner) - elif isinstance(content, Temporal): - return contains_spanset_spanset(set_to_spanset(self._inner), temporal_time(content._inner)) - else: - raise TypeError(f'Operation not supported with type {content.__class__}') - - def __contains__(self, item): - """ - Returns whether ``self`` temporally contains ``content``. - - Examples: - >>> TimestampSet('{2012-01-01, 2012-01-04}').contains(parse('2012-01-01]')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').contains(TimestampSet('{2012-01-01}')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').contains(TimestampSet('{2012-01-01, 2012-01-03}')) - >>> False - - Args: - item: temporal object to compare with - - Returns: - True if contains, False otherwise - - MEOS Functions: - contains_timestampset_timestamp, contains_set_set, contains_spanset_spanset - """ - return self.contains(item) - - def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) -> bool: - """ - Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant - - Examples: - >>> TimestampSet('{2012-01-01, 2012-01-02}').overlaps(TimestampSet('{2012-01-02, 2012-01-03}')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').overlaps(Period('[2012-01-02, 2012-01-03]')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').overlaps(Period('(2012-01-02, 2012-01-03]')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if overlaps, False otherwise - - MEOS Functions: - overlaps_set_set, overlaps_span_span, overlaps_spanset_spanset - """ - from .period import Period - from .periodset import PeriodSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, datetime): - return contains_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) - elif isinstance(other, TimestampSet): - return overlaps_set_set(self._inner, other._inner) - elif isinstance(other, Period): - return overlaps_span_span(set_span(self._inner), other._inner) - elif isinstance(other, PeriodSet): - return overlaps_spanset_spanset(set_to_spanset(self._inner), other._inner) - elif isinstance(other, Temporal): - return overlaps_spanset_spanset(set_to_spanset(self._inner), temporal_time(other._inner)) - elif isinstance(other, Box): - return overlaps_span_span(set_span(self._inner), other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_same(self, other: Union[Time, Temporal, Box]) -> bool: - """ - Returns whether the bounding period of `self` is the same as the bounding period of `other`. - - Args: - other: A time or temporal object to compare to `self`. - - Returns: - True if same, False otherwise. - - See Also: - :meth:`Period.is_same` - """ - return self.to_period().is_same(other) - - # ------------------------- Position Operations --------------------------- - def is_after(self, other: Union[Time, Temporal, Box]) -> bool: - """ - Returns whether ``self`` is strictly after ``other``. That is, the first timestamp in ``self`` - is after ``other``. - - Examples: - >>> TimestampSet('{2012-01-02, 2012-01-03}').is_after(Period('[2012-01-01, 2012-01-02)')) - >>> True - >>> TimestampSet('{2012-01-02, 2012-01-03}').is_after(TimestampSet('{2012-01-01}')) - >>> True - >>> TimestampSet('{2012-01-02, 2012-01-03}').is_after(Period('[2012-01-01, 2012-01-02]')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if after, False otherwise - - MEOS Functions: - overbefore_timestamp_timestampset, right_set_set, right_span_span, right_span_spanset - """ - from .period import Period - from .periodset import PeriodSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, datetime): - return overbefore_timestamp_timestampset(datetime_to_timestamptz(other), self._inner) - elif isinstance(other, TimestampSet): - return right_set_set(self._inner, other._inner) - elif isinstance(other, Period): - return right_span_span(set_span(self._inner), other._inner) - elif isinstance(other, PeriodSet): - return right_span_spanset(set_span(self._inner), other._inner) - elif isinstance(other, Temporal): - return right_span_span(set_span(self._inner), temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return right_span_span(set_span(self._inner), other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_before(self, other: Union[Time, Temporal, Box]) -> bool: - """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. - - Examples: - >>> TimestampSet('{2012-01-01, 2012-01-02}').is_before(TimestampSet('{2012-01-03}')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').is_before(Period('(2012-01-02, 2012-01-03]')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').is_before(Period('[2012-01-02, 2012-01-03]')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if before, False otherwise - - MEOS Functions: - overafter_timestamp_period, left_span_span, left_span_spanset - """ - from .period import Period - from .periodset import PeriodSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, datetime): - return overafter_timestamp_period(datetime_to_timestamptz(other), set_span(self._inner)) - elif isinstance(other, TimestampSet): - return left_set_set(self._inner, other._inner) - elif isinstance(other, Period): - return left_span_span(set_span(self._inner), other._inner) - elif isinstance(other, PeriodSet): - return left_span_spanset(set_span(self._inner), other._inner) - elif isinstance(other, Temporal): - return left_span_span(set_span(self._inner), temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return left_span_span(set_span(self._inner), other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_over_or_after(self, other: Union[Time, Temporal, Box]) -> bool: - """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). - - Examples: - >>> TimestampSet('{2012-01-02, 2012-01-03}').is_over_or_after(Period('[2012-01-01, 2012-01-02)')) - >>> True - >>> TimestampSet('{2012-01-02, 2012-01-03}').is_over_or_after(Period('[2012-01-01, 2012-01-02]')) - >>> True - >>> TimestampSet('{2012-01-02, 2012-01-03}').is_over_or_after(Period('[2012-01-01, 2012-01-03]')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if overlapping or after, False otherwise - - MEOS Functions: - overafter_period_timestamp, overright_span_span, overright_span_spanset - """ - from .period import Period - from .periodset import PeriodSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, datetime): - return overafter_period_timestamp(set_span(self._inner), datetime_to_timestamptz(other)) - elif isinstance(other, TimestampSet): - return overright_set_set(self._inner, other._inner) - elif isinstance(other, Period): - return overright_span_span(set_span(self._inner), other._inner) - elif isinstance(other, PeriodSet): - return overright_span_spanset(set_span(self._inner), other._inner) - elif isinstance(other, Temporal): - return overright_span_span(set_span(self._inner), temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return overright_span_span(set_span(self._inner), other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def is_over_or_before(self, other: Union[Time, Temporal, Box]) -> bool: - """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). - - Examples: - >>> TimestampSet('{2012-01-01, 2012-01-02}').is_over_or_before(Period('[2012-01-02, 2012-01-03]')) - >>> True - >>> TimestampSet('{2012-01-01, 2012-01-02}').is_over_or_before(Period('[2012-01-02, 2012-01-03]')) - >>> True - >>> TimestampSet('{2012-01-03, 2012-01-05}').is_over_or_before(Period('[2012-01-01, 2012-01-04]')) - >>> False - - Args: - other: temporal object to compare with - - Returns: - True if before, False otherwise - - MEOS Functions: - overbefore_period_timestamp, overleft_span_span, overleft_span_spanset - """ - from .period import Period - from .periodset import PeriodSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, datetime): - return overbefore_period_timestamp(set_span(self._inner), datetime_to_timestamptz(other)) - if isinstance(other, TimestampSet): - return overleft_set_set(self._inner, other._inner) - if isinstance(other, Period): - return overleft_span_span(set_span(self._inner), other._inner) - if isinstance(other, PeriodSet): - return overleft_span_spanset(set_span(self._inner), other._inner) - elif isinstance(other, Temporal): - return overleft_span_span(set_span(self._inner), temporal_to_period(other._inner)) - elif isinstance(other, get_args(Box)): - return overleft_span_span(set_span(self._inner), other.to_period()._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - # ------------------------- Distance Operations --------------------------- - def distance(self, other: Union[Time, Temporal, Box]) -> timedelta: - """ - Returns the temporal distance between ``self`` and ``other``. - - Args: - other: temporal object to compare with - - Returns: - A :class:`datetime.timedelta` instance - - MEOS Functions: - distance_timestampset_timestamp, distance_set_set, distance_span_span, distance_spanset_span - """ - from .period import Period - from .periodset import PeriodSet - from ..temporal import Temporal - from ..boxes import Box - if isinstance(other, datetime): - return timedelta(seconds=distance_timestampset_timestamp(self._inner, datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return timedelta(seconds=distance_set_set(self._inner, other._inner)) - elif isinstance(other, Period): - return timedelta(seconds=distance_span_span(set_span(self._inner), other._inner)) - elif isinstance(other, PeriodSet): - return timedelta(seconds=distance_spanset_span(other._inner, set_span(self._inner))) - elif isinstance(other, Temporal): - return timedelta(seconds=distance_span_span(set_span(self._inner), temporal_to_period(other._inner))) - elif isinstance(other, get_args(Box)): - return timedelta(seconds=distance_span_span(set_span(self._inner), other.to_period()._inner)) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - # ------------------------- Set Operations -------------------------------- - @overload - def intersection(self, other: datetime) -> Optional[datetime]: - ... - - @overload - def intersection(self, other: TimestampSet) -> Optional[TimestampSet]: - ... - - @overload - def intersection(self, other: Union[Period, PeriodSet, Temporal]) -> Optional[PeriodSet]: - ... - - def intersection(self, other: Union[Time, Temporal]) -> Optional[Time]: - """ - Returns the temporal intersection of ``self`` and ``other``. - - Args: - other: temporal object to intersect with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - intersection_set_set, intersection_spanset_span, intersection_spanset_spanset - """ - from .period import Period - from .periodset import PeriodSet - if isinstance(other, datetime): - result = intersection_set_set(self._inner, timestamp_to_tstzset(datetime_to_timestamptz(other))) - return timestamptz_to_datetime(result) if result is not None else None - elif isinstance(other, TimestampSet): - result = intersection_set_set(self._inner, other._inner) - return TimestampSet(_inner=result) if result is not None else None - elif isinstance(other, Period): - result = intersection_spanset_span(set_to_spanset(self._inner), other._inner) - return PeriodSet(_inner=result) if result is not None else None - elif isinstance(other, PeriodSet): - result = intersection_spanset_spanset(set_to_spanset(self._inner), other._inner) - return PeriodSet(_inner=result) if result is not None else None - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __mul__(self, other): - """ - Returns the temporal intersection of ``self`` and ``other``. - - Args: - other: temporal object to intersect with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - intersection_set_set, intersection_spanset_span, intersection_spanset_spanset - """ - return self.intersection(other) - - @overload - def minus(self, other: Union[datetime, TimestampSet]) -> Optional[TimestampSet]: - ... - - @overload - def minus(self, other: Union[Period, PeriodSet]) -> Optional[PeriodSet]: - ... - - def minus(self, other: Time) -> Optional[Time]: - """ - Returns the temporal difference of ``self`` and ``other``. - - Args: - other: temporal object to diff with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - minus_timestampset_timestamp, minus_set_set, minus_spanset_span, minus_spanset_spanset - """ - from .period import Period - from .periodset import PeriodSet - if isinstance(other, datetime): - result = minus_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) - return TimestampSet(_inner=result) if result is not None else None - elif isinstance(other, TimestampSet): - result = minus_set_set(self._inner, other._inner) - return TimestampSet(_inner=result) if result is not None else None - elif isinstance(other, Period): - result = minus_spanset_span(set_to_spanset(self._inner), other._inner) - return PeriodSet(_inner=result) if result is not None else None - elif isinstance(other, PeriodSet): - result = minus_spanset_spanset(set_to_spanset(self._inner), other._inner) - return PeriodSet(_inner=result) if result is not None else None - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __sub__(self, other): - """ - Returns the temporal difference of ``self`` and ``other``. - - Args: - other: temporal object to diff with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - minus_timestampset_timestamp, minus_set_set, minus_spanset_span, minus_spanset_spanset - """ - return self.minus(other) - - @overload - def union(self, other: Union[datetime, TimestampSet]) -> TimestampSet: - ... - - @overload - def union(self, other: Union[Period, PeriodSet]) -> PeriodSet: - ... - - def union(self, other: Time) -> Union[PeriodSet, TimestampSet]: - """ - Returns the temporal union of ``self`` and ``other``. - - Args: - other: temporal object to merge with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - union_timestampset_timestamp, union_set_set, union_spanset_span, union_spanset_spanset - """ - from .period import Period - from .periodset import PeriodSet - if isinstance(other, datetime): - return TimestampSet(_inner=union_timestampset_timestamp(self._inner, datetime_to_timestamptz(other))) - elif isinstance(other, TimestampSet): - return TimestampSet(_inner=union_set_set(self._inner, other._inner)) - elif isinstance(other, Period): - return PeriodSet(_inner=union_spanset_span(set_to_spanset(self._inner), other._inner)) - elif isinstance(other, PeriodSet): - return PeriodSet(_inner=union_spanset_spanset(set_to_spanset(self._inner), other._inner)) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __add__(self, other): - """ - Returns the temporal union of ``self`` and ``other``. - - Args: - other: temporal object to merge with - - Returns: - A :class:`Time` instance. The actual class depends on ``other``. - - MEOS Functions: - union_timestampset_timestamp, union_set_set, union_spanset_span, union_spanset_spanset - """ - return self.union(other) - - # ------------------------- Comparisons ----------------------------------- - def __eq__(self, other): - """ - Returns whether ``self`` and ``other`` are equal. - - Args: - other: temporal object to compare with - - Returns: - True if equal, False otherwise - - MEOS Functions: - set_eq - """ - if isinstance(other, self.__class__): - return set_eq(self._inner, other._inner) - return False - - def __ne__(self, other): - """ - Returns whether ``self`` and ``other`` are not equal. - - Args: - other: temporal object to compare with - - Returns: - True if not equal, False otherwise - - MEOS Functions: - set_ne - """ - if isinstance(other, self.__class__): - return set_ne(self._inner, other._inner) - return True - - def __lt__(self, other): - """ - Return whether ``self`` is less than ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if less than, False otherwise - - MEOS Functions: - set_lt - """ - if isinstance(other, self.__class__): - return set_lt(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __le__(self, other): - """ - Return whether ``self`` is less than or equal to ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if less than or equal, False otherwise - - MEOS Functions: - set_le - """ - if isinstance(other, self.__class__): - return set_le(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __gt__(self, other): - """ - Return whether ``self`` is greater than ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if greater than, False otherwise - - MEOS Functions: - set_gt - """ - if isinstance(other, self.__class__): - return set_gt(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - def __ge__(self, other): - """ - Return whether ``self`` is greater than or equal to ``other``. - - Args: - other: temporal object to compare with - - Returns: - True if greater than or equal, False otherwise - - MEOS Functions: - set_ge - """ - if isinstance(other, self.__class__): - return set_ge(self._inner, other._inner) - raise TypeError(f'Operation not supported with type {other.__class__}') - - # ------------------------- Plot Operations ------------------------------- - def plot(self, *args, **kwargs): - from ..plotters import TimePlotter - return TimePlotter.plot_timestampset(self, *args, **kwargs) - - # ------------------------- Database Operations --------------------------- - @staticmethod - def read_from_cursor(value, _=None): - """ - Reads a :class:`TimestampSet` from a database cursor. Used when automatically loading objects from the database. - Users should use the class constructor instead. - """ - if not value: - return None - return TimestampSet(string=value) - diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index b4961ee8..aa5c5336 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -13,7 +13,7 @@ from .tinstant import TInstant from .tsequence import TSequence from ..main import TBool - from ..boxes import Box, TBox + from ..boxes import Box TBase = TypeVar('TBase') TG = TypeVar('TG', bound='Temporal[Any]') TI = TypeVar('TI', bound='TInstant[Any]') @@ -223,7 +223,7 @@ def as_hexwkb(self) -> str: return temporal_as_hexwkb(self._inner, 4)[0] # ------------------------- Accessors ------------------------------------- - def bounding_box(self) -> TBox: + def bounding_box(self) -> Union[Period, Box]: """ Returns the bounding box of `self`. @@ -552,7 +552,7 @@ def temporal_sample(self, duration: Union[str, timedelta], if isinstance(duration, timedelta): dt = timedelta_to_interval(duration) else: - pg_interval_in(duration, -1) + dt = pg_interval_in(duration, -1) result = temporal_tsample(self._inner, dt, st) return Temporal._factory(result) @@ -578,7 +578,7 @@ def temporal_precision(self, duration: Union[str, timedelta], if isinstance(duration, timedelta): dt = timedelta_to_interval(duration) else: - pg_interval_in(duration, -1) + dt = pg_interval_in(duration, -1) result = temporal_tprecision(self._inner, dt, st) return Temporal._factory(result) From 980bdeab9ac9510e5f96be18deb0eb6e63945c4c Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 21 Sep 2023 11:32:28 +0200 Subject: [PATCH 027/101] Add new modules --- pymeos/pymeos/collections/geo/__init__.py | 0 pymeos/pymeos/collections/number/__init__.py | 0 pymeos/pymeos/collections/text/__init__.py | 0 pymeos/pymeos/main/tpoint.py | 64 ++++++++++---------- 4 files changed, 32 insertions(+), 32 deletions(-) create mode 100644 pymeos/pymeos/collections/geo/__init__.py create mode 100644 pymeos/pymeos/collections/number/__init__.py create mode 100644 pymeos/pymeos/collections/text/__init__.py diff --git a/pymeos/pymeos/collections/geo/__init__.py b/pymeos/pymeos/collections/geo/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymeos/pymeos/collections/number/__init__.py b/pymeos/pymeos/collections/number/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymeos/pymeos/collections/text/__init__.py b/pymeos/pymeos/collections/text/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index d79a9066..80f63fbb 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -1096,38 +1096,38 @@ class TPointSeq(TSequence[shpb.BaseGeometry, TG, TI, TS, TSS], TPoint[TG, TI, TS Abstract class for temporal point sequences. """ - @staticmethod - def from_arrays(t: List[Union[datetime, str]], x: List[float], y: List[float], z: Optional[List[float]] = None, - srid: int = 0, geodetic: bool = False, lower_inc: bool = True, upper_inc: bool = False, - interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True) -> TPointSeq: - """ - Creates a temporal point sequence from arrays of timestamps and coordinates. - - Args: - t: The array of timestamps. - x: The array of x coordinates. - y: The array of y coordinates. - z: The array of z coordinates. - srid: The spatial reference system identifier. - geodetic: Whether the coordinates are geodetic. - lower_inc: Whether the lower bound is inclusive. - upper_inc: Whether the upper bound is inclusive. - interpolation: The interpolation method. - normalize: Whether to normalize the timestamps. - - Returns: - A new :class:`TPointSeq` object. - - MEOS Functions: - tpointseq_make_coords - """ - from ..factory import _TemporalFactory - assert len(t) == len(x) == len(y) - times = [datetime_to_timestamptz(ti) if isinstance(ti, datetime) else pg_timestamptz_in(ti, -1) for ti in t] - return _TemporalFactory.create_temporal( - tpointseq_make_coords(x, y, z, times, len(t), srid, geodetic, lower_inc, upper_inc, interpolation, - normalize) - ) + # @staticmethod + # def from_arrays(t: List[Union[datetime, str]], x: List[float], y: List[float], z: Optional[List[float]] = None, + # srid: int = 0, geodetic: bool = False, lower_inc: bool = True, upper_inc: bool = False, + # interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True) -> TPointSeq: + # """ + # Creates a temporal point sequence from arrays of timestamps and coordinates. + # + # Args: + # t: The array of timestamps. + # x: The array of x coordinates. + # y: The array of y coordinates. + # z: The array of z coordinates. + # srid: The spatial reference system identifier. + # geodetic: Whether the coordinates are geodetic. + # lower_inc: Whether the lower bound is inclusive. + # upper_inc: Whether the upper bound is inclusive. + # interpolation: The interpolation method. + # normalize: Whether to normalize the timestamps. + # + # Returns: + # A new :class:`TPointSeq` object. + # + # MEOS Functions: + # tpointseq_make_coords + # """ + # from ..factory import _TemporalFactory + # assert len(t) == len(x) == len(y) + # times = [datetime_to_timestamptz(ti) if isinstance(ti, datetime) else pg_timestamptz_in(ti, -1) for ti in t] + # return _TemporalFactory.create_temporal( + # tpointseq_make_coords(x, y, z, times, len(t), srid, geodetic, lower_inc, upper_inc, interpolation, + # normalize) + # ) def plot(self, *args, **kwargs): """ From 6547d8f001eb2711a3dff02fddf58047e978b288 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 21 Sep 2023 12:09:50 +0200 Subject: [PATCH 028/101] Fix merge issues --- pymeos/pymeos/collections/time/period.py | 20 +- pymeos/pymeos/collections/time/periodset.py | 20 +- .../pymeos/collections/time/timestampset.py | 20 +- pymeos_cffi/pymeos_cffi/__init__.py | 478 +-- pymeos_cffi/pymeos_cffi/builder/meos.h | 593 +-- pymeos_cffi/pymeos_cffi/functions.py | 3552 +++++++++-------- 6 files changed, 2361 insertions(+), 2322 deletions(-) diff --git a/pymeos/pymeos/collections/time/period.py b/pymeos/pymeos/collections/time/period.py index 9224d5a6..f620b1ca 100644 --- a/pymeos/pymeos/collections/time/period.py +++ b/pymeos/pymeos/collections/time/period.py @@ -150,16 +150,16 @@ def shift(self, delta: timedelta) -> Period: A new :class:`Period` instance MEOS Functions: - period_shift_tscale + period_shift_scale """ - return self.shift_tscale(shift=delta) + return self.shift_scale(shift=delta) - def tscale(self, duration: timedelta) -> Period: + def scale(self, duration: timedelta) -> Period: """ Returns a new period that starts as ``self`` but has duration ``duration`` Examples: - >>> Period('[2000-01-01, 2000-01-10]').tscale(timedelta(days=2)) + >>> Period('[2000-01-01, 2000-01-10]').scale(timedelta(days=2)) >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' Args: @@ -169,16 +169,16 @@ def tscale(self, duration: timedelta) -> Period: A new :class:`Period` instance MEOS Functions: - period_shift_tscale + period_shift_scale """ - return self.shift_tscale(duration=duration) + return self.shift_scale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> Period: + def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> Period: """ Returns a new period that starts at ``self`` shifted by ``shift`` and has duration ``duration`` Examples: - >>> Period('[2000-01-01, 2000-01-10]').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) + >>> Period('[2000-01-01, 2000-01-10]').shift_scale(shift=timedelta(days=2), duration=timedelta(days=4)) >>> 'Period([2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01])' Args: @@ -189,10 +189,10 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim A new :class:`Period` instance MEOS Functions: - period_shift_tscale + period_shift_scale """ assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' - modified = period_shift_tscale( + modified = period_shift_scale( self._inner, timedelta_to_interval(shift) if shift else None, timedelta_to_interval(duration) if duration else None, diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py index abab4f48..3699d094 100644 --- a/pymeos/pymeos/collections/time/periodset.py +++ b/pymeos/pymeos/collections/time/periodset.py @@ -345,16 +345,16 @@ def shift(self, delta: timedelta) -> PeriodSet: A new :class:`PeriodSet` instance MEOS Functions: - periodset_shift_tscale + periodset_shift_scale """ - return self.shift_tscale(shift=delta) + return self.shift_scale(shift=delta) - def tscale(self, duration: timedelta) -> PeriodSet: + def scale(self, duration: timedelta) -> PeriodSet: """ Returns a new periodset that starts as ``self`` but has duration ``duration`` Examples: - >>> Period('[2000-01-01, 2000-01-10]').tscale(timedelta(days=2)) + >>> Period('[2000-01-01, 2000-01-10]').scale(timedelta(days=2)) >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' Args: @@ -364,16 +364,16 @@ def tscale(self, duration: timedelta) -> PeriodSet: A new :class:`PeriodSet` instance MEOS Functions: - periodset_shift_tscale + periodset_shift_scale """ - return self.shift_tscale(duration=duration) + return self.shift_scale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> PeriodSet: + def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> PeriodSet: """ Returns a new periodset that starts at ``self`` shifted by ``shift`` and has duration ``duration`` Examples: - >>> Period('[2000-01-01, 2000-01-10]').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) + >>> Period('[2000-01-01, 2000-01-10]').shift_scale(shift=timedelta(days=2), duration=timedelta(days=4)) >>> 'Period([2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01])' Args: @@ -384,10 +384,10 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim A new :class:`PeriodSet` instance MEOS Functions: - periodset_shift_tscale + periodset_shift_scale """ assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' - ps = periodset_shift_tscale( + ps = periodset_shift_scale( self._inner, timedelta_to_interval(shift) if shift else None, timedelta_to_interval(duration) if duration else None diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py index efe6d5eb..a0290d9d 100644 --- a/pymeos/pymeos/collections/time/timestampset.py +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -183,16 +183,16 @@ def shift(self, delta: timedelta) -> TimestampSet: A new :class:`PeriodSet` instance MEOS Functions: - timestampset_shift_tscale + timestampset_shift_scale """ - return self.shift_tscale(shift=delta) + return self.shift_scale(shift=delta) - def tscale(self, duration: timedelta) -> TimestampSet: + def scale(self, duration: timedelta) -> TimestampSet: """ Returns a new TimestampSet that with the scaled so that the span of ``self`` is ``duration``. Examples: - >>> TimestampSet('{2000-01-01, 2000-01-10}').tscale(timedelta(days=2)) + >>> TimestampSet('{2000-01-01, 2000-01-10}').scale(timedelta(days=2)) >>> 'TimestampSet({2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01})' Args: @@ -202,16 +202,16 @@ def tscale(self, duration: timedelta) -> TimestampSet: A new :class:`PeriodSet` instance MEOS Functions: - timestampset_shift_tscale + timestampset_shift_scale """ - return self.shift_tscale(duration=duration) + return self.shift_scale(duration=duration) - def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TimestampSet: + def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TimestampSet: """ Returns a new TimestampSet that is the result of shifting and scaling ``self``. Examples: - >>> TimestampSet('{2000-01-01, 2000-01-10}').shift_tscale(shift=timedelta(days=2), duration=timedelta(days=4)) + >>> TimestampSet('{2000-01-01, 2000-01-10}').shift_scale(shift=timedelta(days=2), duration=timedelta(days=4)) >>> 'TimestampSet({2000-01-03 00:00:00+01, 2000-01-07 00:00:00+01})' Args: @@ -222,10 +222,10 @@ def shift_tscale(self, shift: Optional[timedelta] = None, duration: Optional[tim A new :class:`PeriodSet` instance MEOS Functions: - timestampset_shift_tscale + timestampset_shift_scale """ assert shift is not None or duration is not None, 'shift and scale deltas must not be both None' - tss = timestampset_shift_tscale( + tss = timestampset_shift_scale( self._inner, timedelta_to_interval(shift) if shift else None, timedelta_to_interval(duration) if duration else None diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index cb3bcde3..59eee3f4 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -261,6 +261,75 @@ 'periodset_tprecision', 'timestamp_tprecision', 'timestampset_shift_scale', + 'intersection_bigintset_bigint', + 'intersection_bigintspan_bigint', + 'intersection_bigintspanset_bigint', + 'intersection_floatset_float', + 'intersection_floatspan_float', + 'intersection_floatspanset_float', + 'intersection_geoset_geo', + 'intersection_intset_int', + 'intersection_intspan_int', + 'intersection_intspanset_int', + 'intersection_period_timestamp', + 'intersection_periodset_timestamp', + 'intersection_set_set', + 'intersection_span_span', + 'intersection_spanset_span', + 'intersection_spanset_spanset', + 'intersection_textset_text', + 'intersection_timestampset_timestamp', + 'minus_bigint_bigintset', + 'minus_bigint_bigintspan', + 'minus_bigint_bigintspanset', + 'minus_bigintset_bigint', + 'minus_bigintspan_bigint', + 'minus_bigintspanset_bigint', + 'minus_float_floatset', + 'minus_float_floatspan', + 'minus_float_floatspanset', + 'minus_floatset_float', + 'minus_floatspan_float', + 'minus_floatspanset_float', + 'minus_geo_geoset', + 'minus_geoset_geo', + 'minus_int_intset', + 'minus_int_intspan', + 'minus_int_intspanset', + 'minus_intset_int', + 'minus_intspan_int', + 'minus_intspanset_int', + 'minus_period_timestamp', + 'minus_periodset_timestamp', + 'minus_set_set', + 'minus_span_span', + 'minus_span_spanset', + 'minus_spanset_span', + 'minus_spanset_spanset', + 'minus_text_textset', + 'minus_textset_text', + 'minus_timestamp_period', + 'minus_timestamp_periodset', + 'minus_timestamp_timestampset', + 'minus_timestampset_timestamp', + 'union_bigintset_bigint', + 'union_bigintspan_bigint', + 'union_bigintspanset_bigint', + 'union_floatset_float', + 'union_floatspan_float', + 'union_floatspanset_float', + 'union_geoset_geo', + 'union_intset_int', + 'union_intspan_int', + 'union_intspanset_int', + 'union_period_timestamp', + 'union_periodset_timestamp', + 'union_set_set', + 'union_span_span', + 'union_spanset_span', + 'union_spanset_spanset', + 'union_textset_text', + 'union_timestampset_timestamp', 'adjacent_bigintspan_bigint', 'adjacent_bigintspanset_bigint', 'adjacent_floatspan_float', @@ -361,75 +430,6 @@ 'right_span_spanset', 'right_spanset_span', 'right_spanset_spanset', - 'intersection_bigintset_bigint', - 'intersection_bigintspan_bigint', - 'intersection_bigintspanset_bigint', - 'intersection_floatset_float', - 'intersection_floatspan_float', - 'intersection_floatspanset_float', - 'intersection_geoset_geo', - 'intersection_intset_int', - 'intersection_intspan_int', - 'intersection_intspanset_int', - 'intersection_period_timestamp', - 'intersection_periodset_timestamp', - 'intersection_set_set', - 'intersection_span_span', - 'intersection_spanset_span', - 'intersection_spanset_spanset', - 'intersection_textset_text', - 'intersection_timestampset_timestamp', - 'minus_bigint_bigintset', - 'minus_bigint_bigintspan', - 'minus_bigint_bigintspanset', - 'minus_bigintset_bigint', - 'minus_bigintspan_bigint', - 'minus_bigintspanset_bigint', - 'minus_float_floatset', - 'minus_float_floatspan', - 'minus_float_floatspanset', - 'minus_floatset_float', - 'minus_floatspan_float', - 'minus_floatspanset_float', - 'minus_geo_geoset', - 'minus_geoset_geo', - 'minus_int_intset', - 'minus_int_intspan', - 'minus_int_intspanset', - 'minus_intset_int', - 'minus_intspan_int', - 'minus_intspanset_int', - 'minus_period_timestamp', - 'minus_periodset_timestamp', - 'minus_set_set', - 'minus_span_span', - 'minus_span_spanset', - 'minus_spanset_span', - 'minus_spanset_spanset', - 'minus_text_textset', - 'minus_textset_text', - 'minus_timestamp_period', - 'minus_timestamp_periodset', - 'minus_timestamp_timestampset', - 'minus_timestampset_timestamp', - 'union_bigintset_bigint', - 'union_bigintspan_bigint', - 'union_bigintspanset_bigint', - 'union_floatset_float', - 'union_floatspan_float', - 'union_floatspanset_float', - 'union_geoset_geo', - 'union_intset_int', - 'union_intspan_int', - 'union_intspanset_int', - 'union_period_timestamp', - 'union_periodset_timestamp', - 'union_set_set', - 'union_span_span', - 'union_spanset_span', - 'union_spanset_spanset', - 'union_textset_text', - 'union_timestampset_timestamp', 'distance_bigintset_bigint', 'distance_bigintspan_bigint', 'distance_bigintspanset_bigint', @@ -446,27 +446,6 @@ 'distance_spanset_span', 'distance_spanset_spanset', 'distance_timestampset_timestamp', - 'bigint_extent_transfn', - 'bigint_union_transfn', - 'float_extent_transfn', - 'float_union_transfn', - 'int_extent_transfn', - 'int_union_transfn', - 'period_tcount_transfn', - 'periodset_tcount_transfn', - 'set_extent_transfn', - 'set_union_finalfn', - 'set_union_transfn', - 'span_extent_transfn', - 'span_union_transfn', - 'spanset_extent_transfn', - 'spanset_union_finalfn', - 'spanset_union_transfn', - 'text_union_transfn', - 'timestamp_extent_transfn', - 'timestamp_tcount_transfn', - 'timestamp_union_transfn', - 'timestampset_tcount_transfn', 'set_cmp', 'set_eq', 'set_ge', @@ -488,6 +467,27 @@ 'spanset_le', 'spanset_lt', 'spanset_ne', + 'bigint_extent_transfn', + 'bigint_union_transfn', + 'float_extent_transfn', + 'float_union_transfn', + 'int_extent_transfn', + 'int_union_transfn', + 'period_tcount_transfn', + 'periodset_tcount_transfn', + 'set_extent_transfn', + 'set_union_finalfn', + 'set_union_transfn', + 'span_extent_transfn', + 'span_union_transfn', + 'spanset_extent_transfn', + 'spanset_union_finalfn', + 'spanset_union_transfn', + 'text_union_transfn', + 'timestamp_extent_transfn', + 'timestamp_tcount_transfn', + 'timestamp_union_transfn', + 'timestampset_tcount_transfn', 'tbox_in', 'tbox_out', 'tbox_from_wkb', @@ -500,73 +500,81 @@ 'stbox_as_hexwkb', 'stbox_in', 'stbox_out', - 'stbox_make', - 'stbox_copy', - 'tbox_make', - 'tbox_copy', - 'int_to_tbox', - 'float_to_tbox', - 'timestamp_to_tbox', - 'timestampset_to_tbox', - 'period_to_tbox', - 'periodset_to_tbox', - 'int_timestamp_to_tbox', 'float_period_to_tbox', 'float_timestamp_to_tbox', 'geo_period_to_stbox', 'geo_timestamp_to_stbox', - 'geo_to_stbox', 'int_period_to_tbox', - 'numspan_to_tbox', - 'span_timestamp_to_tbox', + 'int_timestamp_to_tbox', 'span_period_to_tbox', + 'span_timestamp_to_tbox', + 'stbox_copy', + 'stbox_make', + 'tbox_copy', + 'tbox_make', + 'float_to_tbox', + 'geo_to_stbox', + 'int_to_tbox', + 'numset_to_tbox', + 'numspan_to_tbox', + 'numspanset_to_tbox', + 'period_to_stbox', + 'period_to_tbox', + 'periodset_to_stbox', + 'periodset_to_tbox', + 'stbox_to_geo', + 'stbox_to_period', 'tbox_to_floatspan', 'tbox_to_period', - 'stbox_to_period', - 'tnumber_to_tbox', - 'stbox_to_geo', - 'tpoint_to_stbox', 'timestamp_to_stbox', + 'timestamp_to_tbox', 'timestampset_to_stbox', - 'period_to_stbox', - 'periodset_to_stbox', - 'tbox_hasx', - 'tbox_hast', - 'tbox_xmin', - 'tbox_xmin_inc', - 'tbox_xmax', - 'tbox_xmax_inc', - 'tbox_tmin', - 'tbox_tmin_inc', - 'tbox_tmax', - 'tbox_tmax_inc', + 'timestampset_to_tbox', + 'tnumber_to_tbox', + 'tpoint_to_stbox', + 'stbox_hast', 'stbox_hasx', 'stbox_hasz', - 'stbox_hast', 'stbox_isgeodetic', - 'stbox_xmin', + 'stbox_srid', + 'stbox_tmax', + 'stbox_tmax_inc', + 'stbox_tmin', + 'stbox_tmin_inc', 'stbox_xmax', - 'stbox_ymin', + 'stbox_xmin', 'stbox_ymax', - 'stbox_zmin', + 'stbox_ymin', 'stbox_zmax', - 'stbox_tmin', - 'stbox_tmin_inc', - 'stbox_tmax', - 'stbox_tmax_inc', - 'stbox_srid', + 'stbox_zmin', + 'tbox_hast', + 'tbox_hasx', + 'tbox_tmax', + 'tbox_tmax_inc', + 'tbox_tmin', + 'tbox_tmin_inc', + 'tbox_xmax', + 'tbox_xmax_inc', + 'tbox_xmin', + 'tbox_xmin_inc', 'stbox_expand_space', 'stbox_expand_time', 'stbox_get_space', 'stbox_round', 'stbox_set_srid', 'stbox_shift_scale_time', - 'tbox_expand_value', 'tbox_expand_time', + 'tbox_expand_value', 'tbox_round', - 'tbox_shift_scale_int', 'tbox_shift_scale_float', + 'tbox_shift_scale_int', 'tbox_shift_scale_time', + 'union_tbox_tbox', + 'inter_tbox_tbox', + 'intersection_tbox_tbox', + 'union_stbox_stbox', + 'inter_stbox_stbox', + 'intersection_stbox_stbox', 'contains_tbox_tbox', 'contained_tbox_tbox', 'overlaps_tbox_tbox', @@ -601,12 +609,6 @@ 'overbefore_stbox_stbox', 'after_stbox_stbox', 'overafter_stbox_stbox', - 'union_tbox_tbox', - 'inter_tbox_tbox', - 'intersection_tbox_tbox', - 'union_stbox_stbox', - 'inter_stbox_stbox', - 'intersection_stbox_stbox', 'stbox_quad_split', 'tbox_eq', 'tbox_ne', @@ -720,21 +722,29 @@ 'ttext_min_value', 'ttext_start_value', 'ttext_values', + 'temporal_scale_time', 'temporal_set_interp', + 'temporal_shift_scale_time', + 'temporal_shift_time', + 'temporal_to_tinstant', + 'temporal_to_tsequence', + 'temporal_to_tsequenceset', 'tfloat_scale_value', 'tfloat_shift_scale_value', 'tfloat_shift_value', 'tint_scale_value', 'tint_shift_scale_value', 'tint_shift_value', - 'temporal_scale_time', - 'temporal_shift_scale_time', - 'temporal_shift_time', - 'temporal_to_tinstant', - 'temporal_to_tsequence', - 'temporal_to_tsequenceset', - 'temporal_tprecision', - 'temporal_tsample', + 'temporal_append_tinstant', + 'temporal_append_tsequence', + 'temporal_delete_period', + 'temporal_delete_periodset', + 'temporal_delete_timestamp', + 'temporal_delete_timestampset', + 'temporal_insert', + 'temporal_merge', + 'temporal_merge_array', + 'temporal_update', 'tbool_at_value', 'tbool_minus_value', 'tbool_value_at_timestamp', @@ -774,77 +784,13 @@ 'ttext_at_value', 'ttext_minus_value', 'ttext_value_at_timestamp', - 'temporal_append_tinstant', - 'temporal_append_tsequence', - 'temporal_delete_period', - 'temporal_delete_periodset', - 'temporal_delete_timestamp', - 'temporal_delete_timestampset', - 'temporal_insert', - 'temporal_merge', - 'temporal_merge_array', - 'temporal_update', - 'tand_bool_tbool', - 'tand_tbool_bool', - 'tand_tbool_tbool', - 'tbool_when_true', - 'tnot_tbool', - 'tor_bool_tbool', - 'tor_tbool_bool', - 'tor_tbool_tbool', - 'add_float_tfloat', - 'add_int_tint', - 'add_tfloat_float', - 'add_tint_int', - 'add_tnumber_tnumber', - 'div_float_tfloat', - 'div_int_tint', - 'div_tfloat_float', - 'div_tint_int', - 'div_tnumber_tnumber', - 'float_degrees', - 'mult_float_tfloat', - 'mult_int_tint', - 'mult_tfloat_float', - 'mult_tint_int', - 'mult_tnumber_tnumber', - 'sub_float_tfloat', - 'sub_int_tint', - 'sub_tfloat_float', - 'sub_tint_int', - 'sub_tnumber_tnumber', - 'tfloat_round', - 'tfloat_degrees', - 'tfloat_derivative', - 'tfloat_radians', - 'tnumber_abs', - 'tnumber_angular_difference', - 'tnumber_delta_value', - 'textcat_text_ttext', - 'textcat_ttext_text', - 'textcat_ttext_ttext', - 'ttext_upper', - 'ttext_lower', - 'distance_tfloat_float', - 'distance_tint_int', - 'distance_tnumber_tnumber', - 'distance_tpoint_point', - 'distance_tpoint_tpoint', - 'nad_stbox_geo', - 'nad_stbox_stbox', - 'nad_tbox_tbox', - 'nad_tfloat_float', - 'nad_tfloat_tfloat', - 'nad_tint_int', - 'nad_tint_tint', - 'nad_tnumber_tbox', - 'nad_tpoint_geo', - 'nad_tpoint_stbox', - 'nad_tpoint_tpoint', - 'nai_tpoint_geo', - 'nai_tpoint_tpoint', - 'shortestline_tpoint_geo', - 'shortestline_tpoint_tpoint', + 'temporal_cmp', + 'temporal_eq', + 'temporal_ge', + 'temporal_gt', + 'temporal_le', + 'temporal_lt', + 'temporal_ne', 'tbool_always_eq', 'tbool_ever_eq', 'tfloat_always_eq', @@ -867,13 +813,6 @@ 'ttext_ever_eq', 'ttext_ever_le', 'ttext_ever_lt', - 'temporal_cmp', - 'temporal_eq', - 'temporal_ge', - 'temporal_gt', - 'temporal_le', - 'temporal_lt', - 'temporal_ne', 'teq_bool_tbool', 'teq_float_tfloat', 'teq_int_tint', @@ -924,6 +863,67 @@ 'tne_tpoint_point', 'tne_tint_int', 'tne_ttext_text', + 'tand_bool_tbool', + 'tand_tbool_bool', + 'tand_tbool_tbool', + 'tbool_when_true', + 'tnot_tbool', + 'tor_bool_tbool', + 'tor_tbool_bool', + 'tor_tbool_tbool', + 'add_float_tfloat', + 'add_int_tint', + 'add_tfloat_float', + 'add_tint_int', + 'add_tnumber_tnumber', + 'div_float_tfloat', + 'div_int_tint', + 'div_tfloat_float', + 'div_tint_int', + 'div_tnumber_tnumber', + 'float_degrees', + 'mult_float_tfloat', + 'mult_int_tint', + 'mult_tfloat_float', + 'mult_tint_int', + 'mult_tnumber_tnumber', + 'sub_float_tfloat', + 'sub_int_tint', + 'sub_tfloat_float', + 'sub_tint_int', + 'sub_tnumber_tnumber', + 'tfloat_round', + 'tfloat_degrees', + 'tfloat_derivative', + 'tfloat_radians', + 'tnumber_abs', + 'tnumber_angular_difference', + 'tnumber_delta_value', + 'textcat_text_ttext', + 'textcat_ttext_text', + 'textcat_ttext_ttext', + 'ttext_upper', + 'ttext_lower', + 'distance_tfloat_float', + 'distance_tint_int', + 'distance_tnumber_tnumber', + 'distance_tpoint_point', + 'distance_tpoint_tpoint', + 'nad_stbox_geo', + 'nad_stbox_stbox', + 'nad_tbox_tbox', + 'nad_tfloat_float', + 'nad_tfloat_tfloat', + 'nad_tint_int', + 'nad_tint_tint', + 'nad_tnumber_tbox', + 'nad_tpoint_geo', + 'nad_tpoint_stbox', + 'nad_tpoint_tpoint', + 'nai_tpoint_geo', + 'nai_tpoint_tpoint', + 'shortestline_tpoint_geo', + 'shortestline_tpoint_tpoint', 'bearing_point_point', 'bearing_tpoint_point', 'bearing_tpoint_tpoint', @@ -942,12 +942,15 @@ 'tpoint_stboxes', 'tpoint_trajectory', 'geo_expand_space', - 'tpoint_expand_space', - 'tgeompoint_to_tgeogpoint', + 'geo_to_tpoint', 'tgeogpoint_to_tgeompoint', - 'tpoint_round', + 'tgeompoint_to_tgeogpoint', + 'tpoint_AsMVTGeom', + 'tpoint_expand_space', 'tpoint_make_simple', + 'tpoint_round', 'tpoint_set_srid', + 'tpoint_to_geo_meas', 'econtains_geo_tpoint', 'edisjoint_tpoint_geo', 'edisjoint_tpoint_tpoint', @@ -984,6 +987,17 @@ 'tpoint_twcentroid', 'ttext_tmax_transfn', 'ttext_tmin_transfn', + 'temporal_simplify_min_dist', + 'temporal_simplify_min_tdelta', + 'temporal_simplify_dp', + 'temporal_simplify_max_dist', + 'temporal_tprecision', + 'temporal_tsample', + 'temporal_dyntimewarp_distance', + 'temporal_dyntimewarp_path', + 'temporal_frechet_distance', + 'temporal_frechet_path', + 'temporal_hausdorff_distance', 'float_bucket', 'floatspan_bucket_list', 'int_bucket', @@ -997,16 +1011,4 @@ 'timestamptz_bucket', 'tint_value_split', 'tint_value_time_split', - 'temporal_dyntimewarp_distance', - 'temporal_dyntimewarp_path', - 'temporal_frechet_distance', - 'temporal_frechet_path', - 'temporal_hausdorff_distance', - 'geo_to_tpoint', - 'temporal_simplify_min_dist', - 'temporal_simplify_min_tdelta', - 'temporal_simplify_dp', - 'temporal_simplify_max_dist', - 'tpoint_AsMVTGeom', - 'tpoint_to_geo_meas', ] diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 6664c41c..5e173e2b 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -744,7 +744,7 @@ extern void meos_initialize(const char *tz_str, error_handler_fn err_handler); extern void meos_finalize(void); /***************************************************************************** - * Functions for input/output PostgreSQL time types + * Functions for PostgreSQL types *****************************************************************************/ extern bool bool_in(const char *in_str); @@ -775,7 +775,7 @@ extern TimestampTz pg_to_timestamp(text *date_txt, text *fmt); extern char *text2cstring(const text *textptr); /***************************************************************************** - * Functions for input/output and manipulation of PostGIS types + * Functions for PostGIS types *****************************************************************************/ extern GSERIALIZED *geography_from_hexewkb(const char *wkt); @@ -983,6 +983,80 @@ extern SpanSet *periodset_tprecision(const SpanSet *ss, const Interval *duration extern TimestampTz timestamp_tprecision(TimestampTz t, const Interval *duration, TimestampTz torigin); extern Set *timestampset_shift_scale(const Set *ts, const Interval *shift, const Interval *duration); + + + + +extern bool intersection_bigintset_bigint(const Set *s, int64 i, int64 *result); +extern bool intersection_bigintspan_bigint(const Span *s, int64 i, int64 *result); +extern bool intersection_bigintspanset_bigint(const SpanSet *ss, int64 i, int64 *result); +extern bool intersection_floatset_float(const Set *s, double d, double *result); +extern bool intersection_floatspan_float(const Span *s, double d, double *result); +extern bool intersection_floatspanset_float(const SpanSet *ss, double d, double *result); +extern bool intersection_geoset_geo(const Set *s, const GSERIALIZED *gs, GSERIALIZED **result); +extern bool intersection_intset_int(const Set *s, int i, int *result); +extern bool intersection_intspan_int(const Span *s, int i, int *result); +extern bool intersection_intspanset_int(const SpanSet *ss, int i, int *result); +extern bool intersection_period_timestamp(const Span *s, TimestampTz t, TimestampTz *result); +extern bool intersection_periodset_timestamp(const SpanSet *ss, TimestampTz t, TimestampTz *result); +extern Set *intersection_set_set(const Set *s1, const Set *s2); +extern Span *intersection_span_span(const Span *s1, const Span *s2); +extern SpanSet *intersection_spanset_span(const SpanSet *ss, const Span *s); +extern SpanSet *intersection_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern bool intersection_textset_text(const Set *s, const text *txt, text **result); +extern bool intersection_timestampset_timestamp(const Set *s, TimestampTz t, TimestampTz *result); +extern bool minus_bigint_bigintset(int64 i, const Set *s, int64 *result); +extern bool minus_bigint_bigintspan(int64 i, const Span *s, int64 *result); +extern bool minus_bigint_bigintspanset(int64 i, const SpanSet *ss, int64 *result); +extern Set *minus_bigintset_bigint(const Set *s, int64 i); +extern SpanSet *minus_bigintspan_bigint(const Span *s, int64 i); +extern SpanSet *minus_bigintspanset_bigint(const SpanSet *ss, int64 i); +extern bool minus_float_floatset(double d, const Set *s, double *result); +extern bool minus_float_floatspan(double d, const Span *s, double *result); +extern bool minus_float_floatspanset(double d, const SpanSet *ss, double *result); +extern Set *minus_floatset_float(const Set *s, double d); +extern SpanSet *minus_floatspan_float(const Span *s, double d); +extern SpanSet *minus_floatspanset_float(const SpanSet *ss, double d); +extern bool minus_geo_geoset(const GSERIALIZED *gs, const Set *s, GSERIALIZED **result); +extern Set *minus_geoset_geo(const Set *s, const GSERIALIZED *gs); +extern bool minus_int_intset(int i, const Set *s, int *result); +extern bool minus_int_intspan(int i, const Span *s, int *result); +extern bool minus_int_intspanset(int i, const SpanSet *ss, int *result); +extern Set *minus_intset_int(const Set *s, int i); +extern SpanSet *minus_intspan_int(const Span *s, int i); +extern SpanSet *minus_intspanset_int(const SpanSet *ss, int i); +extern SpanSet *minus_period_timestamp(const Span *s, TimestampTz t); +extern SpanSet *minus_periodset_timestamp(const SpanSet *ss, TimestampTz t); +extern Set *minus_set_set(const Set *s1, const Set *s2); +extern SpanSet *minus_span_span(const Span *s1, const Span *s2); +extern SpanSet *minus_span_spanset(const Span *s, const SpanSet *ss); +extern SpanSet *minus_spanset_span(const SpanSet *ss, const Span *s); +extern SpanSet *minus_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern bool minus_text_textset(const text *txt, const Set *s, text **result); +extern Set *minus_textset_text(const Set *s, const text *txt); +extern bool minus_timestamp_period(TimestampTz t, const Span *s, TimestampTz *result); +extern bool minus_timestamp_periodset(TimestampTz t, const SpanSet *ss, TimestampTz *result); +extern bool minus_timestamp_timestampset(TimestampTz t, const Set *s, TimestampTz *result); +extern Set *minus_timestampset_timestamp(const Set *s, TimestampTz t); +extern Set *union_bigintset_bigint(const Set *s, int64 i); +extern SpanSet *union_bigintspan_bigint(const Span *s, int64 i); +extern SpanSet *union_bigintspanset_bigint(const SpanSet *ss, int64 i); +extern Set *union_floatset_float(const Set *s, double d); +extern SpanSet *union_floatspan_float(const Span *s, double d); +extern SpanSet *union_floatspanset_float(const SpanSet *ss, double d); +extern Set *union_geoset_geo(const Set *s, const GSERIALIZED *gs); +extern Set *union_intset_int(const Set *s, int i); +extern SpanSet *union_intspan_int(const Span *s, int i); +extern SpanSet *union_intspanset_int(const SpanSet *ss, int i); +extern SpanSet *union_period_timestamp(const Span *s, TimestampTz t); +extern SpanSet *union_periodset_timestamp(SpanSet *ss, TimestampTz t); +extern Set *union_set_set(const Set *s1, const Set *s2); +extern SpanSet *union_span_span(const Span *s1, const Span *s2); +extern SpanSet *union_spanset_span(const SpanSet *ss, const Span *s); +extern SpanSet *union_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern Set *union_textset_text(const Set *s, const text *txt); +extern Set *union_timestampset_timestamp(const Set *s, const TimestampTz t); + /***************************************************************************** * Bounding box functions for set and span types *****************************************************************************/ @@ -1099,80 +1173,6 @@ extern bool right_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool intersection_bigintset_bigint(const Set *s, int64 i, int64 *result); -extern bool intersection_bigintspan_bigint(const Span *s, int64 i, int64 *result); -extern bool intersection_bigintspanset_bigint(const SpanSet *ss, int64 i, int64 *result); -extern bool intersection_floatset_float(const Set *s, double d, double *result); -extern bool intersection_floatspan_float(const Span *s, double d, double *result); -extern bool intersection_floatspanset_float(const SpanSet *ss, double d, double *result); -extern bool intersection_geoset_geo(const Set *s, const GSERIALIZED *gs, GSERIALIZED **result); -extern bool intersection_intset_int(const Set *s, int i, int *result); -extern bool intersection_intspan_int(const Span *s, int i, int *result); -extern bool intersection_intspanset_int(const SpanSet *ss, int i, int *result); -extern bool intersection_period_timestamp(const Span *s, TimestampTz t, TimestampTz *result); -extern bool intersection_periodset_timestamp(const SpanSet *ss, TimestampTz t, TimestampTz *result); -extern Set *intersection_set_set(const Set *s1, const Set *s2); -extern Span *intersection_span_span(const Span *s1, const Span *s2); -extern SpanSet *intersection_spanset_span(const SpanSet *ss, const Span *s); -extern SpanSet *intersection_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool intersection_textset_text(const Set *s, const text *txt, text **result); -extern bool intersection_timestampset_timestamp(const Set *s, TimestampTz t, TimestampTz *result); -extern bool minus_bigint_bigintset(int64 i, const Set *s, int64 *result); -extern bool minus_bigint_bigintspan(int64 i, const Span *s, int64 *result); -extern bool minus_bigint_bigintspanset(int64 i, const SpanSet *ss, int64 *result); -extern Set *minus_bigintset_bigint(const Set *s, int64 i); -extern SpanSet *minus_bigintspan_bigint(const Span *s, int64 i); -extern SpanSet *minus_bigintspanset_bigint(const SpanSet *ss, int64 i); -extern bool minus_float_floatset(double d, const Set *s, double *result); -extern bool minus_float_floatspan(double d, const Span *s, double *result); -extern bool minus_float_floatspanset(double d, const SpanSet *ss, double *result); -extern Set *minus_floatset_float(const Set *s, double d); -extern SpanSet *minus_floatspan_float(const Span *s, double d); -extern SpanSet *minus_floatspanset_float(const SpanSet *ss, double d); -extern bool minus_geo_geoset(const GSERIALIZED *gs, const Set *s, GSERIALIZED **result); -extern Set *minus_geoset_geo(const Set *s, const GSERIALIZED *gs); -extern bool minus_int_intset(int i, const Set *s, int *result); -extern bool minus_int_intspan(int i, const Span *s, int *result); -extern bool minus_int_intspanset(int i, const SpanSet *ss, int *result); -extern Set *minus_intset_int(const Set *s, int i); -extern SpanSet *minus_intspan_int(const Span *s, int i); -extern SpanSet *minus_intspanset_int(const SpanSet *ss, int i); -extern SpanSet *minus_period_timestamp(const Span *s, TimestampTz t); -extern SpanSet *minus_periodset_timestamp(const SpanSet *ss, TimestampTz t); -extern Set *minus_set_set(const Set *s1, const Set *s2); -extern SpanSet *minus_span_span(const Span *s1, const Span *s2); -extern SpanSet *minus_span_spanset(const Span *s, const SpanSet *ss); -extern SpanSet *minus_spanset_span(const SpanSet *ss, const Span *s); -extern SpanSet *minus_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool minus_text_textset(const text *txt, const Set *s, text **result); -extern Set *minus_textset_text(const Set *s, const text *txt); -extern bool minus_timestamp_period(TimestampTz t, const Span *s, TimestampTz *result); -extern bool minus_timestamp_periodset(TimestampTz t, const SpanSet *ss, TimestampTz *result); -extern bool minus_timestamp_timestampset(TimestampTz t, const Set *s, TimestampTz *result); -extern Set *minus_timestampset_timestamp(const Set *s, TimestampTz t); -extern Set *union_bigintset_bigint(const Set *s, int64 i); -extern SpanSet *union_bigintspan_bigint(const Span *s, int64 i); -extern SpanSet *union_bigintspanset_bigint(const SpanSet *ss, int64 i); -extern Set *union_floatset_float(const Set *s, double d); -extern SpanSet *union_floatspan_float(const Span *s, double d); -extern SpanSet *union_floatspanset_float(const SpanSet *ss, double d); -extern Set *union_geoset_geo(const Set *s, const GSERIALIZED *gs); -extern Set *union_intset_int(const Set *s, int i); -extern SpanSet *union_intspan_int(const Span *s, int i); -extern SpanSet *union_intspanset_int(const SpanSet *ss, int i); -extern SpanSet *union_period_timestamp(const Span *s, TimestampTz t); -extern SpanSet *union_periodset_timestamp(SpanSet *ss, TimestampTz t); -extern Set *union_set_set(const Set *s1, const Set *s2); -extern SpanSet *union_span_span(const Span *s1, const Span *s2); -extern SpanSet *union_spanset_span(const SpanSet *ss, const Span *s); -extern SpanSet *union_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern Set *union_textset_text(const Set *s, const text *txt); -extern Set *union_timestampset_timestamp(const Set *s, const TimestampTz t); - - - - - extern double distance_bigintset_bigint(const Set *s, int64 i); extern double distance_bigintspan_bigint(const Span *s, int64 i); extern double distance_bigintspanset_bigint(const SpanSet *ss, int64 i); @@ -1194,32 +1194,6 @@ extern double distance_timestampset_timestamp(const Set *s, TimestampTz t); -extern Span *bigint_extent_transfn(Span *s, int64 i); -extern Set *bigint_union_transfn(Set *state, int64 i); -extern Span *float_extent_transfn(Span *s, double d); -extern Set *float_union_transfn(Set *state, double d); -extern Span *int_extent_transfn(Span *s, int i); -extern Set *int_union_transfn(Set *state, int i); -extern SkipList *period_tcount_transfn(SkipList *state, const Span *p); -extern SkipList *periodset_tcount_transfn(SkipList *state, const SpanSet *ps); -extern Span *set_extent_transfn(Span *span, const Set *set); -extern Set *set_union_finalfn(Set *state); -extern Set *set_union_transfn(Set *state, Set *set); -extern Span *span_extent_transfn(Span *s1, const Span *s2); -extern SpanSet *span_union_transfn(SpanSet *state, const Span *span); -extern Span *spanset_extent_transfn(Span *s, const SpanSet *ss); -extern SpanSet *spanset_union_finalfn(SpanSet *state); -extern SpanSet *spanset_union_transfn(SpanSet *state, const SpanSet *ss); -extern Set *text_union_transfn(Set *state, const text *txt); -extern Span *timestamp_extent_transfn(Span *p, TimestampTz t); -extern SkipList *timestamp_tcount_transfn(SkipList *state, TimestampTz t); -extern Set *timestamp_union_transfn(Set *state, TimestampTz t); -extern SkipList *timestampset_tcount_transfn(SkipList *state, const Set *ts); - - - - - extern int set_cmp(const Set *s1, const Set *s2); extern bool set_eq(const Set *s1, const Set *s2); extern bool set_ge(const Set *s1, const Set *s2); @@ -1242,6 +1216,32 @@ extern bool spanset_le(const SpanSet *ss1, const SpanSet *ss2); extern bool spanset_lt(const SpanSet *ss1, const SpanSet *ss2); extern bool spanset_ne(const SpanSet *ss1, const SpanSet *ss2); + + + + +extern Span *bigint_extent_transfn(Span *s, int64 i); +extern Set *bigint_union_transfn(Set *state, int64 i); +extern Span *float_extent_transfn(Span *s, double d); +extern Set *float_union_transfn(Set *state, double d); +extern Span *int_extent_transfn(Span *s, int i); +extern Set *int_union_transfn(Set *state, int i); +extern SkipList *period_tcount_transfn(SkipList *state, const Span *p); +extern SkipList *periodset_tcount_transfn(SkipList *state, const SpanSet *ps); +extern Span *set_extent_transfn(Span *span, const Set *set); +extern Set *set_union_finalfn(Set *state); +extern Set *set_union_transfn(Set *state, Set *set); +extern Span *span_extent_transfn(Span *s1, const Span *s2); +extern SpanSet *span_union_transfn(SpanSet *state, const Span *span); +extern Span *spanset_extent_transfn(Span *s, const SpanSet *ss); +extern SpanSet *spanset_union_finalfn(SpanSet *state); +extern SpanSet *spanset_union_transfn(SpanSet *state, const SpanSet *ss); +extern Set *text_union_transfn(Set *state, const text *txt); +extern Span *timestamp_extent_transfn(Span *p, TimestampTz t); +extern SkipList *timestamp_tcount_transfn(SkipList *state, TimestampTz t); +extern Set *timestamp_union_transfn(Set *state, TimestampTz t); +extern SkipList *timestampset_tcount_transfn(SkipList *state, const Set *ts); + /****************************************************************************** * Functions for box types *****************************************************************************/ @@ -1265,72 +1265,74 @@ extern char *stbox_out(const STBox *box, int maxdd); +extern TBox *float_period_to_tbox(double d, const Span *p); +extern TBox *float_timestamp_to_tbox(double d, TimestampTz t); +extern STBox *geo_period_to_stbox(const GSERIALIZED *gs, const Span *p); +extern STBox *geo_timestamp_to_stbox(const GSERIALIZED *gs, TimestampTz t); +extern TBox *int_period_to_tbox(int i, const Span *p); +extern TBox *int_timestamp_to_tbox(int i, TimestampTz t); +extern TBox *span_period_to_tbox(const Span *span, const Span *p); +extern TBox *span_timestamp_to_tbox(const Span *span, TimestampTz t); +extern STBox *stbox_copy(const STBox *box); extern STBox * stbox_make(bool hasx, bool hasz, bool geodetic, int32 srid, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, const Span *p); -extern STBox *stbox_copy(const STBox *box); -extern TBox *tbox_make(const Span *s, const Span *p); extern TBox *tbox_copy(const TBox *box); +extern TBox *tbox_make(const Span *s, const Span *p); -extern TBox *int_to_tbox(int i); extern TBox *float_to_tbox(double d); -extern TBox *timestamp_to_tbox(TimestampTz t); -extern TBox *timestampset_to_tbox(const Set *ss); -extern TBox *period_to_tbox(const Span *p); -extern TBox *periodset_to_tbox(const SpanSet *ps); -extern TBox *int_timestamp_to_tbox(int i, TimestampTz t); -extern TBox *float_period_to_tbox(double d, const Span *p); -extern TBox *float_timestamp_to_tbox(double d, TimestampTz t); -extern STBox *geo_period_to_stbox(const GSERIALIZED *gs, const Span *p); -extern STBox *geo_timestamp_to_stbox(const GSERIALIZED *gs, TimestampTz t); extern STBox *geo_to_stbox(const GSERIALIZED *gs); -extern TBox *int_period_to_tbox(int i, const Span *p); +extern TBox *int_to_tbox(int i); +extern TBox *numset_to_tbox(const Set *s); extern TBox *numspan_to_tbox(const Span *s); -extern TBox *span_timestamp_to_tbox(const Span *span, TimestampTz t); -extern TBox *span_period_to_tbox(const Span *span, const Span *p); +extern TBox *numspanset_to_tbox(const SpanSet *ss); +extern STBox *period_to_stbox(const Span *p); +extern TBox *period_to_tbox(const Span *p); +extern STBox *periodset_to_stbox(const SpanSet *ps); +extern TBox *periodset_to_tbox(const SpanSet *ps); +extern GSERIALIZED *stbox_to_geo(const STBox *box); +extern Span *stbox_to_period(const STBox *box); extern Span *tbox_to_floatspan(const TBox *box); extern Span *tbox_to_period(const TBox *box); -extern Span *stbox_to_period(const STBox *box); -extern TBox *tnumber_to_tbox(const Temporal *temp); -extern GSERIALIZED *stbox_to_geo(const STBox *box); -extern STBox *tpoint_to_stbox(const Temporal *temp); extern STBox *timestamp_to_stbox(TimestampTz t); +extern TBox *timestamp_to_tbox(TimestampTz t); extern STBox *timestampset_to_stbox(const Set *ts); -extern STBox *period_to_stbox(const Span *p); -extern STBox *periodset_to_stbox(const SpanSet *ps); +extern TBox *timestampset_to_tbox(const Set *ss); +extern TBox *tnumber_to_tbox(const Temporal *temp); +extern STBox *tpoint_to_stbox(const Temporal *temp); -extern bool tbox_hasx(const TBox *box); -extern bool tbox_hast(const TBox *box); -extern bool tbox_xmin(const TBox *box, double *result); -extern bool tbox_xmin_inc(const TBox *box, bool *result); -extern bool tbox_xmax(const TBox *box, double *result); -extern bool tbox_xmax_inc(const TBox *box, bool *result); -extern bool tbox_tmin(const TBox *box, TimestampTz *result); -extern bool tbox_tmin_inc(const TBox *box, bool *result); -extern bool tbox_tmax(const TBox *box, TimestampTz *result); -extern bool tbox_tmax_inc(const TBox *box, bool *result); +extern bool stbox_hast(const STBox *box); extern bool stbox_hasx(const STBox *box); extern bool stbox_hasz(const STBox *box); -extern bool stbox_hast(const STBox *box); extern bool stbox_isgeodetic(const STBox *box); -extern bool stbox_xmin(const STBox *box, double *result); +extern int32 stbox_srid(const STBox *box); +extern bool stbox_tmax(const STBox *box, TimestampTz *result); +extern bool stbox_tmax_inc(const STBox *box, bool *result); +extern bool stbox_tmin(const STBox *box, TimestampTz *result); +extern bool stbox_tmin_inc(const STBox *box, bool *result); extern bool stbox_xmax(const STBox *box, double *result); -extern bool stbox_ymin(const STBox *box, double *result); +extern bool stbox_xmin(const STBox *box, double *result); extern bool stbox_ymax(const STBox *box, double *result); -extern bool stbox_zmin(const STBox *box, double *result); +extern bool stbox_ymin(const STBox *box, double *result); extern bool stbox_zmax(const STBox *box, double *result); -extern bool stbox_tmin(const STBox *box, TimestampTz *result); -extern bool stbox_tmin_inc(const STBox *box, bool *result); -extern bool stbox_tmax(const STBox *box, TimestampTz *result); -extern bool stbox_tmax_inc(const STBox *box, bool *result); -extern int32 stbox_srid(const STBox *box); +extern bool stbox_zmin(const STBox *box, double *result); +extern bool tbox_hast(const TBox *box); +extern bool tbox_hasx(const TBox *box); +extern bool tbox_tmax(const TBox *box, TimestampTz *result); +extern bool tbox_tmax_inc(const TBox *box, bool *result); +extern bool tbox_tmin(const TBox *box, TimestampTz *result); +extern bool tbox_tmin_inc(const TBox *box, bool *result); +extern bool tbox_xmax(const TBox *box, double *result); +extern bool tbox_xmax_inc(const TBox *box, bool *result); +extern bool tbox_xmin(const TBox *box, double *result); +extern bool tbox_xmin_inc(const TBox *box, bool *result); @@ -1342,17 +1344,30 @@ extern STBox *stbox_get_space(const STBox *box); extern STBox *stbox_round(const STBox *box, int maxdd); extern STBox *stbox_set_srid(const STBox *box, int32 srid); extern STBox *stbox_shift_scale_time(const STBox *box, const Interval *shift, const Interval *duration); -extern TBox *tbox_expand_value(const TBox *box, const double d); extern TBox *tbox_expand_time(const TBox *box, const Interval *interval); +extern TBox *tbox_expand_value(const TBox *box, const double d); extern TBox *tbox_round(const TBox *box, int maxdd); -extern TBox *tbox_shift_scale_int(const TBox *box, int shift, int width, bool hasshift, bool haswidth); extern TBox *tbox_shift_scale_float(const TBox *box, double shift, double width, bool hasshift, bool haswidth); +extern TBox *tbox_shift_scale_int(const TBox *box, int shift, int width, bool hasshift, bool haswidth); extern TBox *tbox_shift_scale_time(const TBox *box, const Interval *shift, const Interval *duration); +extern TBox *union_tbox_tbox(const TBox *box1, const TBox *box2, bool strict); +extern bool inter_tbox_tbox(const TBox *box1, const TBox *box2, TBox *result); +extern TBox *intersection_tbox_tbox(const TBox *box1, const TBox *box2); +extern STBox *union_stbox_stbox(const STBox *box1, const STBox *box2, bool strict); +extern bool inter_stbox_stbox(const STBox *box1, const STBox *box2, STBox *result); +extern STBox *intersection_stbox_stbox(const STBox *box1, const STBox *box2); + +/***************************************************************************** + * Bounding box functions for box types + *****************************************************************************/ + + + extern bool contains_tbox_tbox(const TBox *box1, const TBox *box2); extern bool contained_tbox_tbox(const TBox *box1, const TBox *box2); extern bool overlaps_tbox_tbox(const TBox *box1, const TBox *box2); @@ -1397,17 +1412,6 @@ extern bool overafter_stbox_stbox(const STBox *box1, const STBox *box2); -extern TBox *union_tbox_tbox(const TBox *box1, const TBox *box2, bool strict); -extern bool inter_tbox_tbox(const TBox *box1, const TBox *box2, TBox *result); -extern TBox *intersection_tbox_tbox(const TBox *box1, const TBox *box2); -extern STBox *union_stbox_stbox(const STBox *box1, const STBox *box2, bool strict); -extern bool inter_stbox_stbox(const STBox *box1, const STBox *box2, STBox *result); -extern STBox *intersection_stbox_stbox(const STBox *box1, const STBox *box2); - - - - - extern STBox *stbox_quad_split(const STBox *box, int *count); @@ -1553,21 +1557,34 @@ extern text **ttext_values(const Temporal *temp, int *count); +extern Temporal *temporal_scale_time(const Temporal *temp, const Interval *duration); extern Temporal *temporal_set_interp(const Temporal *temp, interpType interp); +extern Temporal *temporal_shift_scale_time(const Temporal *temp, const Interval *shift, const Interval *duration); +extern Temporal *temporal_shift_time(const Temporal *temp, const Interval *shift); +extern Temporal *temporal_to_tinstant(const Temporal *temp); +extern Temporal *temporal_to_tsequence(const Temporal *temp, interpType interp); +extern Temporal *temporal_to_tsequenceset(const Temporal *temp, interpType interp); extern Temporal *tfloat_scale_value(const Temporal *temp, double width); extern Temporal *tfloat_shift_scale_value(const Temporal *temp, double shift, double width); extern Temporal *tfloat_shift_value(const Temporal *temp, double shift); extern Temporal *tint_scale_value(const Temporal *temp, int width); extern Temporal *tint_shift_scale_value(const Temporal *temp, int shift, int width); extern Temporal *tint_shift_value(const Temporal *temp, int shift); -extern Temporal *temporal_scale_time(const Temporal *temp, const Interval *duration); -extern Temporal *temporal_shift_scale_time(const Temporal *temp, const Interval *shift, const Interval *duration); -extern Temporal *temporal_shift_time(const Temporal *temp, const Interval *shift); -extern Temporal *temporal_to_tinstant(const Temporal *temp); -extern Temporal *temporal_to_tsequence(const Temporal *temp, interpType interp); -extern Temporal *temporal_to_tsequenceset(const Temporal *temp, interpType interp); -extern Temporal *temporal_tprecision(const Temporal *temp, const Interval *duration, TimestampTz origin); -extern Temporal *temporal_tsample(const Temporal *temp, const Interval *duration, TimestampTz origin); + + + + + +extern Temporal *temporal_append_tinstant(Temporal *temp, const TInstant *inst, double maxdist, Interval *maxt, bool expand); +extern Temporal *temporal_append_tsequence(Temporal *temp, const TSequence *seq, bool expand); +extern Temporal *temporal_delete_period(const Temporal *temp, const Span *p, bool connect); +extern Temporal *temporal_delete_periodset(const Temporal *temp, const SpanSet *ps, bool connect); +extern Temporal *temporal_delete_timestamp(const Temporal *temp, TimestampTz t, bool connect); +extern Temporal *temporal_delete_timestampset(const Temporal *temp, const Set *ts, bool connect); +extern Temporal *temporal_insert(const Temporal *temp1, const Temporal *temp2, bool connect); +extern Temporal *temporal_merge(const Temporal *temp1, const Temporal *temp2); +extern Temporal *temporal_merge_array(Temporal **temparr, int count); +extern Temporal *temporal_update(const Temporal *temp1, const Temporal *temp2, bool connect); @@ -1613,101 +1630,19 @@ extern Temporal *ttext_at_value(const Temporal *temp, text *txt); extern Temporal *ttext_minus_value(const Temporal *temp, text *txt); extern bool ttext_value_at_timestamp(const Temporal *temp, TimestampTz t, bool strict, text **value); +/***************************************************************************** + * Comparison functions for temporal types + *****************************************************************************/ - -extern Temporal *temporal_append_tinstant(Temporal *temp, const TInstant *inst, double maxdist, Interval *maxt, bool expand); -extern Temporal *temporal_append_tsequence(Temporal *temp, const TSequence *seq, bool expand); -extern Temporal *temporal_delete_period(const Temporal *temp, const Span *p, bool connect); -extern Temporal *temporal_delete_periodset(const Temporal *temp, const SpanSet *ps, bool connect); -extern Temporal *temporal_delete_timestamp(const Temporal *temp, TimestampTz t, bool connect); -extern Temporal *temporal_delete_timestampset(const Temporal *temp, const Set *ts, bool connect); -extern Temporal *temporal_insert(const Temporal *temp1, const Temporal *temp2, bool connect); -extern Temporal *temporal_merge(const Temporal *temp1, const Temporal *temp2); -extern Temporal *temporal_merge_array(Temporal **temparr, int count); -extern Temporal *temporal_update(const Temporal *temp1, const Temporal *temp2, bool connect); - - - - - -extern Temporal *tand_bool_tbool(bool b, const Temporal *temp); -extern Temporal *tand_tbool_bool(const Temporal *temp, bool b); -extern Temporal *tand_tbool_tbool(const Temporal *temp1, const Temporal *temp2); -extern SpanSet *tbool_when_true(const Temporal *temp); -extern Temporal *tnot_tbool(const Temporal *temp); -extern Temporal *tor_bool_tbool(bool b, const Temporal *temp); -extern Temporal *tor_tbool_bool(const Temporal *temp, bool b); -extern Temporal *tor_tbool_tbool(const Temporal *temp1, const Temporal *temp2); - - - - - -extern Temporal *add_float_tfloat(double d, const Temporal *tnumber); -extern Temporal *add_int_tint(int i, const Temporal *tnumber); -extern Temporal *add_tfloat_float(const Temporal *tnumber, double d); -extern Temporal *add_tint_int(const Temporal *tnumber, int i); -extern Temporal *add_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); -extern Temporal *div_float_tfloat(double d, const Temporal *tnumber); -extern Temporal *div_int_tint(int i, const Temporal *tnumber); -extern Temporal *div_tfloat_float(const Temporal *tnumber, double d); -extern Temporal *div_tint_int(const Temporal *tnumber, int i); -extern Temporal *div_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); -extern double float_degrees(double value, bool normalize); -extern Temporal *mult_float_tfloat(double d, const Temporal *tnumber); -extern Temporal *mult_int_tint(int i, const Temporal *tnumber); -extern Temporal *mult_tfloat_float(const Temporal *tnumber, double d); -extern Temporal *mult_tint_int(const Temporal *tnumber, int i); -extern Temporal *mult_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); -extern Temporal *sub_float_tfloat(double d, const Temporal *tnumber); -extern Temporal *sub_int_tint(int i, const Temporal *tnumber); -extern Temporal *sub_tfloat_float(const Temporal *tnumber, double d); -extern Temporal *sub_tint_int(const Temporal *tnumber, int i); -extern Temporal *sub_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); -extern Temporal *tfloat_round(const Temporal *temp, int maxdd); -extern Temporal *tfloat_degrees(const Temporal *temp, bool normalize); -extern Temporal *tfloat_derivative(const Temporal *temp); -extern Temporal *tfloat_radians(const Temporal *temp); -extern Temporal *tnumber_abs(const Temporal *temp); -extern Temporal *tnumber_angular_difference(const Temporal *temp); -extern Temporal *tnumber_delta_value(const Temporal *temp); - - - - - -extern Temporal *textcat_text_ttext(const text *txt, const Temporal *temp); -extern Temporal *textcat_ttext_text(const Temporal *temp, const text *txt); -extern Temporal *textcat_ttext_ttext(const Temporal *temp1, const Temporal *temp2); -extern Temporal *ttext_upper(const Temporal *temp); -extern Temporal *ttext_lower(const Temporal *temp); - - - - - -extern Temporal *distance_tfloat_float(const Temporal *temp, double d); -extern Temporal *distance_tint_int(const Temporal *temp, int i); -extern Temporal *distance_tnumber_tnumber(const Temporal *temp1, const Temporal *temp2); -extern Temporal *distance_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); -extern Temporal *distance_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); -extern double nad_stbox_geo(const STBox *box, const GSERIALIZED *gs); -extern double nad_stbox_stbox(const STBox *box1, const STBox *box2); -extern double nad_tbox_tbox(const TBox *box1, const TBox *box2); -extern double nad_tfloat_float(const Temporal *temp, double d); -extern double nad_tfloat_tfloat(const Temporal *temp1, const Temporal *temp2); -extern int nad_tint_int(const Temporal *temp, int i); -extern int nad_tint_tint(const Temporal *temp1, const Temporal *temp2); -extern double nad_tnumber_tbox(const Temporal *temp, const TBox *box); -extern double nad_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); -extern double nad_tpoint_stbox(const Temporal *temp, const STBox *box); -extern double nad_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); -extern TInstant *nai_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); -extern TInstant *nai_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); -extern bool shortestline_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, GSERIALIZED **result); -extern bool shortestline_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2, GSERIALIZED **result); +extern int temporal_cmp(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_eq(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_ge(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_gt(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_le(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_lt(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_ne(const Temporal *temp1, const Temporal *temp2); @@ -1740,13 +1675,6 @@ extern bool ttext_ever_lt(const Temporal *temp, text *txt); -extern int temporal_cmp(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_eq(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_ge(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_gt(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_le(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_lt(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_ne(const Temporal *temp1, const Temporal *temp2); extern Temporal *teq_bool_tbool(bool b, const Temporal *temp); extern Temporal *teq_float_tfloat(double d, const Temporal *temp); extern Temporal *teq_int_tint(int i, const Temporal *temp); @@ -1798,6 +1726,87 @@ extern Temporal *tne_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); extern Temporal *tne_tint_int(const Temporal *temp, int i); extern Temporal *tne_ttext_text(const Temporal *temp, const text *txt); + + + + +extern Temporal *tand_bool_tbool(bool b, const Temporal *temp); +extern Temporal *tand_tbool_bool(const Temporal *temp, bool b); +extern Temporal *tand_tbool_tbool(const Temporal *temp1, const Temporal *temp2); +extern SpanSet *tbool_when_true(const Temporal *temp); +extern Temporal *tnot_tbool(const Temporal *temp); +extern Temporal *tor_bool_tbool(bool b, const Temporal *temp); +extern Temporal *tor_tbool_bool(const Temporal *temp, bool b); +extern Temporal *tor_tbool_tbool(const Temporal *temp1, const Temporal *temp2); + + + + + +extern Temporal *add_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *add_int_tint(int i, const Temporal *tnumber); +extern Temporal *add_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *add_tint_int(const Temporal *tnumber, int i); +extern Temporal *add_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); +extern Temporal *div_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *div_int_tint(int i, const Temporal *tnumber); +extern Temporal *div_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *div_tint_int(const Temporal *tnumber, int i); +extern Temporal *div_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); +extern double float_degrees(double value, bool normalize); +extern Temporal *mult_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *mult_int_tint(int i, const Temporal *tnumber); +extern Temporal *mult_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *mult_tint_int(const Temporal *tnumber, int i); +extern Temporal *mult_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); +extern Temporal *sub_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *sub_int_tint(int i, const Temporal *tnumber); +extern Temporal *sub_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *sub_tint_int(const Temporal *tnumber, int i); +extern Temporal *sub_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); +extern Temporal *tfloat_round(const Temporal *temp, int maxdd); +extern Temporal *tfloat_degrees(const Temporal *temp, bool normalize); +extern Temporal *tfloat_derivative(const Temporal *temp); +extern Temporal *tfloat_radians(const Temporal *temp); +extern Temporal *tnumber_abs(const Temporal *temp); +extern Temporal *tnumber_angular_difference(const Temporal *temp); +extern Temporal *tnumber_delta_value(const Temporal *temp); + + + + + +extern Temporal *textcat_text_ttext(const text *txt, const Temporal *temp); +extern Temporal *textcat_ttext_text(const Temporal *temp, const text *txt); +extern Temporal *textcat_ttext_ttext(const Temporal *temp1, const Temporal *temp2); +extern Temporal *ttext_upper(const Temporal *temp); +extern Temporal *ttext_lower(const Temporal *temp); + + + + + +extern Temporal *distance_tfloat_float(const Temporal *temp, double d); +extern Temporal *distance_tint_int(const Temporal *temp, int i); +extern Temporal *distance_tnumber_tnumber(const Temporal *temp1, const Temporal *temp2); +extern Temporal *distance_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); +extern Temporal *distance_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); +extern double nad_stbox_geo(const STBox *box, const GSERIALIZED *gs); +extern double nad_stbox_stbox(const STBox *box1, const STBox *box2); +extern double nad_tbox_tbox(const TBox *box1, const TBox *box2); +extern double nad_tfloat_float(const Temporal *temp, double d); +extern double nad_tfloat_tfloat(const Temporal *temp1, const Temporal *temp2); +extern int nad_tint_int(const Temporal *temp, int i); +extern int nad_tint_tint(const Temporal *temp1, const Temporal *temp2); +extern double nad_tnumber_tbox(const Temporal *temp, const TBox *box); +extern double nad_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); +extern double nad_tpoint_stbox(const Temporal *temp, const STBox *box); +extern double nad_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); +extern TInstant *nai_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); +extern TInstant *nai_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); +extern bool shortestline_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, GSERIALIZED **result); +extern bool shortestline_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2, GSERIALIZED **result); + /***************************************************************************** Spatial functions for temporal point types *****************************************************************************/ @@ -1827,12 +1836,15 @@ extern GSERIALIZED *tpoint_trajectory(const Temporal *temp); extern STBox *geo_expand_space(const GSERIALIZED *gs, double d); -extern STBox *tpoint_expand_space(const Temporal *temp, double d); -extern Temporal *tgeompoint_to_tgeogpoint(const Temporal *temp); +Temporal *geo_to_tpoint(const GSERIALIZED *gs); extern Temporal *tgeogpoint_to_tgeompoint(const Temporal *temp); -extern Temporal *tpoint_round(const Temporal *temp, int maxdd); +extern Temporal *tgeompoint_to_tgeogpoint(const Temporal *temp); +bool tpoint_AsMVTGeom(const Temporal *temp, const STBox *bounds, int32_t extent, int32_t buffer, bool clip_geom, GSERIALIZED **gsarr, int64 **timesarr, int *count); +extern STBox *tpoint_expand_space(const Temporal *temp, double d); extern Temporal **tpoint_make_simple(const Temporal *temp, int *count); +extern Temporal *tpoint_round(const Temporal *temp, int maxdd); extern Temporal *tpoint_set_srid(const Temporal *temp, int32 srid); +bool tpoint_to_geo_meas(const Temporal *tpoint, const Temporal *measure, bool segmentize, GSERIALIZED **result); @@ -1846,6 +1858,11 @@ extern int edwithin_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2, extern int eintersects_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); extern int eintersects_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); extern int etouches_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); + + + + + extern Temporal *tcontains_geo_tpoint(const GSERIALIZED *gs, const Temporal *temp, bool restr, bool atvalue); extern Temporal *tdisjoint_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, bool restr, bool atvalue); extern Temporal *tdwithin_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, double dist, bool restr, bool atvalue); @@ -1880,23 +1897,23 @@ extern GSERIALIZED *tpoint_twcentroid(const Temporal *temp); extern SkipList *ttext_tmax_transfn(SkipList *state, const Temporal *temp); extern SkipList *ttext_tmin_transfn(SkipList *state, const Temporal *temp); +/***************************************************************************** + * Analytics functions for temporal types + *****************************************************************************/ +Temporal *temporal_simplify_min_dist(const Temporal *temp, double dist); +Temporal *temporal_simplify_min_tdelta(const Temporal *temp, const Interval *mint); +Temporal *temporal_simplify_dp(const Temporal *temp, double eps_dist, bool synchronized); +Temporal *temporal_simplify_max_dist(const Temporal *temp, double eps_dist, bool synchronized); + + -extern double float_bucket(double value, double size, double origin); -extern Span *floatspan_bucket_list(const Span *bounds, double size, double origin, int *newcount); -extern int int_bucket(int value, int size, int origin); -extern Span *intspan_bucket_list(const Span *bounds, int size, int origin, int *newcount); -extern Span *period_bucket_list(const Span *bounds, const Interval *duration, TimestampTz origin, int *newcount); -extern STBox *stbox_tile_list(const STBox *bounds, double xsize, double ysize, double zsize, const Interval *duration, GSERIALIZED *sorigin, TimestampTz torigin, int **cellcount); -extern TBox *tbox_tile_list(const TBox *bounds, double xsize, const Interval *duration, double xorigin, TimestampTz torigin, int *rows, int *columns); -extern Temporal **temporal_time_split(Temporal *temp, Interval *duration, TimestampTz torigin, int *newcount); -extern Temporal **tfloat_value_split(Temporal *temp, double size, double origin, int *newcount); -extern Temporal **tfloat_value_time_split(Temporal *temp, double size, double vorigin, Interval *duration, TimestampTz torigin, int *newcount); -extern TimestampTz timestamptz_bucket(TimestampTz timestamp, const Interval *duration, TimestampTz origin); -extern Temporal **tint_value_split(Temporal *temp, int size, int origin, int *newcount); -extern Temporal **tint_value_time_split(Temporal *temp, int size, int vorigin, Interval *duration, TimestampTz torigin, int *newcount); + + +extern Temporal *temporal_tprecision(const Temporal *temp, const Interval *duration, TimestampTz origin); +extern Temporal *temporal_tsample(const Temporal *temp, const Interval *duration, TimestampTz origin); @@ -1912,13 +1929,19 @@ extern double temporal_hausdorff_distance(const Temporal *temp1, const Temporal -Temporal *geo_to_tpoint(const GSERIALIZED *gs); -Temporal *temporal_simplify_min_dist(const Temporal *temp, double dist); -Temporal *temporal_simplify_min_tdelta(const Temporal *temp, const Interval *mint); -Temporal *temporal_simplify_dp(const Temporal *temp, double eps_dist, bool synchronized); -Temporal *temporal_simplify_max_dist(const Temporal *temp, double eps_dist, bool synchronized); -bool tpoint_AsMVTGeom(const Temporal *temp, const STBox *bounds, int32_t extent, int32_t buffer, bool clip_geom, GSERIALIZED **gsarr, int64 **timesarr, int *count); -bool tpoint_to_geo_meas(const Temporal *tpoint, const Temporal *measure, bool segmentize, GSERIALIZED **result); +extern double float_bucket(double value, double size, double origin); +extern Span *floatspan_bucket_list(const Span *bounds, double size, double origin, int *newcount); +extern int int_bucket(int value, int size, int origin); +extern Span *intspan_bucket_list(const Span *bounds, int size, int origin, int *newcount); +extern Span *period_bucket_list(const Span *bounds, const Interval *duration, TimestampTz origin, int *newcount); +extern STBox *stbox_tile_list(const STBox *bounds, double xsize, double ysize, double zsize, const Interval *duration, GSERIALIZED *sorigin, TimestampTz torigin, int **cellcount); +extern TBox *tbox_tile_list(const TBox *bounds, double xsize, const Interval *duration, double xorigin, TimestampTz torigin, int *rows, int *columns); +extern Temporal **temporal_time_split(Temporal *temp, Interval *duration, TimestampTz torigin, int *newcount); +extern Temporal **tfloat_value_split(Temporal *temp, double size, double origin, int *newcount); +extern Temporal **tfloat_value_time_split(Temporal *temp, double size, double vorigin, Interval *duration, TimestampTz torigin, int *newcount); +extern TimestampTz timestamptz_bucket(TimestampTz timestamp, const Interval *duration, TimestampTz origin); +extern Temporal **tint_value_split(Temporal *temp, int size, int origin, int *newcount); +extern Temporal **tint_value_time_split(Temporal *temp, int size, int vorigin, Interval *duration, TimestampTz torigin, int *newcount); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 9d9c1efd..bea22af4 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -1750,1666 +1750,1504 @@ def timestampset_shift_scale(ts: 'const Set *', shift: "Optional['const Interval return result if result != _ffi.NULL else None -def adjacent_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': +def intersection_bigintset_bigint(s: 'const Set *', i: int) -> 'int64': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintset_bigint(s_converted, i_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_bigintspan_bigint(s: 'const Span *', i: int) -> 'int64': s_converted = _ffi.cast('const Span *', s) i_converted = _ffi.cast('int64', i) - result = _lib.adjacent_bigintspan_bigint(s_converted, i_converted) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintspan_bigint(s_converted, i_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': +def intersection_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'int64': ss_converted = _ffi.cast('const SpanSet *', ss) i_converted = _ffi.cast('int64', i) - result = _lib.adjacent_bigintspanset_bigint(ss_converted, i_converted) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintspanset_bigint(ss_converted, i_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_floatspan_float(s: 'const Span *', d: float) -> 'bool': +def intersection_floatset_float(s: 'const Set *', d: float) -> 'double': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('double *') + result = _lib.intersection_floatset_float(s_converted, d, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_floatspan_float(s: 'const Span *', d: float) -> 'double': s_converted = _ffi.cast('const Span *', s) - result = _lib.adjacent_floatspan_float(s_converted, d) + out_result = _ffi.new('double *') + result = _lib.intersection_floatspan_float(s_converted, d, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': +def intersection_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.adjacent_floatspanset_float(ss_converted, d) + out_result = _ffi.new('double *') + result = _lib.intersection_floatspanset_float(ss_converted, d, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_intspan_int(s: 'const Span *', i: int) -> 'bool': +def intersection_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'GSERIALIZED **': + s_converted = _ffi.cast('const Set *', s) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.intersection_geoset_geo(s_converted, gs_converted, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def intersection_intset_int(s: 'const Set *', i: int) -> 'int': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('int *') + result = _lib.intersection_intset_int(s_converted, i, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_intspan_int(s: 'const Span *', i: int) -> 'int': s_converted = _ffi.cast('const Span *', s) - result = _lib.adjacent_intspan_int(s_converted, i) + out_result = _ffi.new('int *') + result = _lib.intersection_intspan_int(s_converted, i, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': +def intersection_intspanset_int(ss: 'const SpanSet *', i: int) -> 'int': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.adjacent_intspanset_int(ss_converted, i) + out_result = _ffi.new('int *') + result = _lib.intersection_intspanset_int(ss_converted, i, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_period_timestamp(p: 'const Span *', t: int) -> 'bool': - p_converted = _ffi.cast('const Span *', p) +def intersection_period_timestamp(s: 'const Span *', t: int) -> int: + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.adjacent_period_timestamp(p_converted, t_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.intersection_period_timestamp(s_converted, t_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) +def intersection_periodset_timestamp(ss: 'const SpanSet *', t: int) -> int: + ss_converted = _ffi.cast('const SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.adjacent_periodset_timestamp(ps_converted, t_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.intersection_periodset_timestamp(ss_converted, t_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.intersection_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def adjacent_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': +def intersection_span_span(s1: 'const Span *', s2: 'const Span *') -> 'Span *': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) - result = _lib.adjacent_span_span(s1_converted, s2_converted) + result = _lib.intersection_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def adjacent_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def intersection_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - result = _lib.adjacent_spanset_span(ss_converted, s_converted) + result = _lib.intersection_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def adjacent_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': +def intersection_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': ss1_converted = _ffi.cast('const SpanSet *', ss1) ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.adjacent_spanset_spanset(ss1_converted, ss2_converted) + result = _lib.intersection_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def contained_bigint_bigintset(i: int, s: 'const Set *') -> 'bool': +def intersection_textset_text(s: 'const Set *', txt: str) -> 'text **': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + out_result = _ffi.new('text **') + result = _lib.intersection_textset_text(s_converted, txt_converted, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def intersection_timestampset_timestamp(s: 'const Set *', t: int) -> int: + s_converted = _ffi.cast('const Set *', s) + t_converted = _ffi.cast('TimestampTz', t) + out_result = _ffi.new('TimestampTz *') + result = _lib.intersection_timestampset_timestamp(s_converted, t_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_bigint_bigintset(i: int, s: 'const Set *') -> 'int64': i_converted = _ffi.cast('int64', i) s_converted = _ffi.cast('const Set *', s) - result = _lib.contained_bigint_bigintset(i_converted, s_converted) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintset(i_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': +def minus_bigint_bigintspan(i: int, s: 'const Span *') -> 'int64': i_converted = _ffi.cast('int64', i) s_converted = _ffi.cast('const Span *', s) - result = _lib.contained_bigint_bigintspan(i_converted, s_converted) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintspan(i_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'bool': +def minus_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'int64': i_converted = _ffi.cast('int64', i) ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contained_bigint_bigintspanset(i_converted, ss_converted) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintspanset(i_converted, ss_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_float_floatset(d: float, s: 'const Set *') -> 'bool': +def minus_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) - result = _lib.contained_float_floatset(d, s_converted) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintset_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def contained_float_floatspan(d: float, s: 'const Span *') -> 'bool': +def minus_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - result = _lib.contained_float_floatspan(d, s_converted) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintspan_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def contained_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'bool': +def minus_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contained_float_floatspanset(d, ss_converted) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintspanset_bigint(ss_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def contained_int_intset(i: int, s: 'const Set *') -> 'bool': +def minus_float_floatset(d: float, s: 'const Set *') -> 'double': s_converted = _ffi.cast('const Set *', s) - result = _lib.contained_int_intset(i, s_converted) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatset(d, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_int_intspan(i: int, s: 'const Span *') -> 'bool': +def minus_float_floatspan(d: float, s: 'const Span *') -> 'double': s_converted = _ffi.cast('const Span *', s) - result = _lib.contained_int_intspan(i, s_converted) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatspan(d, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_int_intspanset(i: int, ss: 'const SpanSet *') -> 'bool': +def minus_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'double': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contained_int_intspanset(i, ss_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def contained_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.contained_set_set(s1_converted, s2_converted) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatspanset(d, ss_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.contained_span_span(s1_converted, s2_converted) +def minus_floatset_float(s: 'const Set *', d: float) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.minus_floatset_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def contained_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': +def minus_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contained_span_spanset(s_converted, ss_converted) + result = _lib.minus_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def contained_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def minus_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - s_converted = _ffi.cast('const Span *', s) - result = _lib.contained_spanset_span(ss_converted, s_converted) + result = _lib.minus_floatspanset_float(ss_converted, d) _check_error() return result if result != _ffi.NULL else None -def contained_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.contained_spanset_spanset(ss1_converted, ss2_converted) +def minus_geo_geoset(gs: 'const GSERIALIZED *', s: 'const Set *') -> 'GSERIALIZED **': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.minus_geo_geoset(gs_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result if out_result != _ffi.NULL else None + return None -def contained_text_textset(txt: str, s: 'const Set *') -> 'bool': - txt_converted = cstring2text(txt) +def minus_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'Set *': s_converted = _ffi.cast('const Set *', s) - result = _lib.contained_text_textset(txt_converted, s_converted) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.minus_geoset_geo(s_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def contained_timestamp_period(t: int, p: 'const Span *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - p_converted = _ffi.cast('const Span *', p) - result = _lib.contained_timestamp_period(t_converted, p_converted) +def minus_int_intset(i: int, s: 'const Set *') -> 'int': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('int *') + result = _lib.minus_int_intset(i, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_timestamp_periodset(t: int, ss: 'const SpanSet *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contained_timestamp_periodset(t_converted, ss_converted) +def minus_int_intspan(i: int, s: 'const Span *') -> 'int': + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('int *') + result = _lib.minus_int_intspan(i, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.contained_timestamp_timestampset(t_converted, ts_converted) +def minus_int_intspanset(i: int, ss: 'const SpanSet *') -> 'int': + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('int *') + result = _lib.minus_int_intspanset(i, ss_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contains_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': +def minus_intset_int(s: 'const Set *', i: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.contains_bigintset_bigint(s_converted, i_converted) + result = _lib.minus_intset_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def contains_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': +def minus_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.contains_bigintspan_bigint(s_converted, i_converted) + result = _lib.minus_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def contains_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': +def minus_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - i_converted = _ffi.cast('int64', i) - result = _lib.contains_bigintspanset_bigint(ss_converted, i_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def contains_floatset_float(s: 'const Set *', d: float) -> 'bool': - s_converted = _ffi.cast('const Set *', s) - result = _lib.contains_floatset_float(s_converted, d) + result = _lib.minus_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def contains_floatspan_float(s: 'const Span *', d: float) -> 'bool': +def minus_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - result = _lib.contains_floatspan_float(s_converted, d) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.minus_period_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def contains_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': +def minus_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contains_floatspanset_float(ss_converted, d) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.minus_periodset_timestamp(ss_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def contains_intset_int(s: 'const Set *', i: int) -> 'bool': - s_converted = _ffi.cast('const Set *', s) - result = _lib.contains_intset_int(s_converted, i) +def minus_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.minus_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def contains_intspan_int(s: 'const Span *', i: int) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.contains_intspan_int(s_converted, i) +def minus_span_span(s1: 'const Span *', s2: 'const Span *') -> 'SpanSet *': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.minus_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def contains_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': +def minus_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contains_intspanset_int(ss_converted, i) + result = _lib.minus_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def contains_period_timestamp(p: 'const Span *', t: int) -> 'bool': - p_converted = _ffi.cast('const Span *', p) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.contains_period_timestamp(p_converted, t_converted) +def minus_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + s_converted = _ffi.cast('const Span *', s) + result = _lib.minus_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def contains_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.contains_periodset_timestamp(ps_converted, t_converted) +def minus_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.minus_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def contains_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.contains_set_set(s1_converted, s2_converted) +def minus_text_textset(txt: str, s: 'const Set *') -> 'text **': + txt_converted = cstring2text(txt) + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('text **') + result = _lib.minus_text_textset(txt_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result if out_result != _ffi.NULL else None + return None -def contains_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.contains_span_span(s1_converted, s2_converted) +def minus_textset_text(s: 'const Set *', txt: str) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.minus_textset_text(s_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def contains_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': +def minus_timestamp_period(t: int, s: 'const Span *') -> int: + t_converted = _ffi.cast('TimestampTz', t) s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contains_span_spanset(s_converted, ss_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.minus_timestamp_period(t_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contains_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def minus_timestamp_periodset(t: int, ss: 'const SpanSet *') -> int: + t_converted = _ffi.cast('TimestampTz', t) ss_converted = _ffi.cast('const SpanSet *', ss) - s_converted = _ffi.cast('const Span *', s) - result = _lib.contains_spanset_span(ss_converted, s_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def contains_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.contains_spanset_spanset(ss1_converted, ss2_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.minus_timestamp_periodset(t_converted, ss_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contains_textset_text(s: 'const Set *', t: str) -> 'bool': +def minus_timestamp_timestampset(t: int, s: 'const Set *') -> int: + t_converted = _ffi.cast('TimestampTz', t) s_converted = _ffi.cast('const Set *', s) - t_converted = cstring2text(t) - result = _lib.contains_textset_text(s_converted, t_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.minus_timestamp_timestampset(t_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contains_timestampset_timestamp(s: 'const Set *', t: int) -> 'bool': +def minus_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.contains_timestampset_timestamp(s_converted, t_converted) + result = _lib.minus_timestampset_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overlaps_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.overlaps_set_set(s1_converted, s2_converted) +def union_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintset_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def overlaps_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.overlaps_span_span(s1_converted, s2_converted) +def union_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintspan_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def overlaps_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def union_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - s_converted = _ffi.cast('const Span *', s) - result = _lib.overlaps_spanset_span(ss_converted, s_converted) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintspanset_bigint(ss_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def overlaps_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.overlaps_spanset_spanset(ss1_converted, ss2_converted) +def union_floatset_float(s: 'const Set *', d: float) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.union_floatset_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def after_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.after_timestamp_timestampset(t_converted, ts_converted) +def union_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.union_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def before_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.before_periodset_timestamp(ps_converted, t_converted) +def union_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.union_floatspanset_float(ss_converted, d) _check_error() return result if result != _ffi.NULL else None -def before_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.before_timestamp_timestampset(t_converted, ts_converted) +def union_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.union_geoset_geo(s_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def left_float_floatspan(d: float, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.left_float_floatspan(d, s_converted) +def union_intset_int(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.union_intset_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def left_floatspan_float(s: 'const Span *', d: float) -> 'bool': +def union_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - result = _lib.left_floatspan_float(s_converted, d) + result = _lib.union_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def left_int_intspan(i: int, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.left_int_intspan(i, s_converted) +def union_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.union_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def left_intspan_int(s: 'const Span *', i: int) -> 'bool': +def union_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - result = _lib.left_intspan_int(s_converted, i) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.union_period_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def left_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.left_set_set(s1_converted, s2_converted) +def union_periodset_timestamp(ss: 'SpanSet *', t: int) -> 'SpanSet *': + ss_converted = _ffi.cast('SpanSet *', ss) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.union_periodset_timestamp(ss_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def left_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.left_span_span(s1_converted, s2_converted) +def union_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.union_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def left_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.left_span_spanset(s_converted, ss_converted) +def union_span_span(s1: 'const Span *', s2: 'const Span *') -> 'SpanSet *': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.union_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def left_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def union_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - result = _lib.left_spanset_span(ss_converted, s_converted) + result = _lib.union_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def left_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': +def union_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': ss1_converted = _ffi.cast('const SpanSet *', ss1) ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.left_spanset_spanset(ss1_converted, ss2_converted) + result = _lib.union_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_period_timestamp(p: 'const Span *', t: int) -> 'bool': - p_converted = _ffi.cast('const Span *', p) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overafter_period_timestamp(p_converted, t_converted) +def union_textset_text(s: 'const Set *', txt: str) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.union_textset_text(s_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overafter_periodset_timestamp(ps_converted, t_converted) +def union_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + t_converted = _ffi.cast('const TimestampTz', t) + result = _lib.union_timestampset_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_timestamp_period(t: int, p: 'const Span *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - p_converted = _ffi.cast('const Span *', p) - result = _lib.overafter_timestamp_period(t_converted, p_converted) +def adjacent_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.adjacent_bigintspan_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_timestamp_periodset(t: int, ps: 'const SpanSet *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.overafter_timestamp_periodset(t_converted, ps_converted) +def adjacent_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.adjacent_bigintspanset_bigint(ss_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.overafter_timestamp_timestampset(t_converted, ts_converted) +def adjacent_floatspan_float(s: 'const Span *', d: float) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.adjacent_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def overbefore_period_timestamp(p: 'const Span *', t: int) -> 'bool': - p_converted = _ffi.cast('const Span *', p) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overbefore_period_timestamp(p_converted, t_converted) +def adjacent_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.adjacent_floatspanset_float(ss_converted, d) _check_error() return result if result != _ffi.NULL else None -def overbefore_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overbefore_periodset_timestamp(ps_converted, t_converted) +def adjacent_intspan_int(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.adjacent_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def overbefore_timestamp_period(t: int, p: 'const Span *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - p_converted = _ffi.cast('const Span *', p) - result = _lib.overbefore_timestamp_period(t_converted, p_converted) +def adjacent_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.adjacent_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def overbefore_timestamp_periodset(t: int, ps: 'const SpanSet *') -> 'bool': +def adjacent_period_timestamp(p: 'const Span *', t: int) -> 'bool': + p_converted = _ffi.cast('const Span *', p) t_converted = _ffi.cast('TimestampTz', t) - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.overbefore_timestamp_periodset(t_converted, ps_converted) + result = _lib.adjacent_period_timestamp(p_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overbefore_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': +def adjacent_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': + ps_converted = _ffi.cast('const SpanSet *', ps) t_converted = _ffi.cast('TimestampTz', t) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.overbefore_timestamp_timestampset(t_converted, ts_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_float_floatspan(d: float, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overleft_float_floatspan(d, s_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_floatspan_float(s: 'const Span *', d: float) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overleft_floatspan_float(s_converted, d) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_int_intspan(i: int, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overleft_int_intspan(i, s_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_intspan_int(s: 'const Span *', i: int) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overleft_intspan_int(s_converted, i) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.overleft_set_set(s1_converted, s2_converted) + result = _lib.adjacent_periodset_timestamp(ps_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overleft_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': +def adjacent_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) - result = _lib.overleft_span_span(s1_converted, s2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.overleft_span_spanset(s_converted, ss_converted) + result = _lib.adjacent_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def overleft_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def adjacent_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - result = _lib.overleft_spanset_span(ss_converted, s_converted) + result = _lib.adjacent_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def overleft_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': +def adjacent_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': ss1_converted = _ffi.cast('const SpanSet *', ss1) ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.overleft_spanset_spanset(ss1_converted, ss2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overright_float_floatspan(d: float, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overright_float_floatspan(d, s_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overright_floatspan_float(s: 'const Span *', d: float) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overright_floatspan_float(s_converted, d) + result = _lib.adjacent_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def overright_int_intspan(i: int, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overright_int_intspan(i, s_converted) +def contained_bigint_bigintset(i: int, s: 'const Set *') -> 'bool': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Set *', s) + result = _lib.contained_bigint_bigintset(i_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def overright_intspan_int(s: 'const Span *', i: int) -> 'bool': +def contained_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': + i_converted = _ffi.cast('int64', i) s_converted = _ffi.cast('const Span *', s) - result = _lib.overright_intspan_int(s_converted, i) + result = _lib.contained_bigint_bigintspan(i_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def overright_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.overright_set_set(s1_converted, s2_converted) +def contained_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'bool': + i_converted = _ffi.cast('int64', i) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contained_bigint_bigintspanset(i_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def overright_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.overright_span_span(s1_converted, s2_converted) +def contained_float_floatset(d: float, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.contained_float_floatset(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def overright_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': +def contained_float_floatspan(d: float, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.overright_span_spanset(s_converted, ss_converted) + result = _lib.contained_float_floatspan(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def overright_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def contained_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) - s_converted = _ffi.cast('const Span *', s) - result = _lib.overright_spanset_span(ss_converted, s_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overright_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.overright_spanset_spanset(ss1_converted, ss2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def right_float_floatspan(d: float, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.right_float_floatspan(d, s_converted) + result = _lib.contained_float_floatspanset(d, ss_converted) _check_error() return result if result != _ffi.NULL else None -def right_floatspan_float(s: 'const Span *', d: float) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.right_floatspan_float(s_converted, d) +def contained_int_intset(i: int, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.contained_int_intset(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def right_int_intspan(i: int, s: 'const Span *') -> 'bool': +def contained_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.right_int_intspan(i, s_converted) + result = _lib.contained_int_intspan(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def right_intspan_int(s: 'const Span *', i: int) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.right_intspan_int(s_converted, i) +def contained_int_intspanset(i: int, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contained_int_intspanset(i, ss_converted) _check_error() return result if result != _ffi.NULL else None -def right_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': +def contained_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) - result = _lib.right_set_set(s1_converted, s2_converted) + result = _lib.contained_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def right_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': +def contained_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) - result = _lib.right_span_span(s1_converted, s2_converted) + result = _lib.contained_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def right_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': +def contained_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': s_converted = _ffi.cast('const Span *', s) ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.right_span_spanset(s_converted, ss_converted) + result = _lib.contained_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def right_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def contained_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - result = _lib.right_spanset_span(ss_converted, s_converted) + result = _lib.contained_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def right_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': +def contained_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': ss1_converted = _ffi.cast('const SpanSet *', ss1) ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.right_spanset_spanset(ss1_converted, ss2_converted) + result = _lib.contained_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def intersection_bigintset_bigint(s: 'const Set *', i: int) -> 'int64': - s_converted = _ffi.cast('const Set *', s) - i_converted = _ffi.cast('int64', i) - out_result = _ffi.new('int64 *') - result = _lib.intersection_bigintset_bigint(s_converted, i_converted, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_bigintspan_bigint(s: 'const Span *', i: int) -> 'int64': - s_converted = _ffi.cast('const Span *', s) - i_converted = _ffi.cast('int64', i) - out_result = _ffi.new('int64 *') - result = _lib.intersection_bigintspan_bigint(s_converted, i_converted, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'int64': - ss_converted = _ffi.cast('const SpanSet *', ss) - i_converted = _ffi.cast('int64', i) - out_result = _ffi.new('int64 *') - result = _lib.intersection_bigintspanset_bigint(ss_converted, i_converted, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_floatset_float(s: 'const Set *', d: float) -> 'double': - s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('double *') - result = _lib.intersection_floatset_float(s_converted, d, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_floatspan_float(s: 'const Span *', d: float) -> 'double': - s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('double *') - result = _lib.intersection_floatspan_float(s_converted, d, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('double *') - result = _lib.intersection_floatspanset_float(ss_converted, d, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'GSERIALIZED **': - s_converted = _ffi.cast('const Set *', s) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - out_result = _ffi.new('GSERIALIZED **') - result = _lib.intersection_geoset_geo(s_converted, gs_converted, out_result) - _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None - - -def intersection_intset_int(s: 'const Set *', i: int) -> 'int': +def contained_text_textset(txt: str, s: 'const Set *') -> 'bool': + txt_converted = cstring2text(txt) s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('int *') - result = _lib.intersection_intset_int(s_converted, i, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_intspan_int(s: 'const Span *', i: int) -> 'int': - s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('int *') - result = _lib.intersection_intspan_int(s_converted, i, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_intspanset_int(ss: 'const SpanSet *', i: int) -> 'int': - ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('int *') - result = _lib.intersection_intspanset_int(ss_converted, i, out_result) + result = _lib.contained_text_textset(txt_converted, s_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def intersection_period_timestamp(s: 'const Span *', t: int) -> int: - s_converted = _ffi.cast('const Span *', s) +def contained_timestamp_period(t: int, p: 'const Span *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) - out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_period_timestamp(s_converted, t_converted, out_result) + p_converted = _ffi.cast('const Span *', p) + result = _lib.contained_timestamp_period(t_converted, p_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def intersection_periodset_timestamp(ss: 'const SpanSet *', t: int) -> int: - ss_converted = _ffi.cast('const SpanSet *', ss) +def contained_timestamp_periodset(t: int, ss: 'const SpanSet *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) - out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_periodset_timestamp(ss_converted, t_converted, out_result) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contained_timestamp_periodset(t_converted, ss_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def intersection_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.intersection_set_set(s1_converted, s2_converted) +def contained_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.contained_timestamp_timestampset(t_converted, ts_converted) _check_error() return result if result != _ffi.NULL else None -def intersection_span_span(s1: 'const Span *', s2: 'const Span *') -> 'Span *': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.intersection_span_span(s1_converted, s2_converted) +def contains_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.contains_bigintset_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def intersection_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) +def contains_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.intersection_spanset_span(ss_converted, s_converted) + i_converted = _ffi.cast('int64', i) + result = _lib.contains_bigintspan_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def intersection_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.intersection_spanset_spanset(ss1_converted, ss2_converted) +def contains_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.contains_bigintspanset_bigint(ss_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def intersection_textset_text(s: 'const Set *', txt: str) -> 'text **': - s_converted = _ffi.cast('const Set *', s) - txt_converted = cstring2text(txt) - out_result = _ffi.new('text **') - result = _lib.intersection_textset_text(s_converted, txt_converted, out_result) - _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None - - -def intersection_timestampset_timestamp(s: 'const Set *', t: int) -> int: - s_converted = _ffi.cast('const Set *', s) - t_converted = _ffi.cast('TimestampTz', t) - out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_timestampset_timestamp(s_converted, t_converted, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def minus_bigint_bigintset(i: int, s: 'const Set *') -> 'int64': - i_converted = _ffi.cast('int64', i) +def contains_floatset_float(s: 'const Set *', d: float) -> 'bool': s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('int64 *') - result = _lib.minus_bigint_bigintset(i_converted, s_converted, out_result) + result = _lib.contains_floatset_float(s_converted, d) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_bigint_bigintspan(i: int, s: 'const Span *') -> 'int64': - i_converted = _ffi.cast('int64', i) +def contains_floatspan_float(s: 'const Span *', d: float) -> 'bool': s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('int64 *') - result = _lib.minus_bigint_bigintspan(i_converted, s_converted, out_result) + result = _lib.contains_floatspan_float(s_converted, d) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'int64': - i_converted = _ffi.cast('int64', i) +def contains_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('int64 *') - result = _lib.minus_bigint_bigintspanset(i_converted, ss_converted, out_result) + result = _lib.contains_floatspanset_float(ss_converted, d) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': +def contains_intset_int(s: 'const Set *', i: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.minus_bigintset_bigint(s_converted, i_converted) + result = _lib.contains_intset_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def minus_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': +def contains_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.minus_bigintspan_bigint(s_converted, i_converted) + result = _lib.contains_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def minus_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': +def contains_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) - i_converted = _ffi.cast('int64', i) - result = _lib.minus_bigintspanset_bigint(ss_converted, i_converted) + result = _lib.contains_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def minus_float_floatset(d: float, s: 'const Set *') -> 'double': - s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('double *') - result = _lib.minus_float_floatset(d, s_converted, out_result) +def contains_period_timestamp(p: 'const Span *', t: int) -> 'bool': + p_converted = _ffi.cast('const Span *', p) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.contains_period_timestamp(p_converted, t_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_float_floatspan(d: float, s: 'const Span *') -> 'double': - s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('double *') - result = _lib.minus_float_floatspan(d, s_converted, out_result) +def contains_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': + ps_converted = _ffi.cast('const SpanSet *', ps) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.contains_periodset_timestamp(ps_converted, t_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('double *') - result = _lib.minus_float_floatspanset(d, ss_converted, out_result) +def contains_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.contains_set_set(s1_converted, s2_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_floatset_float(s: 'const Set *', d: float) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - result = _lib.minus_floatset_float(s_converted, d) +def contains_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.contains_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def minus_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': +def contains_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.minus_floatspan_float(s_converted, d) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contains_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def minus_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': +def contains_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.minus_floatspanset_float(ss_converted, d) + s_converted = _ffi.cast('const Span *', s) + result = _lib.contains_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def minus_geo_geoset(gs: 'const GSERIALIZED *', s: 'const Set *') -> 'GSERIALIZED **': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('GSERIALIZED **') - result = _lib.minus_geo_geoset(gs_converted, s_converted, out_result) +def contains_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.contains_spanset_spanset(ss1_converted, ss2_converted) _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'Set *': +def contains_textset_text(s: 'const Set *', t: str) -> 'bool': s_converted = _ffi.cast('const Set *', s) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.minus_geoset_geo(s_converted, gs_converted) + t_converted = cstring2text(t) + result = _lib.contains_textset_text(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def minus_int_intset(i: int, s: 'const Set *') -> 'int': +def contains_timestampset_timestamp(s: 'const Set *', t: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('int *') - result = _lib.minus_int_intset(i, s_converted, out_result) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.contains_timestampset_timestamp(s_converted, t_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_int_intspan(i: int, s: 'const Span *') -> 'int': - s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('int *') - result = _lib.minus_int_intspan(i, s_converted, out_result) +def overlaps_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.overlaps_set_set(s1_converted, s2_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_int_intspanset(i: int, ss: 'const SpanSet *') -> 'int': - ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('int *') - result = _lib.minus_int_intspanset(i, ss_converted, out_result) +def overlaps_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.overlaps_span_span(s1_converted, s2_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_intset_int(s: 'const Set *', i: int) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - result = _lib.minus_intset_int(s_converted, i) +def overlaps_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + s_converted = _ffi.cast('const Span *', s) + result = _lib.overlaps_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def minus_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': - s_converted = _ffi.cast('const Span *', s) - result = _lib.minus_intspan_int(s_converted, i) +def overlaps_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.overlaps_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def minus_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.minus_intspanset_int(ss_converted, i) +def after_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.after_timestamp_timestampset(t_converted, ts_converted) _check_error() return result if result != _ffi.NULL else None -def minus_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': - s_converted = _ffi.cast('const Span *', s) +def before_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': + ps_converted = _ffi.cast('const SpanSet *', ps) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_period_timestamp(s_converted, t_converted) + result = _lib.before_periodset_timestamp(ps_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def minus_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) +def before_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_periodset_timestamp(ss_converted, t_converted) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.before_timestamp_timestampset(t_converted, ts_converted) _check_error() return result if result != _ffi.NULL else None -def minus_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.minus_set_set(s1_converted, s2_converted) +def left_float_floatspan(d: float, s: 'const Span *') -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.left_float_floatspan(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def minus_span_span(s1: 'const Span *', s2: 'const Span *') -> 'SpanSet *': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.minus_span_span(s1_converted, s2_converted) +def left_floatspan_float(s: 'const Span *', d: float) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.left_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def minus_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'SpanSet *': +def left_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.minus_span_spanset(s_converted, ss_converted) + result = _lib.left_int_intspan(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def minus_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) +def left_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.minus_spanset_span(ss_converted, s_converted) + result = _lib.left_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def minus_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.minus_spanset_spanset(ss1_converted, ss2_converted) +def left_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.left_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def minus_text_textset(txt: str, s: 'const Set *') -> 'text **': - txt_converted = cstring2text(txt) - s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('text **') - result = _lib.minus_text_textset(txt_converted, s_converted, out_result) +def left_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.left_span_span(s1_converted, s2_converted) _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_textset_text(s: 'const Set *', txt: str) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - txt_converted = cstring2text(txt) - result = _lib.minus_textset_text(s_converted, txt_converted) +def left_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': + s_converted = _ffi.cast('const Span *', s) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.left_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def minus_timestamp_period(t: int, s: 'const Span *') -> int: - t_converted = _ffi.cast('TimestampTz', t) +def left_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('TimestampTz *') - result = _lib.minus_timestamp_period(t_converted, s_converted, out_result) + result = _lib.left_spanset_span(ss_converted, s_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_timestamp_periodset(t: int, ss: 'const SpanSet *') -> int: - t_converted = _ffi.cast('TimestampTz', t) - ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('TimestampTz *') - result = _lib.minus_timestamp_periodset(t_converted, ss_converted, out_result) +def left_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.left_spanset_spanset(ss1_converted, ss2_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_timestamp_timestampset(t: int, s: 'const Set *') -> int: +def overafter_period_timestamp(p: 'const Span *', t: int) -> 'bool': + p_converted = _ffi.cast('const Span *', p) t_converted = _ffi.cast('TimestampTz', t) - s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('TimestampTz *') - result = _lib.minus_timestamp_timestampset(t_converted, s_converted, out_result) + result = _lib.overafter_period_timestamp(p_converted, t_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) +def overafter_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': + ps_converted = _ffi.cast('const SpanSet *', ps) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_timestampset_timestamp(s_converted, t_converted) + result = _lib.overafter_periodset_timestamp(ps_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def union_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.union_bigintset_bigint(s_converted, i_converted) +def overafter_timestamp_period(t: int, p: 'const Span *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + p_converted = _ffi.cast('const Span *', p) + result = _lib.overafter_timestamp_period(t_converted, p_converted) _check_error() return result if result != _ffi.NULL else None -def union_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': - s_converted = _ffi.cast('const Span *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.union_bigintspan_bigint(s_converted, i_converted) +def overafter_timestamp_periodset(t: int, ps: 'const SpanSet *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.overafter_timestamp_periodset(t_converted, ps_converted) _check_error() return result if result != _ffi.NULL else None -def union_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) - i_converted = _ffi.cast('int64', i) - result = _lib.union_bigintspanset_bigint(ss_converted, i_converted) +def overafter_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.overafter_timestamp_timestampset(t_converted, ts_converted) _check_error() return result if result != _ffi.NULL else None -def union_floatset_float(s: 'const Set *', d: float) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - result = _lib.union_floatset_float(s_converted, d) +def overbefore_period_timestamp(p: 'const Span *', t: int) -> 'bool': + p_converted = _ffi.cast('const Span *', p) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.overbefore_period_timestamp(p_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def union_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': - s_converted = _ffi.cast('const Span *', s) - result = _lib.union_floatspan_float(s_converted, d) +def overbefore_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': + ps_converted = _ffi.cast('const SpanSet *', ps) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.overbefore_periodset_timestamp(ps_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def union_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.union_floatspanset_float(ss_converted, d) +def overbefore_timestamp_period(t: int, p: 'const Span *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + p_converted = _ffi.cast('const Span *', p) + result = _lib.overbefore_timestamp_period(t_converted, p_converted) _check_error() return result if result != _ffi.NULL else None -def union_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.union_geoset_geo(s_converted, gs_converted) +def overbefore_timestamp_periodset(t: int, ps: 'const SpanSet *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.overbefore_timestamp_periodset(t_converted, ps_converted) _check_error() return result if result != _ffi.NULL else None -def union_intset_int(s: 'const Set *', i: int) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - result = _lib.union_intset_int(s_converted, i) +def overbefore_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.overbefore_timestamp_timestampset(t_converted, ts_converted) _check_error() return result if result != _ffi.NULL else None -def union_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': +def overleft_float_floatspan(d: float, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.union_intspan_int(s_converted, i) + result = _lib.overleft_float_floatspan(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def union_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.union_intspanset_int(ss_converted, i) +def overleft_floatspan_float(s: 'const Span *', d: float) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.overleft_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def union_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': +def overleft_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.union_period_timestamp(s_converted, t_converted) + result = _lib.overleft_int_intspan(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def union_periodset_timestamp(ss: 'SpanSet *', t: int) -> 'SpanSet *': - ss_converted = _ffi.cast('SpanSet *', ss) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.union_periodset_timestamp(ss_converted, t_converted) +def overleft_intspan_int(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.overleft_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def union_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': +def overleft_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) - result = _lib.union_set_set(s1_converted, s2_converted) + result = _lib.overleft_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def union_span_span(s1: 'const Span *', s2: 'const Span *') -> 'SpanSet *': +def overleft_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) - result = _lib.union_span_span(s1_converted, s2_converted) + result = _lib.overleft_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def union_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': +def overleft_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': + s_converted = _ffi.cast('const Span *', s) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overleft_span_spanset(s_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overleft_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - result = _lib.union_spanset_span(ss_converted, s_converted) + result = _lib.overleft_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def union_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': +def overleft_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': ss1_converted = _ffi.cast('const SpanSet *', ss1) ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.union_spanset_spanset(ss1_converted, ss2_converted) + result = _lib.overleft_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def union_textset_text(s: 'const Set *', txt: str) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - txt_converted = cstring2text(txt) - result = _lib.union_textset_text(s_converted, txt_converted) +def overright_float_floatspan(d: float, s: 'const Span *') -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.overright_float_floatspan(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def union_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - t_converted = _ffi.cast('const TimestampTz', t) - result = _lib.union_timestampset_timestamp(s_converted, t_converted) +def overright_floatspan_float(s: 'const Span *', d: float) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.overright_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def distance_bigintset_bigint(s: 'const Set *', i: int) -> 'double': - s_converted = _ffi.cast('const Set *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.distance_bigintset_bigint(s_converted, i_converted) +def overright_int_intspan(i: int, s: 'const Span *') -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.overright_int_intspan(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def distance_bigintspan_bigint(s: 'const Span *', i: int) -> 'double': +def overright_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.distance_bigintspan_bigint(s_converted, i_converted) + result = _lib.overright_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def distance_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) - i_converted = _ffi.cast('int64', i) - result = _lib.distance_bigintspanset_bigint(ss_converted, i_converted) +def overright_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.overright_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_floatset_float(s: 'const Set *', d: float) -> 'double': - s_converted = _ffi.cast('const Set *', s) - result = _lib.distance_floatset_float(s_converted, d) +def overright_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.overright_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_floatspan_float(s: 'const Span *', d: float) -> 'double': +def overright_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.distance_floatspan_float(s_converted, d) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overright_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def distance_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': +def overright_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.distance_floatspanset_float(ss_converted, d) + s_converted = _ffi.cast('const Span *', s) + result = _lib.overright_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def distance_intset_int(s: 'const Set *', i: int) -> 'double': - s_converted = _ffi.cast('const Set *', s) - result = _lib.distance_intset_int(s_converted, i) +def overright_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.overright_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_intspan_int(s: 'const Span *', i: int) -> 'double': +def right_float_floatspan(d: float, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.distance_intspan_int(s_converted, i) + result = _lib.right_float_floatspan(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def distance_intspanset_int(ss: 'const SpanSet *', i: int) -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.distance_intspanset_int(ss_converted, i) +def right_floatspan_float(s: 'const Span *', d: float) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.right_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def distance_period_timestamp(s: 'const Span *', t: int) -> 'double': +def right_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.distance_period_timestamp(s_converted, t_converted) + result = _lib.right_int_intspan(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def distance_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.distance_periodset_timestamp(ss_converted, t_converted) +def right_intspan_int(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.right_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def distance_set_set(s1: 'const Set *', s2: 'const Set *') -> 'double': +def right_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) - result = _lib.distance_set_set(s1_converted, s2_converted) + result = _lib.right_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_span_span(s1: 'const Span *', s2: 'const Span *') -> 'double': +def right_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) - result = _lib.distance_span_span(s1_converted, s2_converted) + result = _lib.right_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) +def right_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.distance_spanset_span(ss_converted, s_converted) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.right_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def distance_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'double': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.distance_spanset_spanset(ss1_converted, ss2_converted) +def right_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + s_converted = _ffi.cast('const Span *', s) + result = _lib.right_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def distance_timestampset_timestamp(s: 'const Set *', t: int) -> 'double': - s_converted = _ffi.cast('const Set *', s) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.distance_timestampset_timestamp(s_converted, t_converted) +def right_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.right_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def bigint_extent_transfn(s: 'Span *', i: int) -> 'Span *': - s_converted = _ffi.cast('Span *', s) +def distance_bigintset_bigint(s: 'const Set *', i: int) -> 'double': + s_converted = _ffi.cast('const Set *', s) i_converted = _ffi.cast('int64', i) - result = _lib.bigint_extent_transfn(s_converted, i_converted) + result = _lib.distance_bigintset_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def bigint_union_transfn(state: 'Set *', i: int) -> 'Set *': - state_converted = _ffi.cast('Set *', state) +def distance_bigintspan_bigint(s: 'const Span *', i: int) -> 'double': + s_converted = _ffi.cast('const Span *', s) i_converted = _ffi.cast('int64', i) - result = _lib.bigint_union_transfn(state_converted, i_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def float_extent_transfn(s: 'Span *', d: float) -> 'Span *': - s_converted = _ffi.cast('Span *', s) - result = _lib.float_extent_transfn(s_converted, d) - _check_error() - return result if result != _ffi.NULL else None - - -def float_union_transfn(state: 'Set *', d: float) -> 'Set *': - state_converted = _ffi.cast('Set *', state) - result = _lib.float_union_transfn(state_converted, d) - _check_error() - return result if result != _ffi.NULL else None - - -def int_extent_transfn(s: 'Span *', i: int) -> 'Span *': - s_converted = _ffi.cast('Span *', s) - result = _lib.int_extent_transfn(s_converted, i) - _check_error() - return result if result != _ffi.NULL else None - - -def int_union_transfn(state: 'Set *', i: int) -> 'Set *': - state_converted = _ffi.cast('Set *', state) - result = _lib.int_union_transfn(state_converted, i) - _check_error() - return result if result != _ffi.NULL else None - - -def period_tcount_transfn(state: "Optional['SkipList *']", p: 'const Span *') -> 'SkipList *': - state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL - p_converted = _ffi.cast('const Span *', p) - result = _lib.period_tcount_transfn(state_converted, p_converted) + result = _lib.distance_bigintspan_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None - - -def periodset_tcount_transfn(state: "Optional['SkipList *']", ps: 'const SpanSet *') -> 'SkipList *': - state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.periodset_tcount_transfn(state_converted, ps_converted) + + +def distance_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.distance_bigintspanset_bigint(ss_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def set_extent_transfn(span: 'Span *', set: 'const Set *') -> 'Span *': - span_converted = _ffi.cast('Span *', span) - set_converted = _ffi.cast('const Set *', set) - result = _lib.set_extent_transfn(span_converted, set_converted) +def distance_floatset_float(s: 'const Set *', d: float) -> 'double': + s_converted = _ffi.cast('const Set *', s) + result = _lib.distance_floatset_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def set_union_finalfn(state: 'Set *') -> 'Set *': - state_converted = _ffi.cast('Set *', state) - result = _lib.set_union_finalfn(state_converted) +def distance_floatspan_float(s: 'const Span *', d: float) -> 'double': + s_converted = _ffi.cast('const Span *', s) + result = _lib.distance_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def set_union_transfn(state: 'Set *', set: 'Set *') -> 'Set *': - state_converted = _ffi.cast('Set *', state) - set_converted = _ffi.cast('Set *', set) - result = _lib.set_union_transfn(state_converted, set_converted) +def distance_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.distance_floatspanset_float(ss_converted, d) _check_error() return result if result != _ffi.NULL else None -def span_extent_transfn(s1: 'Span *', s2: 'const Span *') -> 'Span *': - s1_converted = _ffi.cast('Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.span_extent_transfn(s1_converted, s2_converted) +def distance_intset_int(s: 'const Set *', i: int) -> 'double': + s_converted = _ffi.cast('const Set *', s) + result = _lib.distance_intset_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def span_union_transfn(state: 'SpanSet *', span: 'const Span *') -> 'SpanSet *': - state_converted = _ffi.cast('SpanSet *', state) - span_converted = _ffi.cast('const Span *', span) - result = _lib.span_union_transfn(state_converted, span_converted) +def distance_intspan_int(s: 'const Span *', i: int) -> 'double': + s_converted = _ffi.cast('const Span *', s) + result = _lib.distance_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def spanset_extent_transfn(s: 'Span *', ss: 'const SpanSet *') -> 'Span *': - s_converted = _ffi.cast('Span *', s) +def distance_intspanset_int(ss: 'const SpanSet *', i: int) -> 'double': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.spanset_extent_transfn(s_converted, ss_converted) + result = _lib.distance_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def spanset_union_finalfn(state: 'SpanSet *') -> 'SpanSet *': - state_converted = _ffi.cast('SpanSet *', state) - result = _lib.spanset_union_finalfn(state_converted) +def distance_period_timestamp(s: 'const Span *', t: int) -> 'double': + s_converted = _ffi.cast('const Span *', s) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.distance_period_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def spanset_union_transfn(state: 'SpanSet *', ss: 'const SpanSet *') -> 'SpanSet *': - state_converted = _ffi.cast('SpanSet *', state) +def distance_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'double': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.spanset_union_transfn(state_converted, ss_converted) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.distance_periodset_timestamp(ss_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def text_union_transfn(state: 'Set *', txt: str) -> 'Set *': - state_converted = _ffi.cast('Set *', state) - txt_converted = cstring2text(txt) - result = _lib.text_union_transfn(state_converted, txt_converted) +def distance_set_set(s1: 'const Set *', s2: 'const Set *') -> 'double': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.distance_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def timestamp_extent_transfn(p: "Optional['Span *']", t: int) -> 'Span *': - p_converted = _ffi.cast('Span *', p) if p is not None else _ffi.NULL - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.timestamp_extent_transfn(p_converted, t_converted) +def distance_span_span(s1: 'const Span *', s2: 'const Span *') -> 'double': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.distance_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def timestamp_tcount_transfn(state: "Optional['SkipList *']", t: int) -> 'SkipList *': - state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.timestamp_tcount_transfn(state_converted, t_converted) +def distance_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + s_converted = _ffi.cast('const Span *', s) + result = _lib.distance_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def timestamp_union_transfn(state: 'Set *', t: int) -> 'Set *': - state_converted = _ffi.cast('Set *', state) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.timestamp_union_transfn(state_converted, t_converted) +def distance_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'double': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.distance_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def timestampset_tcount_transfn(state: "Optional['SkipList *']", ts: 'const Set *') -> 'SkipList *': - state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.timestampset_tcount_transfn(state_converted, ts_converted) +def distance_timestampset_timestamp(s: 'const Set *', t: int) -> 'double': + s_converted = _ffi.cast('const Set *', s) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.distance_timestampset_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None @@ -3582,6 +3420,168 @@ def spanset_ne(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': return result if result != _ffi.NULL else None +def bigint_extent_transfn(s: 'Span *', i: int) -> 'Span *': + s_converted = _ffi.cast('Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.bigint_extent_transfn(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def bigint_union_transfn(state: 'Set *', i: int) -> 'Set *': + state_converted = _ffi.cast('Set *', state) + i_converted = _ffi.cast('int64', i) + result = _lib.bigint_union_transfn(state_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def float_extent_transfn(s: 'Span *', d: float) -> 'Span *': + s_converted = _ffi.cast('Span *', s) + result = _lib.float_extent_transfn(s_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + +def float_union_transfn(state: 'Set *', d: float) -> 'Set *': + state_converted = _ffi.cast('Set *', state) + result = _lib.float_union_transfn(state_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + +def int_extent_transfn(s: 'Span *', i: int) -> 'Span *': + s_converted = _ffi.cast('Span *', s) + result = _lib.int_extent_transfn(s_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + +def int_union_transfn(state: 'Set *', i: int) -> 'Set *': + state_converted = _ffi.cast('Set *', state) + result = _lib.int_union_transfn(state_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + +def period_tcount_transfn(state: "Optional['SkipList *']", p: 'const Span *') -> 'SkipList *': + state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL + p_converted = _ffi.cast('const Span *', p) + result = _lib.period_tcount_transfn(state_converted, p_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def periodset_tcount_transfn(state: "Optional['SkipList *']", ps: 'const SpanSet *') -> 'SkipList *': + state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.periodset_tcount_transfn(state_converted, ps_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def set_extent_transfn(span: 'Span *', set: 'const Set *') -> 'Span *': + span_converted = _ffi.cast('Span *', span) + set_converted = _ffi.cast('const Set *', set) + result = _lib.set_extent_transfn(span_converted, set_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def set_union_finalfn(state: 'Set *') -> 'Set *': + state_converted = _ffi.cast('Set *', state) + result = _lib.set_union_finalfn(state_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def set_union_transfn(state: 'Set *', set: 'Set *') -> 'Set *': + state_converted = _ffi.cast('Set *', state) + set_converted = _ffi.cast('Set *', set) + result = _lib.set_union_transfn(state_converted, set_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def span_extent_transfn(s1: 'Span *', s2: 'const Span *') -> 'Span *': + s1_converted = _ffi.cast('Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.span_extent_transfn(s1_converted, s2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def span_union_transfn(state: 'SpanSet *', span: 'const Span *') -> 'SpanSet *': + state_converted = _ffi.cast('SpanSet *', state) + span_converted = _ffi.cast('const Span *', span) + result = _lib.span_union_transfn(state_converted, span_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def spanset_extent_transfn(s: 'Span *', ss: 'const SpanSet *') -> 'Span *': + s_converted = _ffi.cast('Span *', s) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.spanset_extent_transfn(s_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def spanset_union_finalfn(state: 'SpanSet *') -> 'SpanSet *': + state_converted = _ffi.cast('SpanSet *', state) + result = _lib.spanset_union_finalfn(state_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def spanset_union_transfn(state: 'SpanSet *', ss: 'const SpanSet *') -> 'SpanSet *': + state_converted = _ffi.cast('SpanSet *', state) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.spanset_union_transfn(state_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def text_union_transfn(state: 'Set *', txt: str) -> 'Set *': + state_converted = _ffi.cast('Set *', state) + txt_converted = cstring2text(txt) + result = _lib.text_union_transfn(state_converted, txt_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def timestamp_extent_transfn(p: "Optional['Span *']", t: int) -> 'Span *': + p_converted = _ffi.cast('Span *', p) if p is not None else _ffi.NULL + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.timestamp_extent_transfn(p_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def timestamp_tcount_transfn(state: "Optional['SkipList *']", t: int) -> 'SkipList *': + state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.timestamp_tcount_transfn(state_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def timestamp_union_transfn(state: 'Set *', t: int) -> 'Set *': + state_converted = _ffi.cast('Set *', state) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.timestamp_union_transfn(state_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def timestampset_tcount_transfn(state: "Optional['SkipList *']", ts: 'const Set *') -> 'SkipList *': + state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.timestampset_tcount_transfn(state_converted, ts_converted) + _check_error() + return result if result != _ffi.NULL else None + + def tbox_in(string: str) -> 'TBox *': string_converted = string.encode('utf-8') result = _lib.tbox_in(string_converted) @@ -3678,123 +3678,118 @@ def stbox_out(box: 'const STBox *', maxdd: int) -> str: return result if result != _ffi.NULL else None -def stbox_make(hasx: bool, hasz: bool, geodetic: bool, srid: int, xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, p: "Optional['const Span *']") -> 'STBox *': - srid_converted = _ffi.cast('int32', srid) - p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL - result = _lib.stbox_make(hasx, hasz, geodetic, srid_converted, xmin, xmax, ymin, ymax, zmin, zmax, p_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def stbox_copy(box: 'const STBox *') -> 'STBox *': - box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_copy(box_converted) +def float_period_to_tbox(d: float, p: 'const Span *') -> 'TBox *': + p_converted = _ffi.cast('const Span *', p) + result = _lib.float_period_to_tbox(d, p_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_make(s: "Optional['const Span *']", p: "Optional['const Span *']") -> 'TBox *': - s_converted = _ffi.cast('const Span *', s) if s is not None else _ffi.NULL - p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL - result = _lib.tbox_make(s_converted, p_converted) +def float_timestamp_to_tbox(d: float, t: int) -> 'TBox *': + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.float_timestamp_to_tbox(d, t_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_copy(box: 'const TBox *') -> 'TBox *': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_copy(box_converted) +def geo_period_to_stbox(gs: 'const GSERIALIZED *', p: 'const Span *') -> 'STBox *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + p_converted = _ffi.cast('const Span *', p) + result = _lib.geo_period_to_stbox(gs_converted, p_converted) _check_error() return result if result != _ffi.NULL else None -def int_to_tbox(i: int) -> 'TBox *': - result = _lib.int_to_tbox(i) +def geo_timestamp_to_stbox(gs: 'const GSERIALIZED *', t: int) -> 'STBox *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.geo_timestamp_to_stbox(gs_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def float_to_tbox(d: float) -> 'TBox *': - result = _lib.float_to_tbox(d) +def int_period_to_tbox(i: int, p: 'const Span *') -> 'TBox *': + p_converted = _ffi.cast('const Span *', p) + result = _lib.int_period_to_tbox(i, p_converted) _check_error() return result if result != _ffi.NULL else None -def timestamp_to_tbox(t: int) -> 'TBox *': +def int_timestamp_to_tbox(i: int, t: int) -> 'TBox *': t_converted = _ffi.cast('TimestampTz', t) - result = _lib.timestamp_to_tbox(t_converted) + result = _lib.int_timestamp_to_tbox(i, t_converted) _check_error() return result if result != _ffi.NULL else None -def timestampset_to_tbox(ss: 'const Set *') -> 'TBox *': - ss_converted = _ffi.cast('const Set *', ss) - result = _lib.timestampset_to_tbox(ss_converted) +def span_period_to_tbox(span: 'const Span *', p: 'const Span *') -> 'TBox *': + span_converted = _ffi.cast('const Span *', span) + p_converted = _ffi.cast('const Span *', p) + result = _lib.span_period_to_tbox(span_converted, p_converted) _check_error() return result if result != _ffi.NULL else None -def period_to_tbox(p: 'const Span *') -> 'TBox *': - p_converted = _ffi.cast('const Span *', p) - result = _lib.period_to_tbox(p_converted) +def span_timestamp_to_tbox(span: 'const Span *', t: int) -> 'TBox *': + span_converted = _ffi.cast('const Span *', span) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.span_timestamp_to_tbox(span_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def periodset_to_tbox(ps: 'const SpanSet *') -> 'TBox *': - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.periodset_to_tbox(ps_converted) +def stbox_copy(box: 'const STBox *') -> 'STBox *': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_copy(box_converted) _check_error() return result if result != _ffi.NULL else None -def int_timestamp_to_tbox(i: int, t: int) -> 'TBox *': - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.int_timestamp_to_tbox(i, t_converted) +def stbox_make(hasx: bool, hasz: bool, geodetic: bool, srid: int, xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, p: "Optional['const Span *']") -> 'STBox *': + srid_converted = _ffi.cast('int32', srid) + p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL + result = _lib.stbox_make(hasx, hasz, geodetic, srid_converted, xmin, xmax, ymin, ymax, zmin, zmax, p_converted) _check_error() return result if result != _ffi.NULL else None -def float_period_to_tbox(d: float, p: 'const Span *') -> 'TBox *': - p_converted = _ffi.cast('const Span *', p) - result = _lib.float_period_to_tbox(d, p_converted) +def tbox_copy(box: 'const TBox *') -> 'TBox *': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_copy(box_converted) _check_error() return result if result != _ffi.NULL else None -def float_timestamp_to_tbox(d: float, t: int) -> 'TBox *': - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.float_timestamp_to_tbox(d, t_converted) +def tbox_make(s: "Optional['const Span *']", p: "Optional['const Span *']") -> 'TBox *': + s_converted = _ffi.cast('const Span *', s) if s is not None else _ffi.NULL + p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL + result = _lib.tbox_make(s_converted, p_converted) _check_error() return result if result != _ffi.NULL else None -def geo_period_to_stbox(gs: 'const GSERIALIZED *', p: 'const Span *') -> 'STBox *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - p_converted = _ffi.cast('const Span *', p) - result = _lib.geo_period_to_stbox(gs_converted, p_converted) +def float_to_tbox(d: float) -> 'TBox *': + result = _lib.float_to_tbox(d) _check_error() return result if result != _ffi.NULL else None -def geo_timestamp_to_stbox(gs: 'const GSERIALIZED *', t: int) -> 'STBox *': +def geo_to_stbox(gs: 'const GSERIALIZED *') -> 'STBox *': gs_converted = _ffi.cast('const GSERIALIZED *', gs) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.geo_timestamp_to_stbox(gs_converted, t_converted) + result = _lib.geo_to_stbox(gs_converted) _check_error() return result if result != _ffi.NULL else None -def geo_to_stbox(gs: 'const GSERIALIZED *') -> 'STBox *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.geo_to_stbox(gs_converted) +def int_to_tbox(i: int) -> 'TBox *': + result = _lib.int_to_tbox(i) _check_error() return result if result != _ffi.NULL else None -def int_period_to_tbox(i: int, p: 'const Span *') -> 'TBox *': - p_converted = _ffi.cast('const Span *', p) - result = _lib.int_period_to_tbox(i, p_converted) +def numset_to_tbox(s: 'const Set *') -> 'TBox *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.numset_to_tbox(s_converted) _check_error() return result if result != _ffi.NULL else None @@ -3806,212 +3801,194 @@ def numspan_to_tbox(s: 'const Span *') -> 'TBox *': return result if result != _ffi.NULL else None -def span_timestamp_to_tbox(span: 'const Span *', t: int) -> 'TBox *': - span_converted = _ffi.cast('const Span *', span) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.span_timestamp_to_tbox(span_converted, t_converted) +def numspanset_to_tbox(ss: 'const SpanSet *') -> 'TBox *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.numspanset_to_tbox(ss_converted) _check_error() return result if result != _ffi.NULL else None -def span_period_to_tbox(span: 'const Span *', p: 'const Span *') -> 'TBox *': - span_converted = _ffi.cast('const Span *', span) +def period_to_stbox(p: 'const Span *') -> 'STBox *': p_converted = _ffi.cast('const Span *', p) - result = _lib.span_period_to_tbox(span_converted, p_converted) + result = _lib.period_to_stbox(p_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_to_floatspan(box: 'const TBox *') -> 'Span *': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_to_floatspan(box_converted) +def period_to_tbox(p: 'const Span *') -> 'TBox *': + p_converted = _ffi.cast('const Span *', p) + result = _lib.period_to_tbox(p_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_to_period(box: 'const TBox *') -> 'Span *': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_to_period(box_converted) +def periodset_to_stbox(ps: 'const SpanSet *') -> 'STBox *': + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.periodset_to_stbox(ps_converted) _check_error() return result if result != _ffi.NULL else None -def stbox_to_period(box: 'const STBox *') -> 'Span *': - box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_to_period(box_converted) +def periodset_to_tbox(ps: 'const SpanSet *') -> 'TBox *': + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.periodset_to_tbox(ps_converted) _check_error() return result if result != _ffi.NULL else None -def tnumber_to_tbox(temp: 'const Temporal *') -> 'TBox *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tnumber_to_tbox(temp_converted) +def stbox_to_geo(box: 'const STBox *') -> 'GSERIALIZED *': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_to_geo(box_converted) _check_error() return result if result != _ffi.NULL else None -def stbox_to_geo(box: 'const STBox *') -> 'GSERIALIZED *': +def stbox_to_period(box: 'const STBox *') -> 'Span *': box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_to_geo(box_converted) + result = _lib.stbox_to_period(box_converted) _check_error() return result if result != _ffi.NULL else None -def tpoint_to_stbox(temp: 'const Temporal *') -> 'STBox *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tpoint_to_stbox(temp_converted) +def tbox_to_floatspan(box: 'const TBox *') -> 'Span *': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_to_floatspan(box_converted) _check_error() return result if result != _ffi.NULL else None -def timestamp_to_stbox(t: int) -> 'STBox *': - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.timestamp_to_stbox(t_converted) +def tbox_to_period(box: 'const TBox *') -> 'Span *': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_to_period(box_converted) _check_error() return result if result != _ffi.NULL else None -def timestampset_to_stbox(ts: 'const Set *') -> 'STBox *': - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.timestampset_to_stbox(ts_converted) +def timestamp_to_stbox(t: int) -> 'STBox *': + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.timestamp_to_stbox(t_converted) _check_error() return result if result != _ffi.NULL else None -def period_to_stbox(p: 'const Span *') -> 'STBox *': - p_converted = _ffi.cast('const Span *', p) - result = _lib.period_to_stbox(p_converted) +def timestamp_to_tbox(t: int) -> 'TBox *': + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.timestamp_to_tbox(t_converted) _check_error() return result if result != _ffi.NULL else None -def periodset_to_stbox(ps: 'const SpanSet *') -> 'STBox *': - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.periodset_to_stbox(ps_converted) +def timestampset_to_stbox(ts: 'const Set *') -> 'STBox *': + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.timestampset_to_stbox(ts_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_hasx(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_hasx(box_converted) +def timestampset_to_tbox(ss: 'const Set *') -> 'TBox *': + ss_converted = _ffi.cast('const Set *', ss) + result = _lib.timestampset_to_tbox(ss_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_hast(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_hast(box_converted) +def tnumber_to_tbox(temp: 'const Temporal *') -> 'TBox *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tnumber_to_tbox(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_xmin(box: 'const TBox *') -> 'double': - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('double *') - result = _lib.tbox_xmin(box_converted, out_result) +def tpoint_to_stbox(temp: 'const Temporal *') -> 'STBox *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tpoint_to_stbox(temp_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_xmin_inc(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('bool *') - result = _lib.tbox_xmin_inc(box_converted, out_result) +def stbox_hast(box: 'const STBox *') -> 'bool': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_hast(box_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_xmax(box: 'const TBox *') -> 'double': - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('double *') - result = _lib.tbox_xmax(box_converted, out_result) +def stbox_hasx(box: 'const STBox *') -> 'bool': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_hasx(box_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_xmax_inc(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('bool *') - result = _lib.tbox_xmax_inc(box_converted, out_result) +def stbox_hasz(box: 'const STBox *') -> 'bool': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_hasz(box_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_tmin(box: 'const TBox *') -> int: - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('TimestampTz *') - result = _lib.tbox_tmin(box_converted, out_result) +def stbox_isgeodetic(box: 'const STBox *') -> 'bool': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_isgeodetic(box_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_tmin_inc(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('bool *') - result = _lib.tbox_tmin_inc(box_converted, out_result) +def stbox_srid(box: 'const STBox *') -> 'int32': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_srid(box_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_tmax(box: 'const TBox *') -> int: - box_converted = _ffi.cast('const TBox *', box) +def stbox_tmax(box: 'const STBox *') -> int: + box_converted = _ffi.cast('const STBox *', box) out_result = _ffi.new('TimestampTz *') - result = _lib.tbox_tmax(box_converted, out_result) + result = _lib.stbox_tmax(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def tbox_tmax_inc(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) +def stbox_tmax_inc(box: 'const STBox *') -> 'bool': + box_converted = _ffi.cast('const STBox *', box) out_result = _ffi.new('bool *') - result = _lib.tbox_tmax_inc(box_converted, out_result) + result = _lib.stbox_tmax_inc(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_hasx(box: 'const STBox *') -> 'bool': - box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_hasx(box_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def stbox_hasz(box: 'const STBox *') -> 'bool': +def stbox_tmin(box: 'const STBox *') -> int: box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_hasz(box_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.stbox_tmin(box_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def stbox_hast(box: 'const STBox *') -> 'bool': +def stbox_tmin_inc(box: 'const STBox *') -> 'bool': box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_hast(box_converted) + out_result = _ffi.new('bool *') + result = _lib.stbox_tmin_inc(box_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def stbox_isgeodetic(box: 'const STBox *') -> 'bool': +def stbox_xmax(box: 'const STBox *') -> 'double': box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_isgeodetic(box_converted) + out_result = _ffi.new('double *') + result = _lib.stbox_xmax(box_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None def stbox_xmin(box: 'const STBox *') -> 'double': @@ -4024,10 +4001,10 @@ def stbox_xmin(box: 'const STBox *') -> 'double': return None -def stbox_xmax(box: 'const STBox *') -> 'double': +def stbox_ymax(box: 'const STBox *') -> 'double': box_converted = _ffi.cast('const STBox *', box) out_result = _ffi.new('double *') - result = _lib.stbox_xmax(box_converted, out_result) + result = _lib.stbox_ymax(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None @@ -4044,10 +4021,10 @@ def stbox_ymin(box: 'const STBox *') -> 'double': return None -def stbox_ymax(box: 'const STBox *') -> 'double': +def stbox_zmax(box: 'const STBox *') -> 'double': box_converted = _ffi.cast('const STBox *', box) out_result = _ffi.new('double *') - result = _lib.stbox_ymax(box_converted, out_result) + result = _lib.stbox_zmax(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None @@ -4064,61 +4041,98 @@ def stbox_zmin(box: 'const STBox *') -> 'double': return None -def stbox_zmax(box: 'const STBox *') -> 'double': - box_converted = _ffi.cast('const STBox *', box) - out_result = _ffi.new('double *') - result = _lib.stbox_zmax(box_converted, out_result) +def tbox_hast(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_hast(box_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tbox_hasx(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_hasx(box_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tbox_tmax(box: 'const TBox *') -> int: + box_converted = _ffi.cast('const TBox *', box) + out_result = _ffi.new('TimestampTz *') + result = _lib.tbox_tmax(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_tmin(box: 'const STBox *') -> int: - box_converted = _ffi.cast('const STBox *', box) +def tbox_tmax_inc(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) + out_result = _ffi.new('bool *') + result = _lib.tbox_tmax_inc(box_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def tbox_tmin(box: 'const TBox *') -> int: + box_converted = _ffi.cast('const TBox *', box) out_result = _ffi.new('TimestampTz *') - result = _lib.stbox_tmin(box_converted, out_result) + result = _lib.tbox_tmin(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_tmin_inc(box: 'const STBox *') -> 'bool': - box_converted = _ffi.cast('const STBox *', box) +def tbox_tmin_inc(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) out_result = _ffi.new('bool *') - result = _lib.stbox_tmin_inc(box_converted, out_result) + result = _lib.tbox_tmin_inc(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_tmax(box: 'const STBox *') -> int: - box_converted = _ffi.cast('const STBox *', box) - out_result = _ffi.new('TimestampTz *') - result = _lib.stbox_tmax(box_converted, out_result) +def tbox_xmax(box: 'const TBox *') -> 'double': + box_converted = _ffi.cast('const TBox *', box) + out_result = _ffi.new('double *') + result = _lib.tbox_xmax(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_tmax_inc(box: 'const STBox *') -> 'bool': - box_converted = _ffi.cast('const STBox *', box) +def tbox_xmax_inc(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) out_result = _ffi.new('bool *') - result = _lib.stbox_tmax_inc(box_converted, out_result) + result = _lib.tbox_xmax_inc(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_srid(box: 'const STBox *') -> 'int32': - box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_srid(box_converted) +def tbox_xmin(box: 'const TBox *') -> 'double': + box_converted = _ffi.cast('const TBox *', box) + out_result = _ffi.new('double *') + result = _lib.tbox_xmin(box_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def tbox_xmin_inc(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) + out_result = _ffi.new('bool *') + result = _lib.tbox_xmin_inc(box_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None def stbox_expand_space(box: 'const STBox *', d: float) -> 'STBox *': @@ -4167,18 +4181,18 @@ def stbox_shift_scale_time(box: 'const STBox *', shift: "Optional['const Interva return result if result != _ffi.NULL else None -def tbox_expand_value(box: 'const TBox *', d: 'const double') -> 'TBox *': +def tbox_expand_time(box: 'const TBox *', interval: 'const Interval *') -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) - d_converted = _ffi.cast('const double', d) - result = _lib.tbox_expand_value(box_converted, d_converted) + interval_converted = _ffi.cast('const Interval *', interval) + result = _lib.tbox_expand_time(box_converted, interval_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_expand_time(box: 'const TBox *', interval: 'const Interval *') -> 'TBox *': +def tbox_expand_value(box: 'const TBox *', d: 'const double') -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) - interval_converted = _ffi.cast('const Interval *', interval) - result = _lib.tbox_expand_time(box_converted, interval_converted) + d_converted = _ffi.cast('const double', d) + result = _lib.tbox_expand_value(box_converted, d_converted) _check_error() return result if result != _ffi.NULL else None @@ -4190,16 +4204,16 @@ def tbox_round(box: 'const TBox *', maxdd: int) -> 'TBox *': return result if result != _ffi.NULL else None -def tbox_shift_scale_int(box: 'const TBox *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'TBox *': +def tbox_shift_scale_float(box: 'const TBox *', shift: float, width: float, hasshift: bool, haswidth: bool) -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_shift_scale_int(box_converted, shift, width, hasshift, haswidth) + result = _lib.tbox_shift_scale_float(box_converted, shift, width, hasshift, haswidth) _check_error() return result if result != _ffi.NULL else None -def tbox_shift_scale_float(box: 'const TBox *', shift: float, width: float, hasshift: bool, haswidth: bool) -> 'TBox *': +def tbox_shift_scale_int(box: 'const TBox *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_shift_scale_float(box_converted, shift, width, hasshift, haswidth) + result = _lib.tbox_shift_scale_int(box_converted, shift, width, hasshift, haswidth) _check_error() return result if result != _ffi.NULL else None @@ -4213,6 +4227,60 @@ def tbox_shift_scale_time(box: 'const TBox *', shift: "Optional['const Interval return result if result != _ffi.NULL else None +def union_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *', strict: bool) -> 'TBox *': + box1_converted = _ffi.cast('const TBox *', box1) + box2_converted = _ffi.cast('const TBox *', box2) + result = _lib.union_tbox_tbox(box1_converted, box2_converted, strict) + _check_error() + return result if result != _ffi.NULL else None + + +def inter_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'TBox *': + box1_converted = _ffi.cast('const TBox *', box1) + box2_converted = _ffi.cast('const TBox *', box2) + out_result = _ffi.new('TBox *') + result = _lib.inter_tbox_tbox(box1_converted, box2_converted, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def intersection_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'TBox *': + box1_converted = _ffi.cast('const TBox *', box1) + box2_converted = _ffi.cast('const TBox *', box2) + result = _lib.intersection_tbox_tbox(box1_converted, box2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def union_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *', strict: bool) -> 'STBox *': + box1_converted = _ffi.cast('const STBox *', box1) + box2_converted = _ffi.cast('const STBox *', box2) + result = _lib.union_stbox_stbox(box1_converted, box2_converted, strict) + _check_error() + return result if result != _ffi.NULL else None + + +def inter_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'STBox *': + box1_converted = _ffi.cast('const STBox *', box1) + box2_converted = _ffi.cast('const STBox *', box2) + out_result = _ffi.new('STBox *') + result = _lib.inter_stbox_stbox(box1_converted, box2_converted, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def intersection_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'STBox *': + box1_converted = _ffi.cast('const STBox *', box1) + box2_converted = _ffi.cast('const STBox *', box2) + result = _lib.intersection_stbox_stbox(box1_converted, box2_converted) + _check_error() + return result if result != _ffi.NULL else None + + def contains_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'bool': box1_converted = _ffi.cast('const TBox *', box1) box2_converted = _ffi.cast('const TBox *', box2) @@ -4456,85 +4524,31 @@ def overback_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool' def before_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': box1_converted = _ffi.cast('const STBox *', box1) box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.before_stbox_stbox(box1_converted, box2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overbefore_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': - box1_converted = _ffi.cast('const STBox *', box1) - box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.overbefore_stbox_stbox(box1_converted, box2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def after_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': - box1_converted = _ffi.cast('const STBox *', box1) - box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.after_stbox_stbox(box1_converted, box2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overafter_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': - box1_converted = _ffi.cast('const STBox *', box1) - box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.overafter_stbox_stbox(box1_converted, box2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def union_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *', strict: bool) -> 'TBox *': - box1_converted = _ffi.cast('const TBox *', box1) - box2_converted = _ffi.cast('const TBox *', box2) - result = _lib.union_tbox_tbox(box1_converted, box2_converted, strict) - _check_error() - return result if result != _ffi.NULL else None - - -def inter_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'TBox *': - box1_converted = _ffi.cast('const TBox *', box1) - box2_converted = _ffi.cast('const TBox *', box2) - out_result = _ffi.new('TBox *') - result = _lib.inter_tbox_tbox(box1_converted, box2_converted, out_result) - _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None - - -def intersection_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'TBox *': - box1_converted = _ffi.cast('const TBox *', box1) - box2_converted = _ffi.cast('const TBox *', box2) - result = _lib.intersection_tbox_tbox(box1_converted, box2_converted) + result = _lib.before_stbox_stbox(box1_converted, box2_converted) _check_error() return result if result != _ffi.NULL else None -def union_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *', strict: bool) -> 'STBox *': +def overbefore_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': box1_converted = _ffi.cast('const STBox *', box1) box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.union_stbox_stbox(box1_converted, box2_converted, strict) + result = _lib.overbefore_stbox_stbox(box1_converted, box2_converted) _check_error() return result if result != _ffi.NULL else None -def inter_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'STBox *': +def after_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': box1_converted = _ffi.cast('const STBox *', box1) box2_converted = _ffi.cast('const STBox *', box2) - out_result = _ffi.new('STBox *') - result = _lib.inter_stbox_stbox(box1_converted, box2_converted, out_result) + result = _lib.after_stbox_stbox(box1_converted, box2_converted) _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def intersection_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'STBox *': +def overafter_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': box1_converted = _ffi.cast('const STBox *', box1) box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.intersection_stbox_stbox(box1_converted, box2_converted) + result = _lib.overafter_stbox_stbox(box1_converted, box2_converted) _check_error() return result if result != _ffi.NULL else None @@ -5395,6 +5409,14 @@ def ttext_values(temp: 'const Temporal *') -> "Tuple['text **', 'int']": return result if result != _ffi.NULL else None, count[0] +def temporal_scale_time(temp: 'const Temporal *', duration: 'const Interval *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + duration_converted = _ffi.cast('const Interval *', duration) + result = _lib.temporal_scale_time(temp_converted, duration_converted) + _check_error() + return result if result != _ffi.NULL else None + + def temporal_set_interp(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) interp_converted = _ffi.cast('interpType', interp) @@ -5403,6 +5425,46 @@ def temporal_set_interp(temp: 'const Temporal *', interp: 'interpType') -> 'Temp return result if result != _ffi.NULL else None +def temporal_shift_scale_time(temp: 'const Temporal *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL + duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL + result = _lib.temporal_shift_scale_time(temp_converted, shift_converted, duration_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_shift_time(temp: 'const Temporal *', shift: 'const Interval *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + shift_converted = _ffi.cast('const Interval *', shift) + result = _lib.temporal_shift_time(temp_converted, shift_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_to_tinstant(temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.temporal_to_tinstant(temp_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_to_tsequence(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + interp_converted = _ffi.cast('interpType', interp) + result = _lib.temporal_to_tsequence(temp_converted, interp_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_to_tsequenceset(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + interp_converted = _ffi.cast('interpType', interp) + result = _lib.temporal_to_tsequenceset(temp_converted, interp_converted) + _check_error() + return result if result != _ffi.NULL else None + + def tfloat_scale_value(temp: 'const Temporal *', width: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) result = _lib.tfloat_scale_value(temp_converted, width) @@ -5445,68 +5507,82 @@ def tint_shift_value(temp: 'const Temporal *', shift: int) -> 'Temporal *': return result if result != _ffi.NULL else None -def temporal_scale_time(temp: 'const Temporal *', duration: 'const Interval *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - duration_converted = _ffi.cast('const Interval *', duration) - result = _lib.temporal_scale_time(temp_converted, duration_converted) +def temporal_append_tinstant(temp: 'Temporal *', inst: 'const TInstant *', maxdist: float, maxt: "Optional['Interval *']", expand: bool) -> 'Temporal *': + temp_converted = _ffi.cast('Temporal *', temp) + inst_converted = _ffi.cast('const TInstant *', inst) + maxt_converted = _ffi.cast('Interval *', maxt) if maxt is not None else _ffi.NULL + result = _lib.temporal_append_tinstant(temp_converted, inst_converted, maxdist, maxt_converted, expand) _check_error() return result if result != _ffi.NULL else None -def temporal_shift_scale_time(temp: 'const Temporal *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL - duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL - result = _lib.temporal_shift_scale_time(temp_converted, shift_converted, duration_converted) +def temporal_append_tsequence(temp: 'Temporal *', seq: 'const TSequence *', expand: bool) -> 'Temporal *': + temp_converted = _ffi.cast('Temporal *', temp) + seq_converted = _ffi.cast('const TSequence *', seq) + result = _lib.temporal_append_tsequence(temp_converted, seq_converted, expand) _check_error() return result if result != _ffi.NULL else None -def temporal_shift_time(temp: 'const Temporal *', shift: 'const Interval *') -> 'Temporal *': +def temporal_delete_period(temp: 'const Temporal *', p: 'const Span *', connect: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - shift_converted = _ffi.cast('const Interval *', shift) - result = _lib.temporal_shift_time(temp_converted, shift_converted) + p_converted = _ffi.cast('const Span *', p) + result = _lib.temporal_delete_period(temp_converted, p_converted, connect) _check_error() return result if result != _ffi.NULL else None -def temporal_to_tinstant(temp: 'const Temporal *') -> 'Temporal *': +def temporal_delete_periodset(temp: 'const Temporal *', ps: 'const SpanSet *', connect: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.temporal_to_tinstant(temp_converted) + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.temporal_delete_periodset(temp_converted, ps_converted, connect) _check_error() return result if result != _ffi.NULL else None -def temporal_to_tsequence(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': +def temporal_delete_timestamp(temp: 'const Temporal *', t: int, connect: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - interp_converted = _ffi.cast('interpType', interp) - result = _lib.temporal_to_tsequence(temp_converted, interp_converted) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.temporal_delete_timestamp(temp_converted, t_converted, connect) _check_error() return result if result != _ffi.NULL else None -def temporal_to_tsequenceset(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': +def temporal_delete_timestampset(temp: 'const Temporal *', ts: 'const Set *', connect: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - interp_converted = _ffi.cast('interpType', interp) - result = _lib.temporal_to_tsequenceset(temp_converted, interp_converted) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.temporal_delete_timestampset(temp_converted, ts_converted, connect) _check_error() return result if result != _ffi.NULL else None -def temporal_tprecision(temp: 'const Temporal *', duration: 'const Interval *', origin: int) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - duration_converted = _ffi.cast('const Interval *', duration) - origin_converted = _ffi.cast('TimestampTz', origin) - result = _lib.temporal_tprecision(temp_converted, duration_converted, origin_converted) +def temporal_insert(temp1: 'const Temporal *', temp2: 'const Temporal *', connect: bool) -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_insert(temp1_converted, temp2_converted, connect) _check_error() return result if result != _ffi.NULL else None -def temporal_tsample(temp: 'const Temporal *', duration: 'const Interval *', origin: int) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - duration_converted = _ffi.cast('const Interval *', duration) - origin_converted = _ffi.cast('TimestampTz', origin) - result = _lib.temporal_tsample(temp_converted, duration_converted, origin_converted) +def temporal_merge(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_merge(temp1_converted, temp2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_merge_array(temparr: 'Temporal **', count: int) -> 'Temporal *': + temparr_converted = [_ffi.cast('Temporal *', x) for x in temparr] + result = _lib.temporal_merge_array(temparr_converted, count) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_update(temp1: 'const Temporal *', temp2: 'const Temporal *', connect: bool) -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_update(temp1_converted, temp2_converted, connect) _check_error() return result if result != _ffi.NULL else None @@ -5832,1131 +5908,1051 @@ def ttext_value_at_timestamp(temp: 'const Temporal *', t: int, strict: bool) -> return None -def temporal_append_tinstant(temp: 'Temporal *', inst: 'const TInstant *', maxdist: float, maxt: "Optional['Interval *']", expand: bool) -> 'Temporal *': - temp_converted = _ffi.cast('Temporal *', temp) - inst_converted = _ffi.cast('const TInstant *', inst) - maxt_converted = _ffi.cast('Interval *', maxt) if maxt is not None else _ffi.NULL - result = _lib.temporal_append_tinstant(temp_converted, inst_converted, maxdist, maxt_converted, expand) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_append_tsequence(temp: 'Temporal *', seq: 'const TSequence *', expand: bool) -> 'Temporal *': - temp_converted = _ffi.cast('Temporal *', temp) - seq_converted = _ffi.cast('const TSequence *', seq) - result = _lib.temporal_append_tsequence(temp_converted, seq_converted, expand) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_delete_period(temp: 'const Temporal *', p: 'const Span *', connect: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - p_converted = _ffi.cast('const Span *', p) - result = _lib.temporal_delete_period(temp_converted, p_converted, connect) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_delete_periodset(temp: 'const Temporal *', ps: 'const SpanSet *', connect: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.temporal_delete_periodset(temp_converted, ps_converted, connect) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_delete_timestamp(temp: 'const Temporal *', t: int, connect: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.temporal_delete_timestamp(temp_converted, t_converted, connect) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_delete_timestampset(temp: 'const Temporal *', ts: 'const Set *', connect: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.temporal_delete_timestampset(temp_converted, ts_converted, connect) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_insert(temp1: 'const Temporal *', temp2: 'const Temporal *', connect: bool) -> 'Temporal *': +def temporal_cmp(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'int': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_insert(temp1_converted, temp2_converted, connect) + result = _lib.temporal_cmp(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_merge(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def temporal_eq(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_merge(temp1_converted, temp2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_merge_array(temparr: 'Temporal **', count: int) -> 'Temporal *': - temparr_converted = [_ffi.cast('Temporal *', x) for x in temparr] - result = _lib.temporal_merge_array(temparr_converted, count) + result = _lib.temporal_eq(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_update(temp1: 'const Temporal *', temp2: 'const Temporal *', connect: bool) -> 'Temporal *': +def temporal_ge(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_update(temp1_converted, temp2_converted, connect) - _check_error() - return result if result != _ffi.NULL else None - - -def tand_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tand_bool_tbool(b, temp_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def tand_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tand_tbool_bool(temp_converted, b) + result = _lib.temporal_ge(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tand_tbool_tbool(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def temporal_gt(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tand_tbool_tbool(temp1_converted, temp2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def tbool_when_true(temp: 'const Temporal *') -> 'SpanSet *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tbool_when_true(temp_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def tnot_tbool(temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tnot_tbool(temp_converted) + result = _lib.temporal_gt(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tor_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tor_bool_tbool(b, temp_converted) +def temporal_le(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_le(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tor_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tor_tbool_bool(temp_converted, b) +def temporal_lt(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_lt(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tor_tbool_tbool(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def temporal_ne(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tor_tbool_tbool(temp1_converted, temp2_converted) + result = _lib.temporal_ne(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def add_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_float_tfloat(d, tnumber_converted) +def tbool_always_eq(temp: 'const Temporal *', b: bool) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tbool_always_eq(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def add_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_int_tint(i, tnumber_converted) +def tbool_ever_eq(temp: 'const Temporal *', b: bool) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tbool_ever_eq(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def add_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_tfloat_float(tnumber_converted, d) +def tfloat_always_eq(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_always_eq(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def add_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_tint_int(tnumber_converted, i) +def tfloat_always_le(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_always_le(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def add_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': - tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) - tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) - result = _lib.add_tnumber_tnumber(tnumber1_converted, tnumber2_converted) +def tfloat_always_lt(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_always_lt(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def div_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_float_tfloat(d, tnumber_converted) +def tfloat_ever_eq(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_ever_eq(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def div_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_int_tint(i, tnumber_converted) +def tfloat_ever_le(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_ever_le(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def div_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_tfloat_float(tnumber_converted, d) +def tfloat_ever_lt(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_ever_lt(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def div_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_tint_int(tnumber_converted, i) +def tint_always_eq(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_always_eq(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def div_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': - tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) - tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) - result = _lib.div_tnumber_tnumber(tnumber1_converted, tnumber2_converted) +def tint_always_le(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_always_le(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def float_degrees(value: float, normalize: bool) -> 'double': - result = _lib.float_degrees(value, normalize) +def tint_always_lt(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_always_lt(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def mult_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_float_tfloat(d, tnumber_converted) +def tint_ever_eq(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_ever_eq(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def mult_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_int_tint(i, tnumber_converted) +def tint_ever_le(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_ever_le(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def mult_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_tfloat_float(tnumber_converted, d) +def tint_ever_lt(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_ever_lt(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def mult_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_tint_int(tnumber_converted, i) +def tpoint_always_eq(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.tpoint_always_eq(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def mult_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': - tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) - tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) - result = _lib.mult_tnumber_tnumber(tnumber1_converted, tnumber2_converted) +def tpoint_ever_eq(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.tpoint_ever_eq(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def sub_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_float_tfloat(d, tnumber_converted) +def ttext_always_eq(temp: 'const Temporal *', txt: str) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.ttext_always_eq(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def sub_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_int_tint(i, tnumber_converted) +def ttext_always_le(temp: 'const Temporal *', txt: str) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.ttext_always_le(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def sub_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_tfloat_float(tnumber_converted, d) +def ttext_always_lt(temp: 'const Temporal *', txt: str) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.ttext_always_lt(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def sub_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_tint_int(tnumber_converted, i) +def ttext_ever_eq(temp: 'const Temporal *', txt: str) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.ttext_ever_eq(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def sub_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': - tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) - tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) - result = _lib.sub_tnumber_tnumber(tnumber1_converted, tnumber2_converted) +def ttext_ever_le(temp: 'const Temporal *', txt: str) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.ttext_ever_le(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_round(temp: 'const Temporal *', maxdd: int) -> 'Temporal *': +def ttext_ever_lt(temp: 'const Temporal *', txt: str) -> 'bool': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_round(temp_converted, maxdd) + txt_converted = cstring2text(txt) + result = _lib.ttext_ever_lt(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_degrees(temp: 'const Temporal *', normalize: bool) -> 'Temporal *': +def teq_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_degrees(temp_converted, normalize) + result = _lib.teq_bool_tbool(b, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_derivative(temp: 'const Temporal *') -> 'Temporal *': +def teq_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_derivative(temp_converted) + result = _lib.teq_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_radians(temp: 'const Temporal *') -> 'Temporal *': +def teq_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_radians(temp_converted) + result = _lib.teq_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tnumber_abs(temp: 'const Temporal *') -> 'Temporal *': +def teq_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tnumber_abs(temp_converted) + result = _lib.teq_point_tpoint(gs_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tnumber_angular_difference(temp: 'const Temporal *') -> 'Temporal *': +def teq_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tnumber_angular_difference(temp_converted) + result = _lib.teq_tbool_bool(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def tnumber_delta_value(temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tnumber_delta_value(temp_converted) +def teq_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.teq_temporal_temporal(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def textcat_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': +def teq_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.textcat_text_ttext(txt_converted, temp_converted) + result = _lib.teq_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def textcat_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': +def teq_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.textcat_ttext_text(temp_converted, txt_converted) + result = _lib.teq_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def textcat_ttext_ttext(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.textcat_ttext_ttext(temp1_converted, temp2_converted) +def teq_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.teq_tpoint_point(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def ttext_upper(temp: 'const Temporal *') -> 'Temporal *': +def teq_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.ttext_upper(temp_converted) + result = _lib.teq_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def ttext_lower(temp: 'const Temporal *') -> 'Temporal *': +def teq_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.ttext_lower(temp_converted) + txt_converted = cstring2text(txt) + result = _lib.teq_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def distance_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': +def tge_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.distance_tfloat_float(temp_converted, d) + result = _lib.tge_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def distance_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': +def tge_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.distance_tint_int(temp_converted, i) + result = _lib.tge_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def distance_tnumber_tnumber(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def tge_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.distance_tnumber_tnumber(temp1_converted, temp2_converted) + result = _lib.tge_temporal_temporal(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': +def tge_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.distance_tpoint_point(temp_converted, gs_converted) + result = _lib.tge_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def distance_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.distance_tpoint_tpoint(temp1_converted, temp2_converted) +def tge_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tge_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def nad_stbox_geo(box: 'const STBox *', gs: 'const GSERIALIZED *') -> 'double': - box_converted = _ffi.cast('const STBox *', box) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.nad_stbox_geo(box_converted, gs_converted) +def tge_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tge_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def nad_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'double': - box1_converted = _ffi.cast('const STBox *', box1) - box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.nad_stbox_stbox(box1_converted, box2_converted) +def tge_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.tge_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'double': - box1_converted = _ffi.cast('const TBox *', box1) - box2_converted = _ffi.cast('const TBox *', box2) - result = _lib.nad_tbox_tbox(box1_converted, box2_converted) +def tgt_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tgt_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tfloat_float(temp: 'const Temporal *', d: float) -> 'double': +def tgt_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.nad_tfloat_float(temp_converted, d) + result = _lib.tgt_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tfloat_tfloat(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': +def tgt_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.nad_tfloat_tfloat(temp1_converted, temp2_converted) + result = _lib.tgt_temporal_temporal(temp1_converted, temp2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tgt_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tgt_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tint_int(temp: 'const Temporal *', i: int) -> 'int': +def tgt_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.nad_tint_int(temp_converted, i) + result = _lib.tgt_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def nad_tint_tint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'int': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.nad_tint_tint(temp1_converted, temp2_converted) +def tgt_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tgt_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def nad_tnumber_tbox(temp: 'const Temporal *', box: 'const TBox *') -> 'double': +def tgt_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - box_converted = _ffi.cast('const TBox *', box) - result = _lib.nad_tnumber_tbox(temp_converted, box_converted) + txt_converted = cstring2text(txt) + result = _lib.tgt_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'double': +def tle_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.nad_tpoint_geo(temp_converted, gs_converted) + result = _lib.tle_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tpoint_stbox(temp: 'const Temporal *', box: 'const STBox *') -> 'double': +def tle_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - box_converted = _ffi.cast('const STBox *', box) - result = _lib.nad_tpoint_stbox(temp_converted, box_converted) + result = _lib.tle_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': +def tle_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.nad_tpoint_tpoint(temp1_converted, temp2_converted) + result = _lib.tle_temporal_temporal(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def nai_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'TInstant *': +def tle_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.nai_tpoint_geo(temp_converted, gs_converted) + result = _lib.tle_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nai_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'TInstant *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.nai_tpoint_tpoint(temp1_converted, temp2_converted) +def tle_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tle_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def shortestline_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'GSERIALIZED **': +def tle_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - out_result = _ffi.new('GSERIALIZED **') - result = _lib.shortestline_tpoint_geo(temp_converted, gs_converted, out_result) + result = _lib.tle_tint_int(temp_converted, i) _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def shortestline_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'GSERIALIZED **': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - out_result = _ffi.new('GSERIALIZED **') - result = _lib.shortestline_tpoint_tpoint(temp1_converted, temp2_converted, out_result) +def tle_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.tle_ttext_text(temp_converted, txt_converted) _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbool_always_eq(temp: 'const Temporal *', b: bool) -> 'bool': +def tlt_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tbool_always_eq(temp_converted, b) + result = _lib.tlt_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tbool_ever_eq(temp: 'const Temporal *', b: bool) -> 'bool': +def tlt_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tbool_ever_eq(temp_converted, b) + result = _lib.tlt_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_always_eq(temp: 'const Temporal *', d: float) -> 'bool': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_always_eq(temp_converted, d) +def tlt_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.tlt_temporal_temporal(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_always_le(temp: 'const Temporal *', d: float) -> 'bool': +def tlt_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_always_le(temp_converted, d) + result = _lib.tlt_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_always_lt(temp: 'const Temporal *', d: float) -> 'bool': +def tlt_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_always_lt(temp_converted, d) + result = _lib.tlt_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def tfloat_ever_eq(temp: 'const Temporal *', d: float) -> 'bool': +def tlt_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_ever_eq(temp_converted, d) + result = _lib.tlt_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def tfloat_ever_le(temp: 'const Temporal *', d: float) -> 'bool': +def tlt_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_ever_le(temp_converted, d) + txt_converted = cstring2text(txt) + result = _lib.tlt_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_ever_lt(temp: 'const Temporal *', d: float) -> 'bool': +def tne_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_ever_lt(temp_converted, d) + result = _lib.tne_bool_tbool(b, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tint_always_eq(temp: 'const Temporal *', i: int) -> 'bool': +def tne_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_always_eq(temp_converted, i) + result = _lib.tne_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tint_always_le(temp: 'const Temporal *', i: int) -> 'bool': +def tne_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_always_le(temp_converted, i) + result = _lib.tne_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tint_always_lt(temp: 'const Temporal *', i: int) -> 'bool': +def tne_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_always_lt(temp_converted, i) + result = _lib.tne_point_tpoint(gs_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tint_ever_eq(temp: 'const Temporal *', i: int) -> 'bool': +def tne_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_ever_eq(temp_converted, i) + result = _lib.tne_tbool_bool(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def tint_ever_le(temp: 'const Temporal *', i: int) -> 'bool': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_ever_le(temp_converted, i) +def tne_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.tne_temporal_temporal(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tint_ever_lt(temp: 'const Temporal *', i: int) -> 'bool': +def tne_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_ever_lt(temp_converted, i) + result = _lib.tne_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tpoint_always_eq(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'bool': +def tne_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.tpoint_always_eq(temp_converted, gs_converted) + result = _lib.tne_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def tpoint_ever_eq(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'bool': +def tne_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.tpoint_ever_eq(temp_converted, gs_converted) + result = _lib.tne_tpoint_point(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def ttext_always_eq(temp: 'const Temporal *', txt: str) -> 'bool': +def tne_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.ttext_always_eq(temp_converted, txt_converted) + result = _lib.tne_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def ttext_always_le(temp: 'const Temporal *', txt: str) -> 'bool': +def tne_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) txt_converted = cstring2text(txt) - result = _lib.ttext_always_le(temp_converted, txt_converted) + result = _lib.tne_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def ttext_always_lt(temp: 'const Temporal *', txt: str) -> 'bool': +def tand_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.ttext_always_lt(temp_converted, txt_converted) + result = _lib.tand_bool_tbool(b, temp_converted) _check_error() return result if result != _ffi.NULL else None -def ttext_ever_eq(temp: 'const Temporal *', txt: str) -> 'bool': +def tand_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.ttext_ever_eq(temp_converted, txt_converted) + result = _lib.tand_tbool_bool(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def ttext_ever_le(temp: 'const Temporal *', txt: str) -> 'bool': - temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.ttext_ever_le(temp_converted, txt_converted) +def tand_tbool_tbool(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.tand_tbool_tbool(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def ttext_ever_lt(temp: 'const Temporal *', txt: str) -> 'bool': +def tbool_when_true(temp: 'const Temporal *') -> 'SpanSet *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.ttext_ever_lt(temp_converted, txt_converted) + result = _lib.tbool_when_true(temp_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_cmp(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'int': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_cmp(temp1_converted, temp2_converted) +def tnot_tbool(temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tnot_tbool(temp_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_eq(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_eq(temp1_converted, temp2_converted) +def tor_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tor_bool_tbool(b, temp_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_ge(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_ge(temp1_converted, temp2_converted) +def tor_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tor_tbool_bool(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def temporal_gt(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': +def tor_tbool_tbool(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_gt(temp1_converted, temp2_converted) + result = _lib.tor_tbool_tbool(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_le(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_le(temp1_converted, temp2_converted) +def add_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.add_float_tfloat(d, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_lt(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_lt(temp1_converted, temp2_converted) +def add_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.add_int_tint(i, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_ne(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_ne(temp1_converted, temp2_converted) +def add_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.add_tfloat_float(tnumber_converted, d) _check_error() return result if result != _ffi.NULL else None -def teq_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_bool_tbool(b, temp_converted) +def add_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.add_tint_int(tnumber_converted, i) _check_error() return result if result != _ffi.NULL else None -def teq_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_float_tfloat(d, temp_converted) +def add_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': + tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) + tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) + result = _lib.add_tnumber_tnumber(tnumber1_converted, tnumber2_converted) _check_error() return result if result != _ffi.NULL else None -def teq_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_int_tint(i, temp_converted) +def div_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.div_float_tfloat(d, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def teq_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_point_tpoint(gs_converted, temp_converted) +def div_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.div_int_tint(i, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def teq_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_tbool_bool(temp_converted, b) +def div_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.div_tfloat_float(tnumber_converted, d) _check_error() return result if result != _ffi.NULL else None -def teq_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.teq_temporal_temporal(temp1_converted, temp2_converted) +def div_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.div_tint_int(tnumber_converted, i) _check_error() return result if result != _ffi.NULL else None -def teq_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_text_ttext(txt_converted, temp_converted) +def div_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': + tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) + tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) + result = _lib.div_tnumber_tnumber(tnumber1_converted, tnumber2_converted) _check_error() return result if result != _ffi.NULL else None -def teq_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_tfloat_float(temp_converted, d) +def float_degrees(value: float, normalize: bool) -> 'double': + result = _lib.float_degrees(value, normalize) _check_error() return result if result != _ffi.NULL else None -def teq_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.teq_tpoint_point(temp_converted, gs_converted) +def mult_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.mult_float_tfloat(d, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def teq_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_tint_int(temp_converted, i) +def mult_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.mult_int_tint(i, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def teq_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.teq_ttext_text(temp_converted, txt_converted) +def mult_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.mult_tfloat_float(tnumber_converted, d) _check_error() return result if result != _ffi.NULL else None -def tge_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tge_float_tfloat(d, temp_converted) +def mult_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.mult_tint_int(tnumber_converted, i) _check_error() return result if result != _ffi.NULL else None -def tge_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tge_int_tint(i, temp_converted) +def mult_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': + tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) + tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) + result = _lib.mult_tnumber_tnumber(tnumber1_converted, tnumber2_converted) _check_error() return result if result != _ffi.NULL else None -def tge_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tge_temporal_temporal(temp1_converted, temp2_converted) +def sub_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.sub_float_tfloat(d, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def tge_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tge_text_ttext(txt_converted, temp_converted) +def sub_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.sub_int_tint(i, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def tge_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tge_tfloat_float(temp_converted, d) +def sub_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.sub_tfloat_float(tnumber_converted, d) _check_error() return result if result != _ffi.NULL else None -def tge_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tge_tint_int(temp_converted, i) +def sub_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.sub_tint_int(tnumber_converted, i) _check_error() return result if result != _ffi.NULL else None -def tge_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.tge_ttext_text(temp_converted, txt_converted) +def sub_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': + tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) + tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) + result = _lib.sub_tnumber_tnumber(tnumber1_converted, tnumber2_converted) _check_error() return result if result != _ffi.NULL else None -def tgt_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': +def tfloat_round(temp: 'const Temporal *', maxdd: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgt_float_tfloat(d, temp_converted) + result = _lib.tfloat_round(temp_converted, maxdd) _check_error() return result if result != _ffi.NULL else None -def tgt_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': +def tfloat_degrees(temp: 'const Temporal *', normalize: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgt_int_tint(i, temp_converted) + result = _lib.tfloat_degrees(temp_converted, normalize) _check_error() return result if result != _ffi.NULL else None -def tgt_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tgt_temporal_temporal(temp1_converted, temp2_converted) +def tfloat_derivative(temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_derivative(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tgt_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) +def tfloat_radians(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgt_text_ttext(txt_converted, temp_converted) + result = _lib.tfloat_radians(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tgt_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': +def tnumber_abs(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgt_tfloat_float(temp_converted, d) + result = _lib.tnumber_abs(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tgt_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': +def tnumber_angular_difference(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgt_tint_int(temp_converted, i) + result = _lib.tnumber_angular_difference(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tgt_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': +def tnumber_delta_value(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.tgt_ttext_text(temp_converted, txt_converted) + result = _lib.tnumber_delta_value(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tle_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': +def textcat_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tle_float_tfloat(d, temp_converted) + result = _lib.textcat_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tle_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': +def textcat_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tle_int_tint(i, temp_converted) + txt_converted = cstring2text(txt) + result = _lib.textcat_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def tle_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def textcat_ttext_ttext(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tle_temporal_temporal(temp1_converted, temp2_converted) + result = _lib.textcat_ttext_ttext(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tle_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) +def ttext_upper(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tle_text_ttext(txt_converted, temp_converted) + result = _lib.ttext_upper(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tle_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': +def ttext_lower(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tle_tfloat_float(temp_converted, d) + result = _lib.ttext_lower(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tle_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': +def distance_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tle_tint_int(temp_converted, i) + result = _lib.distance_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def tle_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': +def distance_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.tle_ttext_text(temp_converted, txt_converted) + result = _lib.distance_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def tlt_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tlt_float_tfloat(d, temp_converted) +def distance_tnumber_tnumber(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.distance_tnumber_tnumber(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': +def distance_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tlt_int_tint(i, temp_converted) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.distance_tpoint_point(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def distance_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tlt_temporal_temporal(temp1_converted, temp2_converted) + result = _lib.distance_tpoint_tpoint(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tlt_text_ttext(txt_converted, temp_converted) +def nad_stbox_geo(box: 'const STBox *', gs: 'const GSERIALIZED *') -> 'double': + box_converted = _ffi.cast('const STBox *', box) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.nad_stbox_geo(box_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tlt_tfloat_float(temp_converted, d) +def nad_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'double': + box1_converted = _ffi.cast('const STBox *', box1) + box2_converted = _ffi.cast('const STBox *', box2) + result = _lib.nad_stbox_stbox(box1_converted, box2_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tlt_tint_int(temp_converted, i) +def nad_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'double': + box1_converted = _ffi.cast('const TBox *', box1) + box2_converted = _ffi.cast('const TBox *', box2) + result = _lib.nad_tbox_tbox(box1_converted, box2_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': +def nad_tfloat_float(temp: 'const Temporal *', d: float) -> 'double': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.tlt_ttext_text(temp_converted, txt_converted) + result = _lib.nad_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def tne_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_bool_tbool(b, temp_converted) +def nad_tfloat_tfloat(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.nad_tfloat_tfloat(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tne_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': +def nad_tint_int(temp: 'const Temporal *', i: int) -> 'int': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_float_tfloat(d, temp_converted) + result = _lib.nad_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def tne_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_int_tint(i, temp_converted) +def nad_tint_tint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'int': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.nad_tint_tint(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tne_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) +def nad_tnumber_tbox(temp: 'const Temporal *', box: 'const TBox *') -> 'double': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_point_tpoint(gs_converted, temp_converted) + box_converted = _ffi.cast('const TBox *', box) + result = _lib.nad_tnumber_tbox(temp_converted, box_converted) _check_error() return result if result != _ffi.NULL else None -def tne_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': +def nad_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'double': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_tbool_bool(temp_converted, b) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.nad_tpoint_geo(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def tne_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tne_temporal_temporal(temp1_converted, temp2_converted) +def nad_tpoint_stbox(temp: 'const Temporal *', box: 'const STBox *') -> 'double': + temp_converted = _ffi.cast('const Temporal *', temp) + box_converted = _ffi.cast('const STBox *', box) + result = _lib.nad_tpoint_stbox(temp_converted, box_converted) _check_error() return result if result != _ffi.NULL else None -def tne_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_text_ttext(txt_converted, temp_converted) +def nad_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.nad_tpoint_tpoint(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tne_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': +def nai_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'TInstant *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_tfloat_float(temp_converted, d) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.nai_tpoint_geo(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def tne_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.tne_tpoint_point(temp_converted, gs_converted) +def nai_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'TInstant *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.nai_tpoint_tpoint(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tne_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': +def shortestline_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'GSERIALIZED **': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_tint_int(temp_converted, i) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.shortestline_tpoint_geo(temp_converted, gs_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result if out_result != _ffi.NULL else None + return None -def tne_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.tne_ttext_text(temp_converted, txt_converted) +def shortestline_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'GSERIALIZED **': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.shortestline_tpoint_tpoint(temp1_converted, temp2_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result if out_result != _ffi.NULL else None + return None def bearing_point_point(gs1: 'const GSERIALIZED *', gs2: 'const GSERIALIZED *') -> 'double': @@ -7095,9 +7091,16 @@ def geo_expand_space(gs: 'const GSERIALIZED *', d: float) -> 'STBox *': return result if result != _ffi.NULL else None -def tpoint_expand_space(temp: 'const Temporal *', d: float) -> 'STBox *': +def geo_to_tpoint(gs: 'const GSERIALIZED *') -> 'Temporal *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.geo_to_tpoint(gs_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tgeogpoint_to_tgeompoint(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tpoint_expand_space(temp_converted, d) + result = _lib.tgeogpoint_to_tgeompoint(temp_converted) _check_error() return result if result != _ffi.NULL else None @@ -7109,16 +7112,22 @@ def tgeompoint_to_tgeogpoint(temp: 'const Temporal *') -> 'Temporal *': return result if result != _ffi.NULL else None -def tgeogpoint_to_tgeompoint(temp: 'const Temporal *') -> 'Temporal *': +def tpoint_AsMVTGeom(temp: 'const Temporal *', bounds: 'const STBox *', extent: 'int32_t', buffer: 'int32_t', clip_geom: bool, gsarr: 'GSERIALIZED **', timesarr: 'int64 **') -> "Tuple['bool', 'int']": temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgeogpoint_to_tgeompoint(temp_converted) + bounds_converted = _ffi.cast('const STBox *', bounds) + extent_converted = _ffi.cast('int32_t', extent) + buffer_converted = _ffi.cast('int32_t', buffer) + gsarr_converted = [_ffi.cast('GSERIALIZED *', x) for x in gsarr] + timesarr_converted = [_ffi.cast('int64 *', x) for x in timesarr] + count = _ffi.new('int *') + result = _lib.tpoint_AsMVTGeom(temp_converted, bounds_converted, extent_converted, buffer_converted, clip_geom, gsarr_converted, timesarr_converted, count) _check_error() - return result if result != _ffi.NULL else None + return result if result != _ffi.NULL else None, count[0] -def tpoint_round(temp: 'const Temporal *', maxdd: int) -> 'Temporal *': +def tpoint_expand_space(temp: 'const Temporal *', d: float) -> 'STBox *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tpoint_round(temp_converted, maxdd) + result = _lib.tpoint_expand_space(temp_converted, d) _check_error() return result if result != _ffi.NULL else None @@ -7131,6 +7140,13 @@ def tpoint_make_simple(temp: 'const Temporal *') -> "Tuple['Temporal **', 'int'] return result if result != _ffi.NULL else None, count[0] +def tpoint_round(temp: 'const Temporal *', maxdd: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tpoint_round(temp_converted, maxdd) + _check_error() + return result if result != _ffi.NULL else None + + def tpoint_set_srid(temp: 'const Temporal *', srid: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) srid_converted = _ffi.cast('int32', srid) @@ -7139,6 +7155,17 @@ def tpoint_set_srid(temp: 'const Temporal *', srid: int) -> 'Temporal *': return result if result != _ffi.NULL else None +def tpoint_to_geo_meas(tpoint: 'const Temporal *', measure: 'const Temporal *', segmentize: bool) -> 'GSERIALIZED **': + tpoint_converted = _ffi.cast('const Temporal *', tpoint) + measure_converted = _ffi.cast('const Temporal *', measure) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.tpoint_to_geo_meas(tpoint_converted, measure_converted, segmentize, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + def econtains_geo_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'int': gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) @@ -7421,6 +7448,95 @@ def ttext_tmin_transfn(state: "Optional['SkipList *']", temp: 'const Temporal *' return result if result != _ffi.NULL else None +def temporal_simplify_min_dist(temp: 'const Temporal *', dist: float) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.temporal_simplify_min_dist(temp_converted, dist) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_simplify_min_tdelta(temp: 'const Temporal *', mint: 'const Interval *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + mint_converted = _ffi.cast('const Interval *', mint) + result = _lib.temporal_simplify_min_tdelta(temp_converted, mint_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_simplify_dp(temp: 'const Temporal *', eps_dist: float, synchronized: bool) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.temporal_simplify_dp(temp_converted, eps_dist, synchronized) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_simplify_max_dist(temp: 'const Temporal *', eps_dist: float, synchronized: bool) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.temporal_simplify_max_dist(temp_converted, eps_dist, synchronized) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_tprecision(temp: 'const Temporal *', duration: 'const Interval *', origin: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + duration_converted = _ffi.cast('const Interval *', duration) + origin_converted = _ffi.cast('TimestampTz', origin) + result = _lib.temporal_tprecision(temp_converted, duration_converted, origin_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_tsample(temp: 'const Temporal *', duration: 'const Interval *', origin: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + duration_converted = _ffi.cast('const Interval *', duration) + origin_converted = _ffi.cast('TimestampTz', origin) + result = _lib.temporal_tsample(temp_converted, duration_converted, origin_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_dyntimewarp_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_dyntimewarp_distance(temp1_converted, temp2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_dyntimewarp_path(temp1: 'const Temporal *', temp2: 'const Temporal *') -> "Tuple['Match *', 'int']": + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + count = _ffi.new('int *') + result = _lib.temporal_dyntimewarp_path(temp1_converted, temp2_converted, count) + _check_error() + return result if result != _ffi.NULL else None, count[0] + + +def temporal_frechet_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_frechet_distance(temp1_converted, temp2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_frechet_path(temp1: 'const Temporal *', temp2: 'const Temporal *') -> "Tuple['Match *', 'int']": + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + count = _ffi.new('int *') + result = _lib.temporal_frechet_path(temp1_converted, temp2_converted, count) + _check_error() + return result if result != _ffi.NULL else None, count[0] + + +def temporal_hausdorff_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_hausdorff_distance(temp1_converted, temp2_converted) + _check_error() + return result if result != _ffi.NULL else None + + def float_bucket(value: float, size: float, origin: float) -> 'double': result = _lib.float_bucket(value, size, origin) _check_error() @@ -7537,105 +7653,3 @@ def tint_value_time_split(temp: 'Temporal *', size: int, vorigin: int, duration: return result if result != _ffi.NULL else None, newcount[0] -def temporal_dyntimewarp_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_dyntimewarp_distance(temp1_converted, temp2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_dyntimewarp_path(temp1: 'const Temporal *', temp2: 'const Temporal *') -> "Tuple['Match *', 'int']": - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - count = _ffi.new('int *') - result = _lib.temporal_dyntimewarp_path(temp1_converted, temp2_converted, count) - _check_error() - return result if result != _ffi.NULL else None, count[0] - - -def temporal_frechet_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_frechet_distance(temp1_converted, temp2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_frechet_path(temp1: 'const Temporal *', temp2: 'const Temporal *') -> "Tuple['Match *', 'int']": - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - count = _ffi.new('int *') - result = _lib.temporal_frechet_path(temp1_converted, temp2_converted, count) - _check_error() - return result if result != _ffi.NULL else None, count[0] - - -def temporal_hausdorff_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_hausdorff_distance(temp1_converted, temp2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def geo_to_tpoint(gs: 'const GSERIALIZED *') -> 'Temporal *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.geo_to_tpoint(gs_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_simplify_min_dist(temp: 'const Temporal *', dist: float) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.temporal_simplify_min_dist(temp_converted, dist) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_simplify_min_tdelta(temp: 'const Temporal *', mint: 'const Interval *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - mint_converted = _ffi.cast('const Interval *', mint) - result = _lib.temporal_simplify_min_tdelta(temp_converted, mint_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_simplify_dp(temp: 'const Temporal *', eps_dist: float, synchronized: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.temporal_simplify_dp(temp_converted, eps_dist, synchronized) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_simplify_max_dist(temp: 'const Temporal *', eps_dist: float, synchronized: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.temporal_simplify_max_dist(temp_converted, eps_dist, synchronized) - _check_error() - return result if result != _ffi.NULL else None - - -def tpoint_AsMVTGeom(temp: 'const Temporal *', bounds: 'const STBox *', extent: 'int32_t', buffer: 'int32_t', clip_geom: bool, gsarr: 'GSERIALIZED **', timesarr: 'int64 **') -> "Tuple['bool', 'int']": - temp_converted = _ffi.cast('const Temporal *', temp) - bounds_converted = _ffi.cast('const STBox *', bounds) - extent_converted = _ffi.cast('int32_t', extent) - buffer_converted = _ffi.cast('int32_t', buffer) - gsarr_converted = [_ffi.cast('GSERIALIZED *', x) for x in gsarr] - timesarr_converted = [_ffi.cast('int64 *', x) for x in timesarr] - count = _ffi.new('int *') - result = _lib.tpoint_AsMVTGeom(temp_converted, bounds_converted, extent_converted, buffer_converted, clip_geom, gsarr_converted, timesarr_converted, count) - _check_error() - return result if result != _ffi.NULL else None, count[0] - - -def tpoint_to_geo_meas(tpoint: 'const Temporal *', measure: 'const Temporal *', segmentize: bool) -> 'GSERIALIZED **': - tpoint_converted = _ffi.cast('const Temporal *', tpoint) - measure_converted = _ffi.cast('const Temporal *', measure) - out_result = _ffi.new('GSERIALIZED **') - result = _lib.tpoint_to_geo_meas(tpoint_converted, measure_converted, segmentize, out_result) - _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None - - From d00fe87230728e82dd91b7bf5a86d8874e5839ba Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 21 Sep 2023 12:13:16 +0200 Subject: [PATCH 029/101] Add before/after to tests --- pymeos/tests/time/period_test.py | 4 ++++ pymeos/tests/time/periodset_test.py | 4 +--- pymeos/tests/time/timestampset_test.py | 23 +++++++++++++---------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/pymeos/tests/time/period_test.py b/pymeos/tests/time/period_test.py index 0f2480eb..56bbaa7b 100644 --- a/pymeos/tests/time/period_test.py +++ b/pymeos/tests/time/period_test.py @@ -257,6 +257,7 @@ def test_is_same(self, other): 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_before(self, other): + self.period.is_before(other) self.period.is_left(other) @pytest.mark.parametrize( @@ -267,6 +268,7 @@ def test_is_before(self, other): 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_over_or_before(self, other): + self.period.is_over_or_before(other) self.period.is_over_or_left(other) @pytest.mark.parametrize( @@ -277,6 +279,7 @@ def test_is_over_or_before(self, other): 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_after(self, other): + self.period.is_after(other) self.period.is_right(other) @pytest.mark.parametrize( @@ -287,6 +290,7 @@ def test_is_after(self, other): 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_over_or_after(self, other): + self.period.is_over_or_after(other) self.period.is_over_or_right(other) @pytest.mark.parametrize( diff --git a/pymeos/tests/time/periodset_test.py b/pymeos/tests/time/periodset_test.py index 03417a28..98739944 100644 --- a/pymeos/tests/time/periodset_test.py +++ b/pymeos/tests/time/periodset_test.py @@ -59,6 +59,7 @@ def test_repr(self): def test_hexwkb(self): assert self.periodset.as_hexwkb() == '012200020000000300A01E4E713402000000F66B85340200030060CD899934020000C0A4A7AD340200' + class TestPeriodSetConversions(TestPeriodSet): def test_to_period(self): @@ -338,6 +339,3 @@ def test_gt(self): def test_ge(self): _ = self.periodset >= self.other - - - diff --git a/pymeos/tests/time/timestampset_test.py b/pymeos/tests/time/timestampset_test.py index 9bb176f0..489b751d 100644 --- a/pymeos/tests/time/timestampset_test.py +++ b/pymeos/tests/time/timestampset_test.py @@ -22,8 +22,8 @@ class TestTimestampSetConstructors(TestTimestampSet): def test_string_constructor(self): self.assert_timestampset_equality(self.ts_set, [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) + datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) def test_list_constructor(self): ts_set = TimestampSet(timestamp_list=[datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), @@ -56,7 +56,8 @@ def test_str(self): assert str(self.ts_set) == '{"2019-09-01 00:00:00+00", "2019-09-02 00:00:00+00", "2019-09-03 00:00:00+00"}' def test_repr(self): - assert repr(self.ts_set) == 'TimestampSet({"2019-09-01 00:00:00+00", "2019-09-02 00:00:00+00", "2019-09-03 00:00:00+00"})' + assert repr( + self.ts_set) == 'TimestampSet({"2019-09-01 00:00:00+00", "2019-09-02 00:00:00+00", "2019-09-03 00:00:00+00"})' def test_as_hexwkb(self): assert self.ts_set.as_hexwkb() == '012000010300000000A01E4E713402000000F66B853402000060CD8999340200' @@ -67,8 +68,8 @@ class TestTimestampConversions(TestTimestampSet): def test_to_periodset(self): assert self.ts_set.to_periodset() == PeriodSet( '{[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00], ' - '[2019-09-02 00:00:00+00, 2019-09-02 00:00:00+00], ' - '[2019-09-03 00:00:00+00, 2019-09-03 00:00:00+00]}') + '[2019-09-02 00:00:00+00, 2019-09-02 00:00:00+00], ' + '[2019-09-03 00:00:00+00, 2019-09-03 00:00:00+00]}') class TestTimestampSetAccessors(TestTimestampSet): @@ -97,9 +98,9 @@ def test_timestamp_n_out_of_range(self): def test_timestamps(self): assert self.ts_set.elements() == [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc), - ] + datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc), + ] def test_hash(self): assert hash(self.ts_set) == 527267058 @@ -132,9 +133,11 @@ def test_is_adjacent(self, other): @pytest.mark.parametrize( 'other', - [timestampset, period, periodset, instant, discrete_sequence, stepwise_sequence, continuous_sequence, sequence_set, tbox, + [timestampset, period, periodset, instant, discrete_sequence, stepwise_sequence, continuous_sequence, + sequence_set, tbox, stbox], - ids=['timestampset', 'period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', + ids=['timestampset', 'period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_contained_in(self, other): From e951b416eba55d472a6740b10675474a2ab704de Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 21 Sep 2023 13:34:19 +0200 Subject: [PATCH 030/101] Improve tests --- pymeos/pymeos/temporal/temporal.py | 5 +- pymeos/tests/main/tfloat_test.py | 44 +- pymeos_cffi/pymeos_cffi/__init__.py | 478 ++-- pymeos_cffi/pymeos_cffi/builder/meos.h | 593 ++-- pymeos_cffi/pymeos_cffi/functions.py | 3552 ++++++++++++------------ 5 files changed, 2355 insertions(+), 2317 deletions(-) diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index cdf3d679..35a81030 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -584,13 +584,14 @@ def temporal_precision(self, duration: Union[str, timedelta], duration: A :class:`str` or :class:`timedelta` with the duration of the temporal tiles. start: A :class:`str` or :class:`datetime` with the start time of - the temporal tiles. If None, the start time of `self` is used. + the temporal tiles. If None, the start time Monday, January 3, + 2000 is used. MEOS Functions: temporal_tprecision """ if start is None: - st = temporal_start_timestamp(self._inner) + st = pg_timestamptz_in('2000-01-03', -1) elif isinstance(start, datetime): st = datetime_to_timestamptz(start) else: diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index f8dec0fa..1355cd5f 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -1102,29 +1102,27 @@ def test_shift_scale_time(self): def test_temporal_sample(self, tfloat, delta, expected): assert tfloat.temporal_sample(delta) == expected - # This function should be corrected in MEOS - # @pytest.mark.parametrize( - # 'tfloat, delta, expected', - # [(tfi, timedelta(days=4), TFloatInst('1.5@2019-09-01')), - # (tfi, timedelta(hours=12), TFloatInst('1.5@2019-09-01')), - # (tfds, timedelta(days=4), TFloatSeq('{2@2019-09-01}')), - # (tfds, timedelta(hours=12), TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}')), - # (tfs, timedelta(days=4), TFloatSeq('{2@2019-09-01}')), - # (tfs, timedelta(hours=12), TFloatSeq('{1.5@2019-09-01, 2@2019-09-01 12:00:00, 2.5@2019-09-02}')), - # (tfss, timedelta(days=4), - # TFloatSeq('{1.5@2019-09-01,1.5@2019-09-05}')), - # (tfss, timedelta(hours=12), - # TFloatSeq('{1.5@2019-09-01, 2@2019-09-01 12:00:00, 2.5@2019-09-02,' - # '1.5@2019-09-03, 1.5@2019-09-03 12:00:00, 1.5@2019-09-04, ' - # '1.5@2019-09-04 12:00:00, 1.5@2019-09-05}')), - # ], - # ids=['Instant days', 'Instant hours', - # 'Discrete Sequence days', 'Discrete Sequence hours', - # 'Sequence days', 'Sequence hours', - # 'Sequence Set days', 'Sequence Set hours'] - # ) - # def test_temporal_precision(self, tfloat, delta, expected): - # assert tfloat.temporal_precision(delta) == expected + @pytest.mark.parametrize( + 'tfloat, delta, expected', + [(tfi, timedelta(days=4), TFloatInst('1.5@2019-08-31')), + (tfi, timedelta(hours=12), TFloatInst('1.5@2019-09-01')), + (tfds, timedelta(days=4), TFloatSeq('{2@2019-08-31}')), + (tfds, timedelta(hours=12), TFloatSeq('{1.5@2019-09-01, 2@2019-09-01 12:00:00+00, 2@2019-09-02}')), + (tfs, timedelta(days=4), TFloatSeq('{[2@2019-08-31]}')), + (tfs, timedelta(hours=12), TFloatSeq('{[1.75@2019-09-01, 2.25@2019-09-01 12:00:00+00, 2.5@2019-09-02]}')), + (tfss, timedelta(days=4), + TFloatSeq('{[1.75@2019-08-31, 1.5@2019-09-04]}')), + (tfss, timedelta(hours=12), + TFloatSeq('{[1.75@2019-09-01, 2.25@2019-09-01 12:00:00, 2.5@2019-09-02],' + '[1.5@2019-09-03, 1.5@2019-09-05]}')), + ], + ids=['Instant days', 'Instant hours', + 'Discrete Sequence days', 'Discrete Sequence hours', + 'Sequence days', 'Sequence hours', + 'Sequence Set days', 'Sequence Set hours'] + ) + def test_temporal_precision(self, tfloat, delta, expected): + assert tfloat.temporal_precision(delta) == expected @pytest.mark.parametrize( 'tfloat, delta, expected', diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index cb3bcde3..59eee3f4 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -261,6 +261,75 @@ 'periodset_tprecision', 'timestamp_tprecision', 'timestampset_shift_scale', + 'intersection_bigintset_bigint', + 'intersection_bigintspan_bigint', + 'intersection_bigintspanset_bigint', + 'intersection_floatset_float', + 'intersection_floatspan_float', + 'intersection_floatspanset_float', + 'intersection_geoset_geo', + 'intersection_intset_int', + 'intersection_intspan_int', + 'intersection_intspanset_int', + 'intersection_period_timestamp', + 'intersection_periodset_timestamp', + 'intersection_set_set', + 'intersection_span_span', + 'intersection_spanset_span', + 'intersection_spanset_spanset', + 'intersection_textset_text', + 'intersection_timestampset_timestamp', + 'minus_bigint_bigintset', + 'minus_bigint_bigintspan', + 'minus_bigint_bigintspanset', + 'minus_bigintset_bigint', + 'minus_bigintspan_bigint', + 'minus_bigintspanset_bigint', + 'minus_float_floatset', + 'minus_float_floatspan', + 'minus_float_floatspanset', + 'minus_floatset_float', + 'minus_floatspan_float', + 'minus_floatspanset_float', + 'minus_geo_geoset', + 'minus_geoset_geo', + 'minus_int_intset', + 'minus_int_intspan', + 'minus_int_intspanset', + 'minus_intset_int', + 'minus_intspan_int', + 'minus_intspanset_int', + 'minus_period_timestamp', + 'minus_periodset_timestamp', + 'minus_set_set', + 'minus_span_span', + 'minus_span_spanset', + 'minus_spanset_span', + 'minus_spanset_spanset', + 'minus_text_textset', + 'minus_textset_text', + 'minus_timestamp_period', + 'minus_timestamp_periodset', + 'minus_timestamp_timestampset', + 'minus_timestampset_timestamp', + 'union_bigintset_bigint', + 'union_bigintspan_bigint', + 'union_bigintspanset_bigint', + 'union_floatset_float', + 'union_floatspan_float', + 'union_floatspanset_float', + 'union_geoset_geo', + 'union_intset_int', + 'union_intspan_int', + 'union_intspanset_int', + 'union_period_timestamp', + 'union_periodset_timestamp', + 'union_set_set', + 'union_span_span', + 'union_spanset_span', + 'union_spanset_spanset', + 'union_textset_text', + 'union_timestampset_timestamp', 'adjacent_bigintspan_bigint', 'adjacent_bigintspanset_bigint', 'adjacent_floatspan_float', @@ -361,75 +430,6 @@ 'right_span_spanset', 'right_spanset_span', 'right_spanset_spanset', - 'intersection_bigintset_bigint', - 'intersection_bigintspan_bigint', - 'intersection_bigintspanset_bigint', - 'intersection_floatset_float', - 'intersection_floatspan_float', - 'intersection_floatspanset_float', - 'intersection_geoset_geo', - 'intersection_intset_int', - 'intersection_intspan_int', - 'intersection_intspanset_int', - 'intersection_period_timestamp', - 'intersection_periodset_timestamp', - 'intersection_set_set', - 'intersection_span_span', - 'intersection_spanset_span', - 'intersection_spanset_spanset', - 'intersection_textset_text', - 'intersection_timestampset_timestamp', - 'minus_bigint_bigintset', - 'minus_bigint_bigintspan', - 'minus_bigint_bigintspanset', - 'minus_bigintset_bigint', - 'minus_bigintspan_bigint', - 'minus_bigintspanset_bigint', - 'minus_float_floatset', - 'minus_float_floatspan', - 'minus_float_floatspanset', - 'minus_floatset_float', - 'minus_floatspan_float', - 'minus_floatspanset_float', - 'minus_geo_geoset', - 'minus_geoset_geo', - 'minus_int_intset', - 'minus_int_intspan', - 'minus_int_intspanset', - 'minus_intset_int', - 'minus_intspan_int', - 'minus_intspanset_int', - 'minus_period_timestamp', - 'minus_periodset_timestamp', - 'minus_set_set', - 'minus_span_span', - 'minus_span_spanset', - 'minus_spanset_span', - 'minus_spanset_spanset', - 'minus_text_textset', - 'minus_textset_text', - 'minus_timestamp_period', - 'minus_timestamp_periodset', - 'minus_timestamp_timestampset', - 'minus_timestampset_timestamp', - 'union_bigintset_bigint', - 'union_bigintspan_bigint', - 'union_bigintspanset_bigint', - 'union_floatset_float', - 'union_floatspan_float', - 'union_floatspanset_float', - 'union_geoset_geo', - 'union_intset_int', - 'union_intspan_int', - 'union_intspanset_int', - 'union_period_timestamp', - 'union_periodset_timestamp', - 'union_set_set', - 'union_span_span', - 'union_spanset_span', - 'union_spanset_spanset', - 'union_textset_text', - 'union_timestampset_timestamp', 'distance_bigintset_bigint', 'distance_bigintspan_bigint', 'distance_bigintspanset_bigint', @@ -446,27 +446,6 @@ 'distance_spanset_span', 'distance_spanset_spanset', 'distance_timestampset_timestamp', - 'bigint_extent_transfn', - 'bigint_union_transfn', - 'float_extent_transfn', - 'float_union_transfn', - 'int_extent_transfn', - 'int_union_transfn', - 'period_tcount_transfn', - 'periodset_tcount_transfn', - 'set_extent_transfn', - 'set_union_finalfn', - 'set_union_transfn', - 'span_extent_transfn', - 'span_union_transfn', - 'spanset_extent_transfn', - 'spanset_union_finalfn', - 'spanset_union_transfn', - 'text_union_transfn', - 'timestamp_extent_transfn', - 'timestamp_tcount_transfn', - 'timestamp_union_transfn', - 'timestampset_tcount_transfn', 'set_cmp', 'set_eq', 'set_ge', @@ -488,6 +467,27 @@ 'spanset_le', 'spanset_lt', 'spanset_ne', + 'bigint_extent_transfn', + 'bigint_union_transfn', + 'float_extent_transfn', + 'float_union_transfn', + 'int_extent_transfn', + 'int_union_transfn', + 'period_tcount_transfn', + 'periodset_tcount_transfn', + 'set_extent_transfn', + 'set_union_finalfn', + 'set_union_transfn', + 'span_extent_transfn', + 'span_union_transfn', + 'spanset_extent_transfn', + 'spanset_union_finalfn', + 'spanset_union_transfn', + 'text_union_transfn', + 'timestamp_extent_transfn', + 'timestamp_tcount_transfn', + 'timestamp_union_transfn', + 'timestampset_tcount_transfn', 'tbox_in', 'tbox_out', 'tbox_from_wkb', @@ -500,73 +500,81 @@ 'stbox_as_hexwkb', 'stbox_in', 'stbox_out', - 'stbox_make', - 'stbox_copy', - 'tbox_make', - 'tbox_copy', - 'int_to_tbox', - 'float_to_tbox', - 'timestamp_to_tbox', - 'timestampset_to_tbox', - 'period_to_tbox', - 'periodset_to_tbox', - 'int_timestamp_to_tbox', 'float_period_to_tbox', 'float_timestamp_to_tbox', 'geo_period_to_stbox', 'geo_timestamp_to_stbox', - 'geo_to_stbox', 'int_period_to_tbox', - 'numspan_to_tbox', - 'span_timestamp_to_tbox', + 'int_timestamp_to_tbox', 'span_period_to_tbox', + 'span_timestamp_to_tbox', + 'stbox_copy', + 'stbox_make', + 'tbox_copy', + 'tbox_make', + 'float_to_tbox', + 'geo_to_stbox', + 'int_to_tbox', + 'numset_to_tbox', + 'numspan_to_tbox', + 'numspanset_to_tbox', + 'period_to_stbox', + 'period_to_tbox', + 'periodset_to_stbox', + 'periodset_to_tbox', + 'stbox_to_geo', + 'stbox_to_period', 'tbox_to_floatspan', 'tbox_to_period', - 'stbox_to_period', - 'tnumber_to_tbox', - 'stbox_to_geo', - 'tpoint_to_stbox', 'timestamp_to_stbox', + 'timestamp_to_tbox', 'timestampset_to_stbox', - 'period_to_stbox', - 'periodset_to_stbox', - 'tbox_hasx', - 'tbox_hast', - 'tbox_xmin', - 'tbox_xmin_inc', - 'tbox_xmax', - 'tbox_xmax_inc', - 'tbox_tmin', - 'tbox_tmin_inc', - 'tbox_tmax', - 'tbox_tmax_inc', + 'timestampset_to_tbox', + 'tnumber_to_tbox', + 'tpoint_to_stbox', + 'stbox_hast', 'stbox_hasx', 'stbox_hasz', - 'stbox_hast', 'stbox_isgeodetic', - 'stbox_xmin', + 'stbox_srid', + 'stbox_tmax', + 'stbox_tmax_inc', + 'stbox_tmin', + 'stbox_tmin_inc', 'stbox_xmax', - 'stbox_ymin', + 'stbox_xmin', 'stbox_ymax', - 'stbox_zmin', + 'stbox_ymin', 'stbox_zmax', - 'stbox_tmin', - 'stbox_tmin_inc', - 'stbox_tmax', - 'stbox_tmax_inc', - 'stbox_srid', + 'stbox_zmin', + 'tbox_hast', + 'tbox_hasx', + 'tbox_tmax', + 'tbox_tmax_inc', + 'tbox_tmin', + 'tbox_tmin_inc', + 'tbox_xmax', + 'tbox_xmax_inc', + 'tbox_xmin', + 'tbox_xmin_inc', 'stbox_expand_space', 'stbox_expand_time', 'stbox_get_space', 'stbox_round', 'stbox_set_srid', 'stbox_shift_scale_time', - 'tbox_expand_value', 'tbox_expand_time', + 'tbox_expand_value', 'tbox_round', - 'tbox_shift_scale_int', 'tbox_shift_scale_float', + 'tbox_shift_scale_int', 'tbox_shift_scale_time', + 'union_tbox_tbox', + 'inter_tbox_tbox', + 'intersection_tbox_tbox', + 'union_stbox_stbox', + 'inter_stbox_stbox', + 'intersection_stbox_stbox', 'contains_tbox_tbox', 'contained_tbox_tbox', 'overlaps_tbox_tbox', @@ -601,12 +609,6 @@ 'overbefore_stbox_stbox', 'after_stbox_stbox', 'overafter_stbox_stbox', - 'union_tbox_tbox', - 'inter_tbox_tbox', - 'intersection_tbox_tbox', - 'union_stbox_stbox', - 'inter_stbox_stbox', - 'intersection_stbox_stbox', 'stbox_quad_split', 'tbox_eq', 'tbox_ne', @@ -720,21 +722,29 @@ 'ttext_min_value', 'ttext_start_value', 'ttext_values', + 'temporal_scale_time', 'temporal_set_interp', + 'temporal_shift_scale_time', + 'temporal_shift_time', + 'temporal_to_tinstant', + 'temporal_to_tsequence', + 'temporal_to_tsequenceset', 'tfloat_scale_value', 'tfloat_shift_scale_value', 'tfloat_shift_value', 'tint_scale_value', 'tint_shift_scale_value', 'tint_shift_value', - 'temporal_scale_time', - 'temporal_shift_scale_time', - 'temporal_shift_time', - 'temporal_to_tinstant', - 'temporal_to_tsequence', - 'temporal_to_tsequenceset', - 'temporal_tprecision', - 'temporal_tsample', + 'temporal_append_tinstant', + 'temporal_append_tsequence', + 'temporal_delete_period', + 'temporal_delete_periodset', + 'temporal_delete_timestamp', + 'temporal_delete_timestampset', + 'temporal_insert', + 'temporal_merge', + 'temporal_merge_array', + 'temporal_update', 'tbool_at_value', 'tbool_minus_value', 'tbool_value_at_timestamp', @@ -774,77 +784,13 @@ 'ttext_at_value', 'ttext_minus_value', 'ttext_value_at_timestamp', - 'temporal_append_tinstant', - 'temporal_append_tsequence', - 'temporal_delete_period', - 'temporal_delete_periodset', - 'temporal_delete_timestamp', - 'temporal_delete_timestampset', - 'temporal_insert', - 'temporal_merge', - 'temporal_merge_array', - 'temporal_update', - 'tand_bool_tbool', - 'tand_tbool_bool', - 'tand_tbool_tbool', - 'tbool_when_true', - 'tnot_tbool', - 'tor_bool_tbool', - 'tor_tbool_bool', - 'tor_tbool_tbool', - 'add_float_tfloat', - 'add_int_tint', - 'add_tfloat_float', - 'add_tint_int', - 'add_tnumber_tnumber', - 'div_float_tfloat', - 'div_int_tint', - 'div_tfloat_float', - 'div_tint_int', - 'div_tnumber_tnumber', - 'float_degrees', - 'mult_float_tfloat', - 'mult_int_tint', - 'mult_tfloat_float', - 'mult_tint_int', - 'mult_tnumber_tnumber', - 'sub_float_tfloat', - 'sub_int_tint', - 'sub_tfloat_float', - 'sub_tint_int', - 'sub_tnumber_tnumber', - 'tfloat_round', - 'tfloat_degrees', - 'tfloat_derivative', - 'tfloat_radians', - 'tnumber_abs', - 'tnumber_angular_difference', - 'tnumber_delta_value', - 'textcat_text_ttext', - 'textcat_ttext_text', - 'textcat_ttext_ttext', - 'ttext_upper', - 'ttext_lower', - 'distance_tfloat_float', - 'distance_tint_int', - 'distance_tnumber_tnumber', - 'distance_tpoint_point', - 'distance_tpoint_tpoint', - 'nad_stbox_geo', - 'nad_stbox_stbox', - 'nad_tbox_tbox', - 'nad_tfloat_float', - 'nad_tfloat_tfloat', - 'nad_tint_int', - 'nad_tint_tint', - 'nad_tnumber_tbox', - 'nad_tpoint_geo', - 'nad_tpoint_stbox', - 'nad_tpoint_tpoint', - 'nai_tpoint_geo', - 'nai_tpoint_tpoint', - 'shortestline_tpoint_geo', - 'shortestline_tpoint_tpoint', + 'temporal_cmp', + 'temporal_eq', + 'temporal_ge', + 'temporal_gt', + 'temporal_le', + 'temporal_lt', + 'temporal_ne', 'tbool_always_eq', 'tbool_ever_eq', 'tfloat_always_eq', @@ -867,13 +813,6 @@ 'ttext_ever_eq', 'ttext_ever_le', 'ttext_ever_lt', - 'temporal_cmp', - 'temporal_eq', - 'temporal_ge', - 'temporal_gt', - 'temporal_le', - 'temporal_lt', - 'temporal_ne', 'teq_bool_tbool', 'teq_float_tfloat', 'teq_int_tint', @@ -924,6 +863,67 @@ 'tne_tpoint_point', 'tne_tint_int', 'tne_ttext_text', + 'tand_bool_tbool', + 'tand_tbool_bool', + 'tand_tbool_tbool', + 'tbool_when_true', + 'tnot_tbool', + 'tor_bool_tbool', + 'tor_tbool_bool', + 'tor_tbool_tbool', + 'add_float_tfloat', + 'add_int_tint', + 'add_tfloat_float', + 'add_tint_int', + 'add_tnumber_tnumber', + 'div_float_tfloat', + 'div_int_tint', + 'div_tfloat_float', + 'div_tint_int', + 'div_tnumber_tnumber', + 'float_degrees', + 'mult_float_tfloat', + 'mult_int_tint', + 'mult_tfloat_float', + 'mult_tint_int', + 'mult_tnumber_tnumber', + 'sub_float_tfloat', + 'sub_int_tint', + 'sub_tfloat_float', + 'sub_tint_int', + 'sub_tnumber_tnumber', + 'tfloat_round', + 'tfloat_degrees', + 'tfloat_derivative', + 'tfloat_radians', + 'tnumber_abs', + 'tnumber_angular_difference', + 'tnumber_delta_value', + 'textcat_text_ttext', + 'textcat_ttext_text', + 'textcat_ttext_ttext', + 'ttext_upper', + 'ttext_lower', + 'distance_tfloat_float', + 'distance_tint_int', + 'distance_tnumber_tnumber', + 'distance_tpoint_point', + 'distance_tpoint_tpoint', + 'nad_stbox_geo', + 'nad_stbox_stbox', + 'nad_tbox_tbox', + 'nad_tfloat_float', + 'nad_tfloat_tfloat', + 'nad_tint_int', + 'nad_tint_tint', + 'nad_tnumber_tbox', + 'nad_tpoint_geo', + 'nad_tpoint_stbox', + 'nad_tpoint_tpoint', + 'nai_tpoint_geo', + 'nai_tpoint_tpoint', + 'shortestline_tpoint_geo', + 'shortestline_tpoint_tpoint', 'bearing_point_point', 'bearing_tpoint_point', 'bearing_tpoint_tpoint', @@ -942,12 +942,15 @@ 'tpoint_stboxes', 'tpoint_trajectory', 'geo_expand_space', - 'tpoint_expand_space', - 'tgeompoint_to_tgeogpoint', + 'geo_to_tpoint', 'tgeogpoint_to_tgeompoint', - 'tpoint_round', + 'tgeompoint_to_tgeogpoint', + 'tpoint_AsMVTGeom', + 'tpoint_expand_space', 'tpoint_make_simple', + 'tpoint_round', 'tpoint_set_srid', + 'tpoint_to_geo_meas', 'econtains_geo_tpoint', 'edisjoint_tpoint_geo', 'edisjoint_tpoint_tpoint', @@ -984,6 +987,17 @@ 'tpoint_twcentroid', 'ttext_tmax_transfn', 'ttext_tmin_transfn', + 'temporal_simplify_min_dist', + 'temporal_simplify_min_tdelta', + 'temporal_simplify_dp', + 'temporal_simplify_max_dist', + 'temporal_tprecision', + 'temporal_tsample', + 'temporal_dyntimewarp_distance', + 'temporal_dyntimewarp_path', + 'temporal_frechet_distance', + 'temporal_frechet_path', + 'temporal_hausdorff_distance', 'float_bucket', 'floatspan_bucket_list', 'int_bucket', @@ -997,16 +1011,4 @@ 'timestamptz_bucket', 'tint_value_split', 'tint_value_time_split', - 'temporal_dyntimewarp_distance', - 'temporal_dyntimewarp_path', - 'temporal_frechet_distance', - 'temporal_frechet_path', - 'temporal_hausdorff_distance', - 'geo_to_tpoint', - 'temporal_simplify_min_dist', - 'temporal_simplify_min_tdelta', - 'temporal_simplify_dp', - 'temporal_simplify_max_dist', - 'tpoint_AsMVTGeom', - 'tpoint_to_geo_meas', ] diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 6664c41c..5e173e2b 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -744,7 +744,7 @@ extern void meos_initialize(const char *tz_str, error_handler_fn err_handler); extern void meos_finalize(void); /***************************************************************************** - * Functions for input/output PostgreSQL time types + * Functions for PostgreSQL types *****************************************************************************/ extern bool bool_in(const char *in_str); @@ -775,7 +775,7 @@ extern TimestampTz pg_to_timestamp(text *date_txt, text *fmt); extern char *text2cstring(const text *textptr); /***************************************************************************** - * Functions for input/output and manipulation of PostGIS types + * Functions for PostGIS types *****************************************************************************/ extern GSERIALIZED *geography_from_hexewkb(const char *wkt); @@ -983,6 +983,80 @@ extern SpanSet *periodset_tprecision(const SpanSet *ss, const Interval *duration extern TimestampTz timestamp_tprecision(TimestampTz t, const Interval *duration, TimestampTz torigin); extern Set *timestampset_shift_scale(const Set *ts, const Interval *shift, const Interval *duration); + + + + +extern bool intersection_bigintset_bigint(const Set *s, int64 i, int64 *result); +extern bool intersection_bigintspan_bigint(const Span *s, int64 i, int64 *result); +extern bool intersection_bigintspanset_bigint(const SpanSet *ss, int64 i, int64 *result); +extern bool intersection_floatset_float(const Set *s, double d, double *result); +extern bool intersection_floatspan_float(const Span *s, double d, double *result); +extern bool intersection_floatspanset_float(const SpanSet *ss, double d, double *result); +extern bool intersection_geoset_geo(const Set *s, const GSERIALIZED *gs, GSERIALIZED **result); +extern bool intersection_intset_int(const Set *s, int i, int *result); +extern bool intersection_intspan_int(const Span *s, int i, int *result); +extern bool intersection_intspanset_int(const SpanSet *ss, int i, int *result); +extern bool intersection_period_timestamp(const Span *s, TimestampTz t, TimestampTz *result); +extern bool intersection_periodset_timestamp(const SpanSet *ss, TimestampTz t, TimestampTz *result); +extern Set *intersection_set_set(const Set *s1, const Set *s2); +extern Span *intersection_span_span(const Span *s1, const Span *s2); +extern SpanSet *intersection_spanset_span(const SpanSet *ss, const Span *s); +extern SpanSet *intersection_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern bool intersection_textset_text(const Set *s, const text *txt, text **result); +extern bool intersection_timestampset_timestamp(const Set *s, TimestampTz t, TimestampTz *result); +extern bool minus_bigint_bigintset(int64 i, const Set *s, int64 *result); +extern bool minus_bigint_bigintspan(int64 i, const Span *s, int64 *result); +extern bool minus_bigint_bigintspanset(int64 i, const SpanSet *ss, int64 *result); +extern Set *minus_bigintset_bigint(const Set *s, int64 i); +extern SpanSet *minus_bigintspan_bigint(const Span *s, int64 i); +extern SpanSet *minus_bigintspanset_bigint(const SpanSet *ss, int64 i); +extern bool minus_float_floatset(double d, const Set *s, double *result); +extern bool minus_float_floatspan(double d, const Span *s, double *result); +extern bool minus_float_floatspanset(double d, const SpanSet *ss, double *result); +extern Set *minus_floatset_float(const Set *s, double d); +extern SpanSet *minus_floatspan_float(const Span *s, double d); +extern SpanSet *minus_floatspanset_float(const SpanSet *ss, double d); +extern bool minus_geo_geoset(const GSERIALIZED *gs, const Set *s, GSERIALIZED **result); +extern Set *minus_geoset_geo(const Set *s, const GSERIALIZED *gs); +extern bool minus_int_intset(int i, const Set *s, int *result); +extern bool minus_int_intspan(int i, const Span *s, int *result); +extern bool minus_int_intspanset(int i, const SpanSet *ss, int *result); +extern Set *minus_intset_int(const Set *s, int i); +extern SpanSet *minus_intspan_int(const Span *s, int i); +extern SpanSet *minus_intspanset_int(const SpanSet *ss, int i); +extern SpanSet *minus_period_timestamp(const Span *s, TimestampTz t); +extern SpanSet *minus_periodset_timestamp(const SpanSet *ss, TimestampTz t); +extern Set *minus_set_set(const Set *s1, const Set *s2); +extern SpanSet *minus_span_span(const Span *s1, const Span *s2); +extern SpanSet *minus_span_spanset(const Span *s, const SpanSet *ss); +extern SpanSet *minus_spanset_span(const SpanSet *ss, const Span *s); +extern SpanSet *minus_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern bool minus_text_textset(const text *txt, const Set *s, text **result); +extern Set *minus_textset_text(const Set *s, const text *txt); +extern bool minus_timestamp_period(TimestampTz t, const Span *s, TimestampTz *result); +extern bool minus_timestamp_periodset(TimestampTz t, const SpanSet *ss, TimestampTz *result); +extern bool minus_timestamp_timestampset(TimestampTz t, const Set *s, TimestampTz *result); +extern Set *minus_timestampset_timestamp(const Set *s, TimestampTz t); +extern Set *union_bigintset_bigint(const Set *s, int64 i); +extern SpanSet *union_bigintspan_bigint(const Span *s, int64 i); +extern SpanSet *union_bigintspanset_bigint(const SpanSet *ss, int64 i); +extern Set *union_floatset_float(const Set *s, double d); +extern SpanSet *union_floatspan_float(const Span *s, double d); +extern SpanSet *union_floatspanset_float(const SpanSet *ss, double d); +extern Set *union_geoset_geo(const Set *s, const GSERIALIZED *gs); +extern Set *union_intset_int(const Set *s, int i); +extern SpanSet *union_intspan_int(const Span *s, int i); +extern SpanSet *union_intspanset_int(const SpanSet *ss, int i); +extern SpanSet *union_period_timestamp(const Span *s, TimestampTz t); +extern SpanSet *union_periodset_timestamp(SpanSet *ss, TimestampTz t); +extern Set *union_set_set(const Set *s1, const Set *s2); +extern SpanSet *union_span_span(const Span *s1, const Span *s2); +extern SpanSet *union_spanset_span(const SpanSet *ss, const Span *s); +extern SpanSet *union_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern Set *union_textset_text(const Set *s, const text *txt); +extern Set *union_timestampset_timestamp(const Set *s, const TimestampTz t); + /***************************************************************************** * Bounding box functions for set and span types *****************************************************************************/ @@ -1099,80 +1173,6 @@ extern bool right_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool intersection_bigintset_bigint(const Set *s, int64 i, int64 *result); -extern bool intersection_bigintspan_bigint(const Span *s, int64 i, int64 *result); -extern bool intersection_bigintspanset_bigint(const SpanSet *ss, int64 i, int64 *result); -extern bool intersection_floatset_float(const Set *s, double d, double *result); -extern bool intersection_floatspan_float(const Span *s, double d, double *result); -extern bool intersection_floatspanset_float(const SpanSet *ss, double d, double *result); -extern bool intersection_geoset_geo(const Set *s, const GSERIALIZED *gs, GSERIALIZED **result); -extern bool intersection_intset_int(const Set *s, int i, int *result); -extern bool intersection_intspan_int(const Span *s, int i, int *result); -extern bool intersection_intspanset_int(const SpanSet *ss, int i, int *result); -extern bool intersection_period_timestamp(const Span *s, TimestampTz t, TimestampTz *result); -extern bool intersection_periodset_timestamp(const SpanSet *ss, TimestampTz t, TimestampTz *result); -extern Set *intersection_set_set(const Set *s1, const Set *s2); -extern Span *intersection_span_span(const Span *s1, const Span *s2); -extern SpanSet *intersection_spanset_span(const SpanSet *ss, const Span *s); -extern SpanSet *intersection_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool intersection_textset_text(const Set *s, const text *txt, text **result); -extern bool intersection_timestampset_timestamp(const Set *s, TimestampTz t, TimestampTz *result); -extern bool minus_bigint_bigintset(int64 i, const Set *s, int64 *result); -extern bool minus_bigint_bigintspan(int64 i, const Span *s, int64 *result); -extern bool minus_bigint_bigintspanset(int64 i, const SpanSet *ss, int64 *result); -extern Set *minus_bigintset_bigint(const Set *s, int64 i); -extern SpanSet *minus_bigintspan_bigint(const Span *s, int64 i); -extern SpanSet *minus_bigintspanset_bigint(const SpanSet *ss, int64 i); -extern bool minus_float_floatset(double d, const Set *s, double *result); -extern bool minus_float_floatspan(double d, const Span *s, double *result); -extern bool minus_float_floatspanset(double d, const SpanSet *ss, double *result); -extern Set *minus_floatset_float(const Set *s, double d); -extern SpanSet *minus_floatspan_float(const Span *s, double d); -extern SpanSet *minus_floatspanset_float(const SpanSet *ss, double d); -extern bool minus_geo_geoset(const GSERIALIZED *gs, const Set *s, GSERIALIZED **result); -extern Set *minus_geoset_geo(const Set *s, const GSERIALIZED *gs); -extern bool minus_int_intset(int i, const Set *s, int *result); -extern bool minus_int_intspan(int i, const Span *s, int *result); -extern bool minus_int_intspanset(int i, const SpanSet *ss, int *result); -extern Set *minus_intset_int(const Set *s, int i); -extern SpanSet *minus_intspan_int(const Span *s, int i); -extern SpanSet *minus_intspanset_int(const SpanSet *ss, int i); -extern SpanSet *minus_period_timestamp(const Span *s, TimestampTz t); -extern SpanSet *minus_periodset_timestamp(const SpanSet *ss, TimestampTz t); -extern Set *minus_set_set(const Set *s1, const Set *s2); -extern SpanSet *minus_span_span(const Span *s1, const Span *s2); -extern SpanSet *minus_span_spanset(const Span *s, const SpanSet *ss); -extern SpanSet *minus_spanset_span(const SpanSet *ss, const Span *s); -extern SpanSet *minus_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool minus_text_textset(const text *txt, const Set *s, text **result); -extern Set *minus_textset_text(const Set *s, const text *txt); -extern bool minus_timestamp_period(TimestampTz t, const Span *s, TimestampTz *result); -extern bool minus_timestamp_periodset(TimestampTz t, const SpanSet *ss, TimestampTz *result); -extern bool minus_timestamp_timestampset(TimestampTz t, const Set *s, TimestampTz *result); -extern Set *minus_timestampset_timestamp(const Set *s, TimestampTz t); -extern Set *union_bigintset_bigint(const Set *s, int64 i); -extern SpanSet *union_bigintspan_bigint(const Span *s, int64 i); -extern SpanSet *union_bigintspanset_bigint(const SpanSet *ss, int64 i); -extern Set *union_floatset_float(const Set *s, double d); -extern SpanSet *union_floatspan_float(const Span *s, double d); -extern SpanSet *union_floatspanset_float(const SpanSet *ss, double d); -extern Set *union_geoset_geo(const Set *s, const GSERIALIZED *gs); -extern Set *union_intset_int(const Set *s, int i); -extern SpanSet *union_intspan_int(const Span *s, int i); -extern SpanSet *union_intspanset_int(const SpanSet *ss, int i); -extern SpanSet *union_period_timestamp(const Span *s, TimestampTz t); -extern SpanSet *union_periodset_timestamp(SpanSet *ss, TimestampTz t); -extern Set *union_set_set(const Set *s1, const Set *s2); -extern SpanSet *union_span_span(const Span *s1, const Span *s2); -extern SpanSet *union_spanset_span(const SpanSet *ss, const Span *s); -extern SpanSet *union_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern Set *union_textset_text(const Set *s, const text *txt); -extern Set *union_timestampset_timestamp(const Set *s, const TimestampTz t); - - - - - extern double distance_bigintset_bigint(const Set *s, int64 i); extern double distance_bigintspan_bigint(const Span *s, int64 i); extern double distance_bigintspanset_bigint(const SpanSet *ss, int64 i); @@ -1194,32 +1194,6 @@ extern double distance_timestampset_timestamp(const Set *s, TimestampTz t); -extern Span *bigint_extent_transfn(Span *s, int64 i); -extern Set *bigint_union_transfn(Set *state, int64 i); -extern Span *float_extent_transfn(Span *s, double d); -extern Set *float_union_transfn(Set *state, double d); -extern Span *int_extent_transfn(Span *s, int i); -extern Set *int_union_transfn(Set *state, int i); -extern SkipList *period_tcount_transfn(SkipList *state, const Span *p); -extern SkipList *periodset_tcount_transfn(SkipList *state, const SpanSet *ps); -extern Span *set_extent_transfn(Span *span, const Set *set); -extern Set *set_union_finalfn(Set *state); -extern Set *set_union_transfn(Set *state, Set *set); -extern Span *span_extent_transfn(Span *s1, const Span *s2); -extern SpanSet *span_union_transfn(SpanSet *state, const Span *span); -extern Span *spanset_extent_transfn(Span *s, const SpanSet *ss); -extern SpanSet *spanset_union_finalfn(SpanSet *state); -extern SpanSet *spanset_union_transfn(SpanSet *state, const SpanSet *ss); -extern Set *text_union_transfn(Set *state, const text *txt); -extern Span *timestamp_extent_transfn(Span *p, TimestampTz t); -extern SkipList *timestamp_tcount_transfn(SkipList *state, TimestampTz t); -extern Set *timestamp_union_transfn(Set *state, TimestampTz t); -extern SkipList *timestampset_tcount_transfn(SkipList *state, const Set *ts); - - - - - extern int set_cmp(const Set *s1, const Set *s2); extern bool set_eq(const Set *s1, const Set *s2); extern bool set_ge(const Set *s1, const Set *s2); @@ -1242,6 +1216,32 @@ extern bool spanset_le(const SpanSet *ss1, const SpanSet *ss2); extern bool spanset_lt(const SpanSet *ss1, const SpanSet *ss2); extern bool spanset_ne(const SpanSet *ss1, const SpanSet *ss2); + + + + +extern Span *bigint_extent_transfn(Span *s, int64 i); +extern Set *bigint_union_transfn(Set *state, int64 i); +extern Span *float_extent_transfn(Span *s, double d); +extern Set *float_union_transfn(Set *state, double d); +extern Span *int_extent_transfn(Span *s, int i); +extern Set *int_union_transfn(Set *state, int i); +extern SkipList *period_tcount_transfn(SkipList *state, const Span *p); +extern SkipList *periodset_tcount_transfn(SkipList *state, const SpanSet *ps); +extern Span *set_extent_transfn(Span *span, const Set *set); +extern Set *set_union_finalfn(Set *state); +extern Set *set_union_transfn(Set *state, Set *set); +extern Span *span_extent_transfn(Span *s1, const Span *s2); +extern SpanSet *span_union_transfn(SpanSet *state, const Span *span); +extern Span *spanset_extent_transfn(Span *s, const SpanSet *ss); +extern SpanSet *spanset_union_finalfn(SpanSet *state); +extern SpanSet *spanset_union_transfn(SpanSet *state, const SpanSet *ss); +extern Set *text_union_transfn(Set *state, const text *txt); +extern Span *timestamp_extent_transfn(Span *p, TimestampTz t); +extern SkipList *timestamp_tcount_transfn(SkipList *state, TimestampTz t); +extern Set *timestamp_union_transfn(Set *state, TimestampTz t); +extern SkipList *timestampset_tcount_transfn(SkipList *state, const Set *ts); + /****************************************************************************** * Functions for box types *****************************************************************************/ @@ -1265,72 +1265,74 @@ extern char *stbox_out(const STBox *box, int maxdd); +extern TBox *float_period_to_tbox(double d, const Span *p); +extern TBox *float_timestamp_to_tbox(double d, TimestampTz t); +extern STBox *geo_period_to_stbox(const GSERIALIZED *gs, const Span *p); +extern STBox *geo_timestamp_to_stbox(const GSERIALIZED *gs, TimestampTz t); +extern TBox *int_period_to_tbox(int i, const Span *p); +extern TBox *int_timestamp_to_tbox(int i, TimestampTz t); +extern TBox *span_period_to_tbox(const Span *span, const Span *p); +extern TBox *span_timestamp_to_tbox(const Span *span, TimestampTz t); +extern STBox *stbox_copy(const STBox *box); extern STBox * stbox_make(bool hasx, bool hasz, bool geodetic, int32 srid, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, const Span *p); -extern STBox *stbox_copy(const STBox *box); -extern TBox *tbox_make(const Span *s, const Span *p); extern TBox *tbox_copy(const TBox *box); +extern TBox *tbox_make(const Span *s, const Span *p); -extern TBox *int_to_tbox(int i); extern TBox *float_to_tbox(double d); -extern TBox *timestamp_to_tbox(TimestampTz t); -extern TBox *timestampset_to_tbox(const Set *ss); -extern TBox *period_to_tbox(const Span *p); -extern TBox *periodset_to_tbox(const SpanSet *ps); -extern TBox *int_timestamp_to_tbox(int i, TimestampTz t); -extern TBox *float_period_to_tbox(double d, const Span *p); -extern TBox *float_timestamp_to_tbox(double d, TimestampTz t); -extern STBox *geo_period_to_stbox(const GSERIALIZED *gs, const Span *p); -extern STBox *geo_timestamp_to_stbox(const GSERIALIZED *gs, TimestampTz t); extern STBox *geo_to_stbox(const GSERIALIZED *gs); -extern TBox *int_period_to_tbox(int i, const Span *p); +extern TBox *int_to_tbox(int i); +extern TBox *numset_to_tbox(const Set *s); extern TBox *numspan_to_tbox(const Span *s); -extern TBox *span_timestamp_to_tbox(const Span *span, TimestampTz t); -extern TBox *span_period_to_tbox(const Span *span, const Span *p); +extern TBox *numspanset_to_tbox(const SpanSet *ss); +extern STBox *period_to_stbox(const Span *p); +extern TBox *period_to_tbox(const Span *p); +extern STBox *periodset_to_stbox(const SpanSet *ps); +extern TBox *periodset_to_tbox(const SpanSet *ps); +extern GSERIALIZED *stbox_to_geo(const STBox *box); +extern Span *stbox_to_period(const STBox *box); extern Span *tbox_to_floatspan(const TBox *box); extern Span *tbox_to_period(const TBox *box); -extern Span *stbox_to_period(const STBox *box); -extern TBox *tnumber_to_tbox(const Temporal *temp); -extern GSERIALIZED *stbox_to_geo(const STBox *box); -extern STBox *tpoint_to_stbox(const Temporal *temp); extern STBox *timestamp_to_stbox(TimestampTz t); +extern TBox *timestamp_to_tbox(TimestampTz t); extern STBox *timestampset_to_stbox(const Set *ts); -extern STBox *period_to_stbox(const Span *p); -extern STBox *periodset_to_stbox(const SpanSet *ps); +extern TBox *timestampset_to_tbox(const Set *ss); +extern TBox *tnumber_to_tbox(const Temporal *temp); +extern STBox *tpoint_to_stbox(const Temporal *temp); -extern bool tbox_hasx(const TBox *box); -extern bool tbox_hast(const TBox *box); -extern bool tbox_xmin(const TBox *box, double *result); -extern bool tbox_xmin_inc(const TBox *box, bool *result); -extern bool tbox_xmax(const TBox *box, double *result); -extern bool tbox_xmax_inc(const TBox *box, bool *result); -extern bool tbox_tmin(const TBox *box, TimestampTz *result); -extern bool tbox_tmin_inc(const TBox *box, bool *result); -extern bool tbox_tmax(const TBox *box, TimestampTz *result); -extern bool tbox_tmax_inc(const TBox *box, bool *result); +extern bool stbox_hast(const STBox *box); extern bool stbox_hasx(const STBox *box); extern bool stbox_hasz(const STBox *box); -extern bool stbox_hast(const STBox *box); extern bool stbox_isgeodetic(const STBox *box); -extern bool stbox_xmin(const STBox *box, double *result); +extern int32 stbox_srid(const STBox *box); +extern bool stbox_tmax(const STBox *box, TimestampTz *result); +extern bool stbox_tmax_inc(const STBox *box, bool *result); +extern bool stbox_tmin(const STBox *box, TimestampTz *result); +extern bool stbox_tmin_inc(const STBox *box, bool *result); extern bool stbox_xmax(const STBox *box, double *result); -extern bool stbox_ymin(const STBox *box, double *result); +extern bool stbox_xmin(const STBox *box, double *result); extern bool stbox_ymax(const STBox *box, double *result); -extern bool stbox_zmin(const STBox *box, double *result); +extern bool stbox_ymin(const STBox *box, double *result); extern bool stbox_zmax(const STBox *box, double *result); -extern bool stbox_tmin(const STBox *box, TimestampTz *result); -extern bool stbox_tmin_inc(const STBox *box, bool *result); -extern bool stbox_tmax(const STBox *box, TimestampTz *result); -extern bool stbox_tmax_inc(const STBox *box, bool *result); -extern int32 stbox_srid(const STBox *box); +extern bool stbox_zmin(const STBox *box, double *result); +extern bool tbox_hast(const TBox *box); +extern bool tbox_hasx(const TBox *box); +extern bool tbox_tmax(const TBox *box, TimestampTz *result); +extern bool tbox_tmax_inc(const TBox *box, bool *result); +extern bool tbox_tmin(const TBox *box, TimestampTz *result); +extern bool tbox_tmin_inc(const TBox *box, bool *result); +extern bool tbox_xmax(const TBox *box, double *result); +extern bool tbox_xmax_inc(const TBox *box, bool *result); +extern bool tbox_xmin(const TBox *box, double *result); +extern bool tbox_xmin_inc(const TBox *box, bool *result); @@ -1342,17 +1344,30 @@ extern STBox *stbox_get_space(const STBox *box); extern STBox *stbox_round(const STBox *box, int maxdd); extern STBox *stbox_set_srid(const STBox *box, int32 srid); extern STBox *stbox_shift_scale_time(const STBox *box, const Interval *shift, const Interval *duration); -extern TBox *tbox_expand_value(const TBox *box, const double d); extern TBox *tbox_expand_time(const TBox *box, const Interval *interval); +extern TBox *tbox_expand_value(const TBox *box, const double d); extern TBox *tbox_round(const TBox *box, int maxdd); -extern TBox *tbox_shift_scale_int(const TBox *box, int shift, int width, bool hasshift, bool haswidth); extern TBox *tbox_shift_scale_float(const TBox *box, double shift, double width, bool hasshift, bool haswidth); +extern TBox *tbox_shift_scale_int(const TBox *box, int shift, int width, bool hasshift, bool haswidth); extern TBox *tbox_shift_scale_time(const TBox *box, const Interval *shift, const Interval *duration); +extern TBox *union_tbox_tbox(const TBox *box1, const TBox *box2, bool strict); +extern bool inter_tbox_tbox(const TBox *box1, const TBox *box2, TBox *result); +extern TBox *intersection_tbox_tbox(const TBox *box1, const TBox *box2); +extern STBox *union_stbox_stbox(const STBox *box1, const STBox *box2, bool strict); +extern bool inter_stbox_stbox(const STBox *box1, const STBox *box2, STBox *result); +extern STBox *intersection_stbox_stbox(const STBox *box1, const STBox *box2); + +/***************************************************************************** + * Bounding box functions for box types + *****************************************************************************/ + + + extern bool contains_tbox_tbox(const TBox *box1, const TBox *box2); extern bool contained_tbox_tbox(const TBox *box1, const TBox *box2); extern bool overlaps_tbox_tbox(const TBox *box1, const TBox *box2); @@ -1397,17 +1412,6 @@ extern bool overafter_stbox_stbox(const STBox *box1, const STBox *box2); -extern TBox *union_tbox_tbox(const TBox *box1, const TBox *box2, bool strict); -extern bool inter_tbox_tbox(const TBox *box1, const TBox *box2, TBox *result); -extern TBox *intersection_tbox_tbox(const TBox *box1, const TBox *box2); -extern STBox *union_stbox_stbox(const STBox *box1, const STBox *box2, bool strict); -extern bool inter_stbox_stbox(const STBox *box1, const STBox *box2, STBox *result); -extern STBox *intersection_stbox_stbox(const STBox *box1, const STBox *box2); - - - - - extern STBox *stbox_quad_split(const STBox *box, int *count); @@ -1553,21 +1557,34 @@ extern text **ttext_values(const Temporal *temp, int *count); +extern Temporal *temporal_scale_time(const Temporal *temp, const Interval *duration); extern Temporal *temporal_set_interp(const Temporal *temp, interpType interp); +extern Temporal *temporal_shift_scale_time(const Temporal *temp, const Interval *shift, const Interval *duration); +extern Temporal *temporal_shift_time(const Temporal *temp, const Interval *shift); +extern Temporal *temporal_to_tinstant(const Temporal *temp); +extern Temporal *temporal_to_tsequence(const Temporal *temp, interpType interp); +extern Temporal *temporal_to_tsequenceset(const Temporal *temp, interpType interp); extern Temporal *tfloat_scale_value(const Temporal *temp, double width); extern Temporal *tfloat_shift_scale_value(const Temporal *temp, double shift, double width); extern Temporal *tfloat_shift_value(const Temporal *temp, double shift); extern Temporal *tint_scale_value(const Temporal *temp, int width); extern Temporal *tint_shift_scale_value(const Temporal *temp, int shift, int width); extern Temporal *tint_shift_value(const Temporal *temp, int shift); -extern Temporal *temporal_scale_time(const Temporal *temp, const Interval *duration); -extern Temporal *temporal_shift_scale_time(const Temporal *temp, const Interval *shift, const Interval *duration); -extern Temporal *temporal_shift_time(const Temporal *temp, const Interval *shift); -extern Temporal *temporal_to_tinstant(const Temporal *temp); -extern Temporal *temporal_to_tsequence(const Temporal *temp, interpType interp); -extern Temporal *temporal_to_tsequenceset(const Temporal *temp, interpType interp); -extern Temporal *temporal_tprecision(const Temporal *temp, const Interval *duration, TimestampTz origin); -extern Temporal *temporal_tsample(const Temporal *temp, const Interval *duration, TimestampTz origin); + + + + + +extern Temporal *temporal_append_tinstant(Temporal *temp, const TInstant *inst, double maxdist, Interval *maxt, bool expand); +extern Temporal *temporal_append_tsequence(Temporal *temp, const TSequence *seq, bool expand); +extern Temporal *temporal_delete_period(const Temporal *temp, const Span *p, bool connect); +extern Temporal *temporal_delete_periodset(const Temporal *temp, const SpanSet *ps, bool connect); +extern Temporal *temporal_delete_timestamp(const Temporal *temp, TimestampTz t, bool connect); +extern Temporal *temporal_delete_timestampset(const Temporal *temp, const Set *ts, bool connect); +extern Temporal *temporal_insert(const Temporal *temp1, const Temporal *temp2, bool connect); +extern Temporal *temporal_merge(const Temporal *temp1, const Temporal *temp2); +extern Temporal *temporal_merge_array(Temporal **temparr, int count); +extern Temporal *temporal_update(const Temporal *temp1, const Temporal *temp2, bool connect); @@ -1613,101 +1630,19 @@ extern Temporal *ttext_at_value(const Temporal *temp, text *txt); extern Temporal *ttext_minus_value(const Temporal *temp, text *txt); extern bool ttext_value_at_timestamp(const Temporal *temp, TimestampTz t, bool strict, text **value); +/***************************************************************************** + * Comparison functions for temporal types + *****************************************************************************/ - -extern Temporal *temporal_append_tinstant(Temporal *temp, const TInstant *inst, double maxdist, Interval *maxt, bool expand); -extern Temporal *temporal_append_tsequence(Temporal *temp, const TSequence *seq, bool expand); -extern Temporal *temporal_delete_period(const Temporal *temp, const Span *p, bool connect); -extern Temporal *temporal_delete_periodset(const Temporal *temp, const SpanSet *ps, bool connect); -extern Temporal *temporal_delete_timestamp(const Temporal *temp, TimestampTz t, bool connect); -extern Temporal *temporal_delete_timestampset(const Temporal *temp, const Set *ts, bool connect); -extern Temporal *temporal_insert(const Temporal *temp1, const Temporal *temp2, bool connect); -extern Temporal *temporal_merge(const Temporal *temp1, const Temporal *temp2); -extern Temporal *temporal_merge_array(Temporal **temparr, int count); -extern Temporal *temporal_update(const Temporal *temp1, const Temporal *temp2, bool connect); - - - - - -extern Temporal *tand_bool_tbool(bool b, const Temporal *temp); -extern Temporal *tand_tbool_bool(const Temporal *temp, bool b); -extern Temporal *tand_tbool_tbool(const Temporal *temp1, const Temporal *temp2); -extern SpanSet *tbool_when_true(const Temporal *temp); -extern Temporal *tnot_tbool(const Temporal *temp); -extern Temporal *tor_bool_tbool(bool b, const Temporal *temp); -extern Temporal *tor_tbool_bool(const Temporal *temp, bool b); -extern Temporal *tor_tbool_tbool(const Temporal *temp1, const Temporal *temp2); - - - - - -extern Temporal *add_float_tfloat(double d, const Temporal *tnumber); -extern Temporal *add_int_tint(int i, const Temporal *tnumber); -extern Temporal *add_tfloat_float(const Temporal *tnumber, double d); -extern Temporal *add_tint_int(const Temporal *tnumber, int i); -extern Temporal *add_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); -extern Temporal *div_float_tfloat(double d, const Temporal *tnumber); -extern Temporal *div_int_tint(int i, const Temporal *tnumber); -extern Temporal *div_tfloat_float(const Temporal *tnumber, double d); -extern Temporal *div_tint_int(const Temporal *tnumber, int i); -extern Temporal *div_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); -extern double float_degrees(double value, bool normalize); -extern Temporal *mult_float_tfloat(double d, const Temporal *tnumber); -extern Temporal *mult_int_tint(int i, const Temporal *tnumber); -extern Temporal *mult_tfloat_float(const Temporal *tnumber, double d); -extern Temporal *mult_tint_int(const Temporal *tnumber, int i); -extern Temporal *mult_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); -extern Temporal *sub_float_tfloat(double d, const Temporal *tnumber); -extern Temporal *sub_int_tint(int i, const Temporal *tnumber); -extern Temporal *sub_tfloat_float(const Temporal *tnumber, double d); -extern Temporal *sub_tint_int(const Temporal *tnumber, int i); -extern Temporal *sub_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); -extern Temporal *tfloat_round(const Temporal *temp, int maxdd); -extern Temporal *tfloat_degrees(const Temporal *temp, bool normalize); -extern Temporal *tfloat_derivative(const Temporal *temp); -extern Temporal *tfloat_radians(const Temporal *temp); -extern Temporal *tnumber_abs(const Temporal *temp); -extern Temporal *tnumber_angular_difference(const Temporal *temp); -extern Temporal *tnumber_delta_value(const Temporal *temp); - - - - - -extern Temporal *textcat_text_ttext(const text *txt, const Temporal *temp); -extern Temporal *textcat_ttext_text(const Temporal *temp, const text *txt); -extern Temporal *textcat_ttext_ttext(const Temporal *temp1, const Temporal *temp2); -extern Temporal *ttext_upper(const Temporal *temp); -extern Temporal *ttext_lower(const Temporal *temp); - - - - - -extern Temporal *distance_tfloat_float(const Temporal *temp, double d); -extern Temporal *distance_tint_int(const Temporal *temp, int i); -extern Temporal *distance_tnumber_tnumber(const Temporal *temp1, const Temporal *temp2); -extern Temporal *distance_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); -extern Temporal *distance_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); -extern double nad_stbox_geo(const STBox *box, const GSERIALIZED *gs); -extern double nad_stbox_stbox(const STBox *box1, const STBox *box2); -extern double nad_tbox_tbox(const TBox *box1, const TBox *box2); -extern double nad_tfloat_float(const Temporal *temp, double d); -extern double nad_tfloat_tfloat(const Temporal *temp1, const Temporal *temp2); -extern int nad_tint_int(const Temporal *temp, int i); -extern int nad_tint_tint(const Temporal *temp1, const Temporal *temp2); -extern double nad_tnumber_tbox(const Temporal *temp, const TBox *box); -extern double nad_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); -extern double nad_tpoint_stbox(const Temporal *temp, const STBox *box); -extern double nad_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); -extern TInstant *nai_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); -extern TInstant *nai_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); -extern bool shortestline_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, GSERIALIZED **result); -extern bool shortestline_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2, GSERIALIZED **result); +extern int temporal_cmp(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_eq(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_ge(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_gt(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_le(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_lt(const Temporal *temp1, const Temporal *temp2); +extern bool temporal_ne(const Temporal *temp1, const Temporal *temp2); @@ -1740,13 +1675,6 @@ extern bool ttext_ever_lt(const Temporal *temp, text *txt); -extern int temporal_cmp(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_eq(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_ge(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_gt(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_le(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_lt(const Temporal *temp1, const Temporal *temp2); -extern bool temporal_ne(const Temporal *temp1, const Temporal *temp2); extern Temporal *teq_bool_tbool(bool b, const Temporal *temp); extern Temporal *teq_float_tfloat(double d, const Temporal *temp); extern Temporal *teq_int_tint(int i, const Temporal *temp); @@ -1798,6 +1726,87 @@ extern Temporal *tne_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); extern Temporal *tne_tint_int(const Temporal *temp, int i); extern Temporal *tne_ttext_text(const Temporal *temp, const text *txt); + + + + +extern Temporal *tand_bool_tbool(bool b, const Temporal *temp); +extern Temporal *tand_tbool_bool(const Temporal *temp, bool b); +extern Temporal *tand_tbool_tbool(const Temporal *temp1, const Temporal *temp2); +extern SpanSet *tbool_when_true(const Temporal *temp); +extern Temporal *tnot_tbool(const Temporal *temp); +extern Temporal *tor_bool_tbool(bool b, const Temporal *temp); +extern Temporal *tor_tbool_bool(const Temporal *temp, bool b); +extern Temporal *tor_tbool_tbool(const Temporal *temp1, const Temporal *temp2); + + + + + +extern Temporal *add_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *add_int_tint(int i, const Temporal *tnumber); +extern Temporal *add_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *add_tint_int(const Temporal *tnumber, int i); +extern Temporal *add_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); +extern Temporal *div_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *div_int_tint(int i, const Temporal *tnumber); +extern Temporal *div_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *div_tint_int(const Temporal *tnumber, int i); +extern Temporal *div_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); +extern double float_degrees(double value, bool normalize); +extern Temporal *mult_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *mult_int_tint(int i, const Temporal *tnumber); +extern Temporal *mult_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *mult_tint_int(const Temporal *tnumber, int i); +extern Temporal *mult_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); +extern Temporal *sub_float_tfloat(double d, const Temporal *tnumber); +extern Temporal *sub_int_tint(int i, const Temporal *tnumber); +extern Temporal *sub_tfloat_float(const Temporal *tnumber, double d); +extern Temporal *sub_tint_int(const Temporal *tnumber, int i); +extern Temporal *sub_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); +extern Temporal *tfloat_round(const Temporal *temp, int maxdd); +extern Temporal *tfloat_degrees(const Temporal *temp, bool normalize); +extern Temporal *tfloat_derivative(const Temporal *temp); +extern Temporal *tfloat_radians(const Temporal *temp); +extern Temporal *tnumber_abs(const Temporal *temp); +extern Temporal *tnumber_angular_difference(const Temporal *temp); +extern Temporal *tnumber_delta_value(const Temporal *temp); + + + + + +extern Temporal *textcat_text_ttext(const text *txt, const Temporal *temp); +extern Temporal *textcat_ttext_text(const Temporal *temp, const text *txt); +extern Temporal *textcat_ttext_ttext(const Temporal *temp1, const Temporal *temp2); +extern Temporal *ttext_upper(const Temporal *temp); +extern Temporal *ttext_lower(const Temporal *temp); + + + + + +extern Temporal *distance_tfloat_float(const Temporal *temp, double d); +extern Temporal *distance_tint_int(const Temporal *temp, int i); +extern Temporal *distance_tnumber_tnumber(const Temporal *temp1, const Temporal *temp2); +extern Temporal *distance_tpoint_point(const Temporal *temp, const GSERIALIZED *gs); +extern Temporal *distance_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); +extern double nad_stbox_geo(const STBox *box, const GSERIALIZED *gs); +extern double nad_stbox_stbox(const STBox *box1, const STBox *box2); +extern double nad_tbox_tbox(const TBox *box1, const TBox *box2); +extern double nad_tfloat_float(const Temporal *temp, double d); +extern double nad_tfloat_tfloat(const Temporal *temp1, const Temporal *temp2); +extern int nad_tint_int(const Temporal *temp, int i); +extern int nad_tint_tint(const Temporal *temp1, const Temporal *temp2); +extern double nad_tnumber_tbox(const Temporal *temp, const TBox *box); +extern double nad_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); +extern double nad_tpoint_stbox(const Temporal *temp, const STBox *box); +extern double nad_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); +extern TInstant *nai_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); +extern TInstant *nai_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); +extern bool shortestline_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, GSERIALIZED **result); +extern bool shortestline_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2, GSERIALIZED **result); + /***************************************************************************** Spatial functions for temporal point types *****************************************************************************/ @@ -1827,12 +1836,15 @@ extern GSERIALIZED *tpoint_trajectory(const Temporal *temp); extern STBox *geo_expand_space(const GSERIALIZED *gs, double d); -extern STBox *tpoint_expand_space(const Temporal *temp, double d); -extern Temporal *tgeompoint_to_tgeogpoint(const Temporal *temp); +Temporal *geo_to_tpoint(const GSERIALIZED *gs); extern Temporal *tgeogpoint_to_tgeompoint(const Temporal *temp); -extern Temporal *tpoint_round(const Temporal *temp, int maxdd); +extern Temporal *tgeompoint_to_tgeogpoint(const Temporal *temp); +bool tpoint_AsMVTGeom(const Temporal *temp, const STBox *bounds, int32_t extent, int32_t buffer, bool clip_geom, GSERIALIZED **gsarr, int64 **timesarr, int *count); +extern STBox *tpoint_expand_space(const Temporal *temp, double d); extern Temporal **tpoint_make_simple(const Temporal *temp, int *count); +extern Temporal *tpoint_round(const Temporal *temp, int maxdd); extern Temporal *tpoint_set_srid(const Temporal *temp, int32 srid); +bool tpoint_to_geo_meas(const Temporal *tpoint, const Temporal *measure, bool segmentize, GSERIALIZED **result); @@ -1846,6 +1858,11 @@ extern int edwithin_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2, extern int eintersects_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); extern int eintersects_tpoint_tpoint(const Temporal *temp1, const Temporal *temp2); extern int etouches_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs); + + + + + extern Temporal *tcontains_geo_tpoint(const GSERIALIZED *gs, const Temporal *temp, bool restr, bool atvalue); extern Temporal *tdisjoint_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, bool restr, bool atvalue); extern Temporal *tdwithin_tpoint_geo(const Temporal *temp, const GSERIALIZED *gs, double dist, bool restr, bool atvalue); @@ -1880,23 +1897,23 @@ extern GSERIALIZED *tpoint_twcentroid(const Temporal *temp); extern SkipList *ttext_tmax_transfn(SkipList *state, const Temporal *temp); extern SkipList *ttext_tmin_transfn(SkipList *state, const Temporal *temp); +/***************************************************************************** + * Analytics functions for temporal types + *****************************************************************************/ +Temporal *temporal_simplify_min_dist(const Temporal *temp, double dist); +Temporal *temporal_simplify_min_tdelta(const Temporal *temp, const Interval *mint); +Temporal *temporal_simplify_dp(const Temporal *temp, double eps_dist, bool synchronized); +Temporal *temporal_simplify_max_dist(const Temporal *temp, double eps_dist, bool synchronized); + + -extern double float_bucket(double value, double size, double origin); -extern Span *floatspan_bucket_list(const Span *bounds, double size, double origin, int *newcount); -extern int int_bucket(int value, int size, int origin); -extern Span *intspan_bucket_list(const Span *bounds, int size, int origin, int *newcount); -extern Span *period_bucket_list(const Span *bounds, const Interval *duration, TimestampTz origin, int *newcount); -extern STBox *stbox_tile_list(const STBox *bounds, double xsize, double ysize, double zsize, const Interval *duration, GSERIALIZED *sorigin, TimestampTz torigin, int **cellcount); -extern TBox *tbox_tile_list(const TBox *bounds, double xsize, const Interval *duration, double xorigin, TimestampTz torigin, int *rows, int *columns); -extern Temporal **temporal_time_split(Temporal *temp, Interval *duration, TimestampTz torigin, int *newcount); -extern Temporal **tfloat_value_split(Temporal *temp, double size, double origin, int *newcount); -extern Temporal **tfloat_value_time_split(Temporal *temp, double size, double vorigin, Interval *duration, TimestampTz torigin, int *newcount); -extern TimestampTz timestamptz_bucket(TimestampTz timestamp, const Interval *duration, TimestampTz origin); -extern Temporal **tint_value_split(Temporal *temp, int size, int origin, int *newcount); -extern Temporal **tint_value_time_split(Temporal *temp, int size, int vorigin, Interval *duration, TimestampTz torigin, int *newcount); + + +extern Temporal *temporal_tprecision(const Temporal *temp, const Interval *duration, TimestampTz origin); +extern Temporal *temporal_tsample(const Temporal *temp, const Interval *duration, TimestampTz origin); @@ -1912,13 +1929,19 @@ extern double temporal_hausdorff_distance(const Temporal *temp1, const Temporal -Temporal *geo_to_tpoint(const GSERIALIZED *gs); -Temporal *temporal_simplify_min_dist(const Temporal *temp, double dist); -Temporal *temporal_simplify_min_tdelta(const Temporal *temp, const Interval *mint); -Temporal *temporal_simplify_dp(const Temporal *temp, double eps_dist, bool synchronized); -Temporal *temporal_simplify_max_dist(const Temporal *temp, double eps_dist, bool synchronized); -bool tpoint_AsMVTGeom(const Temporal *temp, const STBox *bounds, int32_t extent, int32_t buffer, bool clip_geom, GSERIALIZED **gsarr, int64 **timesarr, int *count); -bool tpoint_to_geo_meas(const Temporal *tpoint, const Temporal *measure, bool segmentize, GSERIALIZED **result); +extern double float_bucket(double value, double size, double origin); +extern Span *floatspan_bucket_list(const Span *bounds, double size, double origin, int *newcount); +extern int int_bucket(int value, int size, int origin); +extern Span *intspan_bucket_list(const Span *bounds, int size, int origin, int *newcount); +extern Span *period_bucket_list(const Span *bounds, const Interval *duration, TimestampTz origin, int *newcount); +extern STBox *stbox_tile_list(const STBox *bounds, double xsize, double ysize, double zsize, const Interval *duration, GSERIALIZED *sorigin, TimestampTz torigin, int **cellcount); +extern TBox *tbox_tile_list(const TBox *bounds, double xsize, const Interval *duration, double xorigin, TimestampTz torigin, int *rows, int *columns); +extern Temporal **temporal_time_split(Temporal *temp, Interval *duration, TimestampTz torigin, int *newcount); +extern Temporal **tfloat_value_split(Temporal *temp, double size, double origin, int *newcount); +extern Temporal **tfloat_value_time_split(Temporal *temp, double size, double vorigin, Interval *duration, TimestampTz torigin, int *newcount); +extern TimestampTz timestamptz_bucket(TimestampTz timestamp, const Interval *duration, TimestampTz origin); +extern Temporal **tint_value_split(Temporal *temp, int size, int origin, int *newcount); +extern Temporal **tint_value_time_split(Temporal *temp, int size, int vorigin, Interval *duration, TimestampTz torigin, int *newcount); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 40606031..9afd35e6 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -1750,1666 +1750,1504 @@ def timestampset_shift_scale(ts: 'const Set *', shift: "Optional['const Interval return result if result != _ffi.NULL else None -def adjacent_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': +def intersection_bigintset_bigint(s: 'const Set *', i: int) -> 'int64': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintset_bigint(s_converted, i_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_bigintspan_bigint(s: 'const Span *', i: int) -> 'int64': s_converted = _ffi.cast('const Span *', s) i_converted = _ffi.cast('int64', i) - result = _lib.adjacent_bigintspan_bigint(s_converted, i_converted) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintspan_bigint(s_converted, i_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': +def intersection_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'int64': ss_converted = _ffi.cast('const SpanSet *', ss) i_converted = _ffi.cast('int64', i) - result = _lib.adjacent_bigintspanset_bigint(ss_converted, i_converted) + out_result = _ffi.new('int64 *') + result = _lib.intersection_bigintspanset_bigint(ss_converted, i_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_floatspan_float(s: 'const Span *', d: float) -> 'bool': +def intersection_floatset_float(s: 'const Set *', d: float) -> 'double': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('double *') + result = _lib.intersection_floatset_float(s_converted, d, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_floatspan_float(s: 'const Span *', d: float) -> 'double': s_converted = _ffi.cast('const Span *', s) - result = _lib.adjacent_floatspan_float(s_converted, d) + out_result = _ffi.new('double *') + result = _lib.intersection_floatspan_float(s_converted, d, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': +def intersection_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.adjacent_floatspanset_float(ss_converted, d) + out_result = _ffi.new('double *') + result = _lib.intersection_floatspanset_float(ss_converted, d, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_intspan_int(s: 'const Span *', i: int) -> 'bool': +def intersection_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'GSERIALIZED **': + s_converted = _ffi.cast('const Set *', s) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.intersection_geoset_geo(s_converted, gs_converted, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def intersection_intset_int(s: 'const Set *', i: int) -> 'int': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('int *') + result = _lib.intersection_intset_int(s_converted, i, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_intspan_int(s: 'const Span *', i: int) -> 'int': s_converted = _ffi.cast('const Span *', s) - result = _lib.adjacent_intspan_int(s_converted, i) + out_result = _ffi.new('int *') + result = _lib.intersection_intspan_int(s_converted, i, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': +def intersection_intspanset_int(ss: 'const SpanSet *', i: int) -> 'int': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.adjacent_intspanset_int(ss_converted, i) + out_result = _ffi.new('int *') + result = _lib.intersection_intspanset_int(ss_converted, i, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_period_timestamp(p: 'const Span *', t: int) -> 'bool': - p_converted = _ffi.cast('const Span *', p) +def intersection_period_timestamp(s: 'const Span *', t: int) -> int: + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.adjacent_period_timestamp(p_converted, t_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.intersection_period_timestamp(s_converted, t_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def adjacent_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) +def intersection_periodset_timestamp(ss: 'const SpanSet *', t: int) -> int: + ss_converted = _ffi.cast('const SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.adjacent_periodset_timestamp(ps_converted, t_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.intersection_periodset_timestamp(ss_converted, t_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def intersection_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.intersection_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def adjacent_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': +def intersection_span_span(s1: 'const Span *', s2: 'const Span *') -> 'Span *': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) - result = _lib.adjacent_span_span(s1_converted, s2_converted) + result = _lib.intersection_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def adjacent_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def intersection_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - result = _lib.adjacent_spanset_span(ss_converted, s_converted) + result = _lib.intersection_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def adjacent_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': +def intersection_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': ss1_converted = _ffi.cast('const SpanSet *', ss1) ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.adjacent_spanset_spanset(ss1_converted, ss2_converted) + result = _lib.intersection_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def contained_bigint_bigintset(i: int, s: 'const Set *') -> 'bool': +def intersection_textset_text(s: 'const Set *', txt: str) -> 'text **': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + out_result = _ffi.new('text **') + result = _lib.intersection_textset_text(s_converted, txt_converted, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def intersection_timestampset_timestamp(s: 'const Set *', t: int) -> int: + s_converted = _ffi.cast('const Set *', s) + t_converted = _ffi.cast('TimestampTz', t) + out_result = _ffi.new('TimestampTz *') + result = _lib.intersection_timestampset_timestamp(s_converted, t_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def minus_bigint_bigintset(i: int, s: 'const Set *') -> 'int64': i_converted = _ffi.cast('int64', i) s_converted = _ffi.cast('const Set *', s) - result = _lib.contained_bigint_bigintset(i_converted, s_converted) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintset(i_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': +def minus_bigint_bigintspan(i: int, s: 'const Span *') -> 'int64': i_converted = _ffi.cast('int64', i) s_converted = _ffi.cast('const Span *', s) - result = _lib.contained_bigint_bigintspan(i_converted, s_converted) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintspan(i_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'bool': +def minus_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'int64': i_converted = _ffi.cast('int64', i) ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contained_bigint_bigintspanset(i_converted, ss_converted) + out_result = _ffi.new('int64 *') + result = _lib.minus_bigint_bigintspanset(i_converted, ss_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_float_floatset(d: float, s: 'const Set *') -> 'bool': +def minus_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) - result = _lib.contained_float_floatset(d, s_converted) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintset_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def contained_float_floatspan(d: float, s: 'const Span *') -> 'bool': +def minus_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - result = _lib.contained_float_floatspan(d, s_converted) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintspan_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def contained_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'bool': +def minus_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contained_float_floatspanset(d, ss_converted) + i_converted = _ffi.cast('int64', i) + result = _lib.minus_bigintspanset_bigint(ss_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def contained_int_intset(i: int, s: 'const Set *') -> 'bool': +def minus_float_floatset(d: float, s: 'const Set *') -> 'double': s_converted = _ffi.cast('const Set *', s) - result = _lib.contained_int_intset(i, s_converted) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatset(d, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_int_intspan(i: int, s: 'const Span *') -> 'bool': +def minus_float_floatspan(d: float, s: 'const Span *') -> 'double': s_converted = _ffi.cast('const Span *', s) - result = _lib.contained_int_intspan(i, s_converted) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatspan(d, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_int_intspanset(i: int, ss: 'const SpanSet *') -> 'bool': +def minus_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'double': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contained_int_intspanset(i, ss_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def contained_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.contained_set_set(s1_converted, s2_converted) + out_result = _ffi.new('double *') + result = _lib.minus_float_floatspanset(d, ss_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.contained_span_span(s1_converted, s2_converted) +def minus_floatset_float(s: 'const Set *', d: float) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.minus_floatset_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def contained_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': +def minus_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contained_span_spanset(s_converted, ss_converted) + result = _lib.minus_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def contained_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def minus_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - s_converted = _ffi.cast('const Span *', s) - result = _lib.contained_spanset_span(ss_converted, s_converted) + result = _lib.minus_floatspanset_float(ss_converted, d) _check_error() return result if result != _ffi.NULL else None -def contained_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.contained_spanset_spanset(ss1_converted, ss2_converted) +def minus_geo_geoset(gs: 'const GSERIALIZED *', s: 'const Set *') -> 'GSERIALIZED **': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.minus_geo_geoset(gs_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result if out_result != _ffi.NULL else None + return None -def contained_text_textset(txt: str, s: 'const Set *') -> 'bool': - txt_converted = cstring2text(txt) +def minus_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'Set *': s_converted = _ffi.cast('const Set *', s) - result = _lib.contained_text_textset(txt_converted, s_converted) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.minus_geoset_geo(s_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def contained_timestamp_period(t: int, p: 'const Span *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - p_converted = _ffi.cast('const Span *', p) - result = _lib.contained_timestamp_period(t_converted, p_converted) +def minus_int_intset(i: int, s: 'const Set *') -> 'int': + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('int *') + result = _lib.minus_int_intset(i, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_timestamp_periodset(t: int, ss: 'const SpanSet *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contained_timestamp_periodset(t_converted, ss_converted) +def minus_int_intspan(i: int, s: 'const Span *') -> 'int': + s_converted = _ffi.cast('const Span *', s) + out_result = _ffi.new('int *') + result = _lib.minus_int_intspan(i, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contained_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.contained_timestamp_timestampset(t_converted, ts_converted) +def minus_int_intspanset(i: int, ss: 'const SpanSet *') -> 'int': + ss_converted = _ffi.cast('const SpanSet *', ss) + out_result = _ffi.new('int *') + result = _lib.minus_int_intspanset(i, ss_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contains_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': +def minus_intset_int(s: 'const Set *', i: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.contains_bigintset_bigint(s_converted, i_converted) + result = _lib.minus_intset_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def contains_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': +def minus_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.contains_bigintspan_bigint(s_converted, i_converted) + result = _lib.minus_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def contains_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': +def minus_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - i_converted = _ffi.cast('int64', i) - result = _lib.contains_bigintspanset_bigint(ss_converted, i_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def contains_floatset_float(s: 'const Set *', d: float) -> 'bool': - s_converted = _ffi.cast('const Set *', s) - result = _lib.contains_floatset_float(s_converted, d) + result = _lib.minus_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def contains_floatspan_float(s: 'const Span *', d: float) -> 'bool': +def minus_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - result = _lib.contains_floatspan_float(s_converted, d) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.minus_period_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def contains_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': +def minus_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contains_floatspanset_float(ss_converted, d) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.minus_periodset_timestamp(ss_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def contains_intset_int(s: 'const Set *', i: int) -> 'bool': - s_converted = _ffi.cast('const Set *', s) - result = _lib.contains_intset_int(s_converted, i) +def minus_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.minus_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def contains_intspan_int(s: 'const Span *', i: int) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.contains_intspan_int(s_converted, i) +def minus_span_span(s1: 'const Span *', s2: 'const Span *') -> 'SpanSet *': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.minus_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def contains_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': +def minus_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contains_intspanset_int(ss_converted, i) + result = _lib.minus_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def contains_period_timestamp(p: 'const Span *', t: int) -> 'bool': - p_converted = _ffi.cast('const Span *', p) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.contains_period_timestamp(p_converted, t_converted) +def minus_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + s_converted = _ffi.cast('const Span *', s) + result = _lib.minus_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def contains_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.contains_periodset_timestamp(ps_converted, t_converted) +def minus_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.minus_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def contains_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.contains_set_set(s1_converted, s2_converted) +def minus_text_textset(txt: str, s: 'const Set *') -> 'text **': + txt_converted = cstring2text(txt) + s_converted = _ffi.cast('const Set *', s) + out_result = _ffi.new('text **') + result = _lib.minus_text_textset(txt_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result if out_result != _ffi.NULL else None + return None -def contains_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.contains_span_span(s1_converted, s2_converted) +def minus_textset_text(s: 'const Set *', txt: str) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.minus_textset_text(s_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def contains_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': +def minus_timestamp_period(t: int, s: 'const Span *') -> int: + t_converted = _ffi.cast('TimestampTz', t) s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.contains_span_spanset(s_converted, ss_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.minus_timestamp_period(t_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contains_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def minus_timestamp_periodset(t: int, ss: 'const SpanSet *') -> int: + t_converted = _ffi.cast('TimestampTz', t) ss_converted = _ffi.cast('const SpanSet *', ss) - s_converted = _ffi.cast('const Span *', s) - result = _lib.contains_spanset_span(ss_converted, s_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def contains_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.contains_spanset_spanset(ss1_converted, ss2_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.minus_timestamp_periodset(t_converted, ss_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contains_textset_text(s: 'const Set *', t: str) -> 'bool': +def minus_timestamp_timestampset(t: int, s: 'const Set *') -> int: + t_converted = _ffi.cast('TimestampTz', t) s_converted = _ffi.cast('const Set *', s) - t_converted = cstring2text(t) - result = _lib.contains_textset_text(s_converted, t_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.minus_timestamp_timestampset(t_converted, s_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def contains_timestampset_timestamp(s: 'const Set *', t: int) -> 'bool': +def minus_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.contains_timestampset_timestamp(s_converted, t_converted) + result = _lib.minus_timestampset_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overlaps_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.overlaps_set_set(s1_converted, s2_converted) +def union_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintset_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def overlaps_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.overlaps_span_span(s1_converted, s2_converted) +def union_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintspan_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def overlaps_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def union_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - s_converted = _ffi.cast('const Span *', s) - result = _lib.overlaps_spanset_span(ss_converted, s_converted) + i_converted = _ffi.cast('int64', i) + result = _lib.union_bigintspanset_bigint(ss_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def overlaps_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.overlaps_spanset_spanset(ss1_converted, ss2_converted) +def union_floatset_float(s: 'const Set *', d: float) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.union_floatset_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def after_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.after_timestamp_timestampset(t_converted, ts_converted) +def union_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.union_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def before_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.before_periodset_timestamp(ps_converted, t_converted) +def union_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.union_floatspanset_float(ss_converted, d) _check_error() return result if result != _ffi.NULL else None -def before_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.before_timestamp_timestampset(t_converted, ts_converted) +def union_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.union_geoset_geo(s_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def left_float_floatspan(d: float, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.left_float_floatspan(d, s_converted) +def union_intset_int(s: 'const Set *', i: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.union_intset_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def left_floatspan_float(s: 'const Span *', d: float) -> 'bool': +def union_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - result = _lib.left_floatspan_float(s_converted, d) + result = _lib.union_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def left_int_intspan(i: int, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.left_int_intspan(i, s_converted) +def union_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.union_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def left_intspan_int(s: 'const Span *', i: int) -> 'bool': +def union_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': s_converted = _ffi.cast('const Span *', s) - result = _lib.left_intspan_int(s_converted, i) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.union_period_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def left_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.left_set_set(s1_converted, s2_converted) +def union_periodset_timestamp(ss: 'SpanSet *', t: int) -> 'SpanSet *': + ss_converted = _ffi.cast('SpanSet *', ss) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.union_periodset_timestamp(ss_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def left_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.left_span_span(s1_converted, s2_converted) +def union_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.union_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def left_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.left_span_spanset(s_converted, ss_converted) +def union_span_span(s1: 'const Span *', s2: 'const Span *') -> 'SpanSet *': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.union_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def left_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def union_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - result = _lib.left_spanset_span(ss_converted, s_converted) + result = _lib.union_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def left_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': +def union_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': ss1_converted = _ffi.cast('const SpanSet *', ss1) ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.left_spanset_spanset(ss1_converted, ss2_converted) + result = _lib.union_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_period_timestamp(p: 'const Span *', t: int) -> 'bool': - p_converted = _ffi.cast('const Span *', p) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overafter_period_timestamp(p_converted, t_converted) +def union_textset_text(s: 'const Set *', txt: str) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.union_textset_text(s_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overafter_periodset_timestamp(ps_converted, t_converted) +def union_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + t_converted = _ffi.cast('const TimestampTz', t) + result = _lib.union_timestampset_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_timestamp_period(t: int, p: 'const Span *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - p_converted = _ffi.cast('const Span *', p) - result = _lib.overafter_timestamp_period(t_converted, p_converted) +def adjacent_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.adjacent_bigintspan_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_timestamp_periodset(t: int, ps: 'const SpanSet *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.overafter_timestamp_periodset(t_converted, ps_converted) +def adjacent_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.adjacent_bigintspanset_bigint(ss_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.overafter_timestamp_timestampset(t_converted, ts_converted) +def adjacent_floatspan_float(s: 'const Span *', d: float) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.adjacent_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def overbefore_period_timestamp(p: 'const Span *', t: int) -> 'bool': - p_converted = _ffi.cast('const Span *', p) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overbefore_period_timestamp(p_converted, t_converted) +def adjacent_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.adjacent_floatspanset_float(ss_converted, d) _check_error() return result if result != _ffi.NULL else None -def overbefore_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overbefore_periodset_timestamp(ps_converted, t_converted) +def adjacent_intspan_int(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.adjacent_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def overbefore_timestamp_period(t: int, p: 'const Span *') -> 'bool': - t_converted = _ffi.cast('TimestampTz', t) - p_converted = _ffi.cast('const Span *', p) - result = _lib.overbefore_timestamp_period(t_converted, p_converted) +def adjacent_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.adjacent_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def overbefore_timestamp_periodset(t: int, ps: 'const SpanSet *') -> 'bool': +def adjacent_period_timestamp(p: 'const Span *', t: int) -> 'bool': + p_converted = _ffi.cast('const Span *', p) t_converted = _ffi.cast('TimestampTz', t) - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.overbefore_timestamp_periodset(t_converted, ps_converted) + result = _lib.adjacent_period_timestamp(p_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overbefore_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': +def adjacent_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': + ps_converted = _ffi.cast('const SpanSet *', ps) t_converted = _ffi.cast('TimestampTz', t) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.overbefore_timestamp_timestampset(t_converted, ts_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_float_floatspan(d: float, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overleft_float_floatspan(d, s_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_floatspan_float(s: 'const Span *', d: float) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overleft_floatspan_float(s_converted, d) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_int_intspan(i: int, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overleft_int_intspan(i, s_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_intspan_int(s: 'const Span *', i: int) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overleft_intspan_int(s_converted, i) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.overleft_set_set(s1_converted, s2_converted) + result = _lib.adjacent_periodset_timestamp(ps_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overleft_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': +def adjacent_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) - result = _lib.overleft_span_span(s1_converted, s2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overleft_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.overleft_span_spanset(s_converted, ss_converted) + result = _lib.adjacent_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def overleft_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def adjacent_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - result = _lib.overleft_spanset_span(ss_converted, s_converted) + result = _lib.adjacent_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def overleft_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': +def adjacent_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': ss1_converted = _ffi.cast('const SpanSet *', ss1) ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.overleft_spanset_spanset(ss1_converted, ss2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overright_float_floatspan(d: float, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overright_float_floatspan(d, s_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overright_floatspan_float(s: 'const Span *', d: float) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overright_floatspan_float(s_converted, d) + result = _lib.adjacent_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def overright_int_intspan(i: int, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.overright_int_intspan(i, s_converted) +def contained_bigint_bigintset(i: int, s: 'const Set *') -> 'bool': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Set *', s) + result = _lib.contained_bigint_bigintset(i_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def overright_intspan_int(s: 'const Span *', i: int) -> 'bool': +def contained_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': + i_converted = _ffi.cast('int64', i) s_converted = _ffi.cast('const Span *', s) - result = _lib.overright_intspan_int(s_converted, i) + result = _lib.contained_bigint_bigintspan(i_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def overright_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.overright_set_set(s1_converted, s2_converted) +def contained_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'bool': + i_converted = _ffi.cast('int64', i) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contained_bigint_bigintspanset(i_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def overright_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.overright_span_span(s1_converted, s2_converted) +def contained_float_floatset(d: float, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.contained_float_floatset(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def overright_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': +def contained_float_floatspan(d: float, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.overright_span_spanset(s_converted, ss_converted) + result = _lib.contained_float_floatspan(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def overright_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def contained_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) - s_converted = _ffi.cast('const Span *', s) - result = _lib.overright_spanset_span(ss_converted, s_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overright_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.overright_spanset_spanset(ss1_converted, ss2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def right_float_floatspan(d: float, s: 'const Span *') -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.right_float_floatspan(d, s_converted) + result = _lib.contained_float_floatspanset(d, ss_converted) _check_error() return result if result != _ffi.NULL else None -def right_floatspan_float(s: 'const Span *', d: float) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.right_floatspan_float(s_converted, d) +def contained_int_intset(i: int, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.contained_int_intset(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def right_int_intspan(i: int, s: 'const Span *') -> 'bool': +def contained_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.right_int_intspan(i, s_converted) + result = _lib.contained_int_intspan(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def right_intspan_int(s: 'const Span *', i: int) -> 'bool': - s_converted = _ffi.cast('const Span *', s) - result = _lib.right_intspan_int(s_converted, i) +def contained_int_intspanset(i: int, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contained_int_intspanset(i, ss_converted) _check_error() return result if result != _ffi.NULL else None -def right_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': +def contained_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) - result = _lib.right_set_set(s1_converted, s2_converted) + result = _lib.contained_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def right_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': +def contained_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) - result = _lib.right_span_span(s1_converted, s2_converted) + result = _lib.contained_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def right_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': +def contained_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': s_converted = _ffi.cast('const Span *', s) ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.right_span_spanset(s_converted, ss_converted) + result = _lib.contained_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def right_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': +def contained_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - result = _lib.right_spanset_span(ss_converted, s_converted) + result = _lib.contained_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def right_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': +def contained_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': ss1_converted = _ffi.cast('const SpanSet *', ss1) ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.right_spanset_spanset(ss1_converted, ss2_converted) + result = _lib.contained_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def intersection_bigintset_bigint(s: 'const Set *', i: int) -> 'int64': - s_converted = _ffi.cast('const Set *', s) - i_converted = _ffi.cast('int64', i) - out_result = _ffi.new('int64 *') - result = _lib.intersection_bigintset_bigint(s_converted, i_converted, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_bigintspan_bigint(s: 'const Span *', i: int) -> 'int64': - s_converted = _ffi.cast('const Span *', s) - i_converted = _ffi.cast('int64', i) - out_result = _ffi.new('int64 *') - result = _lib.intersection_bigintspan_bigint(s_converted, i_converted, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'int64': - ss_converted = _ffi.cast('const SpanSet *', ss) - i_converted = _ffi.cast('int64', i) - out_result = _ffi.new('int64 *') - result = _lib.intersection_bigintspanset_bigint(ss_converted, i_converted, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_floatset_float(s: 'const Set *', d: float) -> 'double': - s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('double *') - result = _lib.intersection_floatset_float(s_converted, d, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_floatspan_float(s: 'const Span *', d: float) -> 'double': - s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('double *') - result = _lib.intersection_floatspan_float(s_converted, d, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('double *') - result = _lib.intersection_floatspanset_float(ss_converted, d, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'GSERIALIZED **': - s_converted = _ffi.cast('const Set *', s) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - out_result = _ffi.new('GSERIALIZED **') - result = _lib.intersection_geoset_geo(s_converted, gs_converted, out_result) - _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None - - -def intersection_intset_int(s: 'const Set *', i: int) -> 'int': +def contained_text_textset(txt: str, s: 'const Set *') -> 'bool': + txt_converted = cstring2text(txt) s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('int *') - result = _lib.intersection_intset_int(s_converted, i, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_intspan_int(s: 'const Span *', i: int) -> 'int': - s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('int *') - result = _lib.intersection_intspan_int(s_converted, i, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def intersection_intspanset_int(ss: 'const SpanSet *', i: int) -> 'int': - ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('int *') - result = _lib.intersection_intspanset_int(ss_converted, i, out_result) + result = _lib.contained_text_textset(txt_converted, s_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def intersection_period_timestamp(s: 'const Span *', t: int) -> int: - s_converted = _ffi.cast('const Span *', s) +def contained_timestamp_period(t: int, p: 'const Span *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) - out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_period_timestamp(s_converted, t_converted, out_result) + p_converted = _ffi.cast('const Span *', p) + result = _lib.contained_timestamp_period(t_converted, p_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def intersection_periodset_timestamp(ss: 'const SpanSet *', t: int) -> int: - ss_converted = _ffi.cast('const SpanSet *', ss) +def contained_timestamp_periodset(t: int, ss: 'const SpanSet *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) - out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_periodset_timestamp(ss_converted, t_converted, out_result) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contained_timestamp_periodset(t_converted, ss_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def intersection_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.intersection_set_set(s1_converted, s2_converted) +def contained_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.contained_timestamp_timestampset(t_converted, ts_converted) _check_error() return result if result != _ffi.NULL else None -def intersection_span_span(s1: 'const Span *', s2: 'const Span *') -> 'Span *': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.intersection_span_span(s1_converted, s2_converted) +def contains_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.contains_bigintset_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def intersection_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) +def contains_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.intersection_spanset_span(ss_converted, s_converted) + i_converted = _ffi.cast('int64', i) + result = _lib.contains_bigintspan_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def intersection_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.intersection_spanset_spanset(ss1_converted, ss2_converted) +def contains_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.contains_bigintspanset_bigint(ss_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def intersection_textset_text(s: 'const Set *', txt: str) -> 'text **': - s_converted = _ffi.cast('const Set *', s) - txt_converted = cstring2text(txt) - out_result = _ffi.new('text **') - result = _lib.intersection_textset_text(s_converted, txt_converted, out_result) - _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None - - -def intersection_timestampset_timestamp(s: 'const Set *', t: int) -> int: - s_converted = _ffi.cast('const Set *', s) - t_converted = _ffi.cast('TimestampTz', t) - out_result = _ffi.new('TimestampTz *') - result = _lib.intersection_timestampset_timestamp(s_converted, t_converted, out_result) - _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None - - -def minus_bigint_bigintset(i: int, s: 'const Set *') -> 'int64': - i_converted = _ffi.cast('int64', i) +def contains_floatset_float(s: 'const Set *', d: float) -> 'bool': s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('int64 *') - result = _lib.minus_bigint_bigintset(i_converted, s_converted, out_result) + result = _lib.contains_floatset_float(s_converted, d) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_bigint_bigintspan(i: int, s: 'const Span *') -> 'int64': - i_converted = _ffi.cast('int64', i) +def contains_floatspan_float(s: 'const Span *', d: float) -> 'bool': s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('int64 *') - result = _lib.minus_bigint_bigintspan(i_converted, s_converted, out_result) + result = _lib.contains_floatspan_float(s_converted, d) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'int64': - i_converted = _ffi.cast('int64', i) +def contains_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('int64 *') - result = _lib.minus_bigint_bigintspanset(i_converted, ss_converted, out_result) + result = _lib.contains_floatspanset_float(ss_converted, d) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': +def contains_intset_int(s: 'const Set *', i: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.minus_bigintset_bigint(s_converted, i_converted) + result = _lib.contains_intset_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def minus_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': +def contains_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.minus_bigintspan_bigint(s_converted, i_converted) + result = _lib.contains_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def minus_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': +def contains_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) - i_converted = _ffi.cast('int64', i) - result = _lib.minus_bigintspanset_bigint(ss_converted, i_converted) + result = _lib.contains_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def minus_float_floatset(d: float, s: 'const Set *') -> 'double': - s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('double *') - result = _lib.minus_float_floatset(d, s_converted, out_result) +def contains_period_timestamp(p: 'const Span *', t: int) -> 'bool': + p_converted = _ffi.cast('const Span *', p) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.contains_period_timestamp(p_converted, t_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_float_floatspan(d: float, s: 'const Span *') -> 'double': - s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('double *') - result = _lib.minus_float_floatspan(d, s_converted, out_result) +def contains_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': + ps_converted = _ffi.cast('const SpanSet *', ps) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.contains_periodset_timestamp(ps_converted, t_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('double *') - result = _lib.minus_float_floatspanset(d, ss_converted, out_result) +def contains_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.contains_set_set(s1_converted, s2_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_floatset_float(s: 'const Set *', d: float) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - result = _lib.minus_floatset_float(s_converted, d) +def contains_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.contains_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def minus_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': +def contains_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.minus_floatspan_float(s_converted, d) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.contains_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def minus_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': +def contains_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.minus_floatspanset_float(ss_converted, d) + s_converted = _ffi.cast('const Span *', s) + result = _lib.contains_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def minus_geo_geoset(gs: 'const GSERIALIZED *', s: 'const Set *') -> 'GSERIALIZED **': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('GSERIALIZED **') - result = _lib.minus_geo_geoset(gs_converted, s_converted, out_result) +def contains_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.contains_spanset_spanset(ss1_converted, ss2_converted) _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'Set *': +def contains_textset_text(s: 'const Set *', t: str) -> 'bool': s_converted = _ffi.cast('const Set *', s) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.minus_geoset_geo(s_converted, gs_converted) + t_converted = cstring2text(t) + result = _lib.contains_textset_text(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def minus_int_intset(i: int, s: 'const Set *') -> 'int': +def contains_timestampset_timestamp(s: 'const Set *', t: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('int *') - result = _lib.minus_int_intset(i, s_converted, out_result) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.contains_timestampset_timestamp(s_converted, t_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_int_intspan(i: int, s: 'const Span *') -> 'int': - s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('int *') - result = _lib.minus_int_intspan(i, s_converted, out_result) +def overlaps_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.overlaps_set_set(s1_converted, s2_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_int_intspanset(i: int, ss: 'const SpanSet *') -> 'int': - ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('int *') - result = _lib.minus_int_intspanset(i, ss_converted, out_result) +def overlaps_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.overlaps_span_span(s1_converted, s2_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_intset_int(s: 'const Set *', i: int) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - result = _lib.minus_intset_int(s_converted, i) +def overlaps_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + s_converted = _ffi.cast('const Span *', s) + result = _lib.overlaps_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def minus_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': - s_converted = _ffi.cast('const Span *', s) - result = _lib.minus_intspan_int(s_converted, i) +def overlaps_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.overlaps_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def minus_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.minus_intspanset_int(ss_converted, i) +def after_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.after_timestamp_timestampset(t_converted, ts_converted) _check_error() return result if result != _ffi.NULL else None -def minus_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': - s_converted = _ffi.cast('const Span *', s) +def before_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': + ps_converted = _ffi.cast('const SpanSet *', ps) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_period_timestamp(s_converted, t_converted) + result = _lib.before_periodset_timestamp(ps_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def minus_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) +def before_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_periodset_timestamp(ss_converted, t_converted) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.before_timestamp_timestampset(t_converted, ts_converted) _check_error() return result if result != _ffi.NULL else None -def minus_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': - s1_converted = _ffi.cast('const Set *', s1) - s2_converted = _ffi.cast('const Set *', s2) - result = _lib.minus_set_set(s1_converted, s2_converted) +def left_float_floatspan(d: float, s: 'const Span *') -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.left_float_floatspan(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def minus_span_span(s1: 'const Span *', s2: 'const Span *') -> 'SpanSet *': - s1_converted = _ffi.cast('const Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.minus_span_span(s1_converted, s2_converted) +def left_floatspan_float(s: 'const Span *', d: float) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.left_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def minus_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'SpanSet *': +def left_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.minus_span_spanset(s_converted, ss_converted) + result = _lib.left_int_intspan(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def minus_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) +def left_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.minus_spanset_span(ss_converted, s_converted) + result = _lib.left_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def minus_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.minus_spanset_spanset(ss1_converted, ss2_converted) +def left_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.left_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def minus_text_textset(txt: str, s: 'const Set *') -> 'text **': - txt_converted = cstring2text(txt) - s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('text **') - result = _lib.minus_text_textset(txt_converted, s_converted, out_result) +def left_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.left_span_span(s1_converted, s2_converted) _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_textset_text(s: 'const Set *', txt: str) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - txt_converted = cstring2text(txt) - result = _lib.minus_textset_text(s_converted, txt_converted) +def left_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': + s_converted = _ffi.cast('const Span *', s) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.left_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def minus_timestamp_period(t: int, s: 'const Span *') -> int: - t_converted = _ffi.cast('TimestampTz', t) +def left_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - out_result = _ffi.new('TimestampTz *') - result = _lib.minus_timestamp_period(t_converted, s_converted, out_result) + result = _lib.left_spanset_span(ss_converted, s_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_timestamp_periodset(t: int, ss: 'const SpanSet *') -> int: - t_converted = _ffi.cast('TimestampTz', t) - ss_converted = _ffi.cast('const SpanSet *', ss) - out_result = _ffi.new('TimestampTz *') - result = _lib.minus_timestamp_periodset(t_converted, ss_converted, out_result) +def left_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.left_spanset_spanset(ss1_converted, ss2_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_timestamp_timestampset(t: int, s: 'const Set *') -> int: +def overafter_period_timestamp(p: 'const Span *', t: int) -> 'bool': + p_converted = _ffi.cast('const Span *', p) t_converted = _ffi.cast('TimestampTz', t) - s_converted = _ffi.cast('const Set *', s) - out_result = _ffi.new('TimestampTz *') - result = _lib.minus_timestamp_timestampset(t_converted, s_converted, out_result) + result = _lib.overafter_period_timestamp(p_converted, t_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def minus_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) +def overafter_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': + ps_converted = _ffi.cast('const SpanSet *', ps) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.minus_timestampset_timestamp(s_converted, t_converted) + result = _lib.overafter_periodset_timestamp(ps_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def union_bigintset_bigint(s: 'const Set *', i: int) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.union_bigintset_bigint(s_converted, i_converted) +def overafter_timestamp_period(t: int, p: 'const Span *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + p_converted = _ffi.cast('const Span *', p) + result = _lib.overafter_timestamp_period(t_converted, p_converted) _check_error() return result if result != _ffi.NULL else None -def union_bigintspan_bigint(s: 'const Span *', i: int) -> 'SpanSet *': - s_converted = _ffi.cast('const Span *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.union_bigintspan_bigint(s_converted, i_converted) +def overafter_timestamp_periodset(t: int, ps: 'const SpanSet *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.overafter_timestamp_periodset(t_converted, ps_converted) _check_error() return result if result != _ffi.NULL else None -def union_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) - i_converted = _ffi.cast('int64', i) - result = _lib.union_bigintspanset_bigint(ss_converted, i_converted) +def overafter_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.overafter_timestamp_timestampset(t_converted, ts_converted) _check_error() return result if result != _ffi.NULL else None -def union_floatset_float(s: 'const Set *', d: float) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - result = _lib.union_floatset_float(s_converted, d) +def overbefore_period_timestamp(p: 'const Span *', t: int) -> 'bool': + p_converted = _ffi.cast('const Span *', p) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.overbefore_period_timestamp(p_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def union_floatspan_float(s: 'const Span *', d: float) -> 'SpanSet *': - s_converted = _ffi.cast('const Span *', s) - result = _lib.union_floatspan_float(s_converted, d) +def overbefore_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': + ps_converted = _ffi.cast('const SpanSet *', ps) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.overbefore_periodset_timestamp(ps_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def union_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.union_floatspanset_float(ss_converted, d) +def overbefore_timestamp_period(t: int, p: 'const Span *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + p_converted = _ffi.cast('const Span *', p) + result = _lib.overbefore_timestamp_period(t_converted, p_converted) _check_error() return result if result != _ffi.NULL else None -def union_geoset_geo(s: 'const Set *', gs: 'const GSERIALIZED *') -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.union_geoset_geo(s_converted, gs_converted) +def overbefore_timestamp_periodset(t: int, ps: 'const SpanSet *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.overbefore_timestamp_periodset(t_converted, ps_converted) _check_error() return result if result != _ffi.NULL else None -def union_intset_int(s: 'const Set *', i: int) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - result = _lib.union_intset_int(s_converted, i) +def overbefore_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.overbefore_timestamp_timestampset(t_converted, ts_converted) _check_error() return result if result != _ffi.NULL else None -def union_intspan_int(s: 'const Span *', i: int) -> 'SpanSet *': +def overleft_float_floatspan(d: float, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.union_intspan_int(s_converted, i) + result = _lib.overleft_float_floatspan(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def union_intspanset_int(ss: 'const SpanSet *', i: int) -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.union_intspanset_int(ss_converted, i) +def overleft_floatspan_float(s: 'const Span *', d: float) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.overleft_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def union_period_timestamp(s: 'const Span *', t: int) -> 'SpanSet *': +def overleft_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.union_period_timestamp(s_converted, t_converted) + result = _lib.overleft_int_intspan(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def union_periodset_timestamp(ss: 'SpanSet *', t: int) -> 'SpanSet *': - ss_converted = _ffi.cast('SpanSet *', ss) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.union_periodset_timestamp(ss_converted, t_converted) +def overleft_intspan_int(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.overleft_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def union_set_set(s1: 'const Set *', s2: 'const Set *') -> 'Set *': +def overleft_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) - result = _lib.union_set_set(s1_converted, s2_converted) + result = _lib.overleft_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def union_span_span(s1: 'const Span *', s2: 'const Span *') -> 'SpanSet *': +def overleft_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) - result = _lib.union_span_span(s1_converted, s2_converted) + result = _lib.overleft_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def union_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'SpanSet *': +def overleft_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': + s_converted = _ffi.cast('const Span *', s) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overleft_span_spanset(s_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overleft_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) s_converted = _ffi.cast('const Span *', s) - result = _lib.union_spanset_span(ss_converted, s_converted) + result = _lib.overleft_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def union_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'SpanSet *': +def overleft_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': ss1_converted = _ffi.cast('const SpanSet *', ss1) ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.union_spanset_spanset(ss1_converted, ss2_converted) + result = _lib.overleft_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def union_textset_text(s: 'const Set *', txt: str) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - txt_converted = cstring2text(txt) - result = _lib.union_textset_text(s_converted, txt_converted) +def overright_float_floatspan(d: float, s: 'const Span *') -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.overright_float_floatspan(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def union_timestampset_timestamp(s: 'const Set *', t: int) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - t_converted = _ffi.cast('const TimestampTz', t) - result = _lib.union_timestampset_timestamp(s_converted, t_converted) +def overright_floatspan_float(s: 'const Span *', d: float) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.overright_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def distance_bigintset_bigint(s: 'const Set *', i: int) -> 'double': - s_converted = _ffi.cast('const Set *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.distance_bigintset_bigint(s_converted, i_converted) +def overright_int_intspan(i: int, s: 'const Span *') -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.overright_int_intspan(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def distance_bigintspan_bigint(s: 'const Span *', i: int) -> 'double': +def overright_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) - i_converted = _ffi.cast('int64', i) - result = _lib.distance_bigintspan_bigint(s_converted, i_converted) + result = _lib.overright_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def distance_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) - i_converted = _ffi.cast('int64', i) - result = _lib.distance_bigintspanset_bigint(ss_converted, i_converted) +def overright_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.overright_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_floatset_float(s: 'const Set *', d: float) -> 'double': - s_converted = _ffi.cast('const Set *', s) - result = _lib.distance_floatset_float(s_converted, d) +def overright_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.overright_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_floatspan_float(s: 'const Span *', d: float) -> 'double': +def overright_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.distance_floatspan_float(s_converted, d) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overright_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def distance_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': +def overright_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.distance_floatspanset_float(ss_converted, d) + s_converted = _ffi.cast('const Span *', s) + result = _lib.overright_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def distance_intset_int(s: 'const Set *', i: int) -> 'double': - s_converted = _ffi.cast('const Set *', s) - result = _lib.distance_intset_int(s_converted, i) +def overright_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.overright_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_intspan_int(s: 'const Span *', i: int) -> 'double': +def right_float_floatspan(d: float, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.distance_intspan_int(s_converted, i) + result = _lib.right_float_floatspan(d, s_converted) _check_error() return result if result != _ffi.NULL else None -def distance_intspanset_int(ss: 'const SpanSet *', i: int) -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.distance_intspanset_int(ss_converted, i) +def right_floatspan_float(s: 'const Span *', d: float) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.right_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def distance_period_timestamp(s: 'const Span *', t: int) -> 'double': +def right_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.distance_period_timestamp(s_converted, t_converted) + result = _lib.right_int_intspan(i, s_converted) _check_error() return result if result != _ffi.NULL else None -def distance_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.distance_periodset_timestamp(ss_converted, t_converted) +def right_intspan_int(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + result = _lib.right_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def distance_set_set(s1: 'const Set *', s2: 'const Set *') -> 'double': +def right_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) - result = _lib.distance_set_set(s1_converted, s2_converted) + result = _lib.right_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_span_span(s1: 'const Span *', s2: 'const Span *') -> 'double': +def right_span_span(s1: 'const Span *', s2: 'const Span *') -> 'bool': s1_converted = _ffi.cast('const Span *', s1) s2_converted = _ffi.cast('const Span *', s2) - result = _lib.distance_span_span(s1_converted, s2_converted) + result = _lib.right_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'double': - ss_converted = _ffi.cast('const SpanSet *', ss) +def right_span_spanset(s: 'const Span *', ss: 'const SpanSet *') -> 'bool': s_converted = _ffi.cast('const Span *', s) - result = _lib.distance_spanset_span(ss_converted, s_converted) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.right_span_spanset(s_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None -def distance_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'double': - ss1_converted = _ffi.cast('const SpanSet *', ss1) - ss2_converted = _ffi.cast('const SpanSet *', ss2) - result = _lib.distance_spanset_spanset(ss1_converted, ss2_converted) +def right_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + s_converted = _ffi.cast('const Span *', s) + result = _lib.right_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def distance_timestampset_timestamp(s: 'const Set *', t: int) -> 'double': - s_converted = _ffi.cast('const Set *', s) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.distance_timestampset_timestamp(s_converted, t_converted) +def right_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.right_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def bigint_extent_transfn(s: 'Span *', i: int) -> 'Span *': - s_converted = _ffi.cast('Span *', s) +def distance_bigintset_bigint(s: 'const Set *', i: int) -> 'double': + s_converted = _ffi.cast('const Set *', s) i_converted = _ffi.cast('int64', i) - result = _lib.bigint_extent_transfn(s_converted, i_converted) + result = _lib.distance_bigintset_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def bigint_union_transfn(state: 'Set *', i: int) -> 'Set *': - state_converted = _ffi.cast('Set *', state) +def distance_bigintspan_bigint(s: 'const Span *', i: int) -> 'double': + s_converted = _ffi.cast('const Span *', s) i_converted = _ffi.cast('int64', i) - result = _lib.bigint_union_transfn(state_converted, i_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def float_extent_transfn(s: 'Span *', d: float) -> 'Span *': - s_converted = _ffi.cast('Span *', s) - result = _lib.float_extent_transfn(s_converted, d) - _check_error() - return result if result != _ffi.NULL else None - - -def float_union_transfn(state: 'Set *', d: float) -> 'Set *': - state_converted = _ffi.cast('Set *', state) - result = _lib.float_union_transfn(state_converted, d) - _check_error() - return result if result != _ffi.NULL else None - - -def int_extent_transfn(s: 'Span *', i: int) -> 'Span *': - s_converted = _ffi.cast('Span *', s) - result = _lib.int_extent_transfn(s_converted, i) - _check_error() - return result if result != _ffi.NULL else None - - -def int_union_transfn(state: 'Set *', i: int) -> 'Set *': - state_converted = _ffi.cast('Set *', state) - result = _lib.int_union_transfn(state_converted, i) - _check_error() - return result if result != _ffi.NULL else None - - -def period_tcount_transfn(state: "Optional['SkipList *']", p: 'const Span *') -> 'SkipList *': - state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL - p_converted = _ffi.cast('const Span *', p) - result = _lib.period_tcount_transfn(state_converted, p_converted) + result = _lib.distance_bigintspan_bigint(s_converted, i_converted) _check_error() return result if result != _ffi.NULL else None - - -def periodset_tcount_transfn(state: "Optional['SkipList *']", ps: 'const SpanSet *') -> 'SkipList *': - state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.periodset_tcount_transfn(state_converted, ps_converted) + + +def distance_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.distance_bigintspanset_bigint(ss_converted, i_converted) _check_error() return result if result != _ffi.NULL else None -def set_extent_transfn(span: 'Span *', set: 'const Set *') -> 'Span *': - span_converted = _ffi.cast('Span *', span) - set_converted = _ffi.cast('const Set *', set) - result = _lib.set_extent_transfn(span_converted, set_converted) +def distance_floatset_float(s: 'const Set *', d: float) -> 'double': + s_converted = _ffi.cast('const Set *', s) + result = _lib.distance_floatset_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def set_union_finalfn(state: 'Set *') -> 'Set *': - state_converted = _ffi.cast('Set *', state) - result = _lib.set_union_finalfn(state_converted) +def distance_floatspan_float(s: 'const Span *', d: float) -> 'double': + s_converted = _ffi.cast('const Span *', s) + result = _lib.distance_floatspan_float(s_converted, d) _check_error() return result if result != _ffi.NULL else None -def set_union_transfn(state: 'Set *', set: 'Set *') -> 'Set *': - state_converted = _ffi.cast('Set *', state) - set_converted = _ffi.cast('Set *', set) - result = _lib.set_union_transfn(state_converted, set_converted) +def distance_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.distance_floatspanset_float(ss_converted, d) _check_error() return result if result != _ffi.NULL else None -def span_extent_transfn(s1: 'Span *', s2: 'const Span *') -> 'Span *': - s1_converted = _ffi.cast('Span *', s1) - s2_converted = _ffi.cast('const Span *', s2) - result = _lib.span_extent_transfn(s1_converted, s2_converted) +def distance_intset_int(s: 'const Set *', i: int) -> 'double': + s_converted = _ffi.cast('const Set *', s) + result = _lib.distance_intset_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def span_union_transfn(state: 'SpanSet *', span: 'const Span *') -> 'SpanSet *': - state_converted = _ffi.cast('SpanSet *', state) - span_converted = _ffi.cast('const Span *', span) - result = _lib.span_union_transfn(state_converted, span_converted) +def distance_intspan_int(s: 'const Span *', i: int) -> 'double': + s_converted = _ffi.cast('const Span *', s) + result = _lib.distance_intspan_int(s_converted, i) _check_error() return result if result != _ffi.NULL else None -def spanset_extent_transfn(s: 'Span *', ss: 'const SpanSet *') -> 'Span *': - s_converted = _ffi.cast('Span *', s) +def distance_intspanset_int(ss: 'const SpanSet *', i: int) -> 'double': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.spanset_extent_transfn(s_converted, ss_converted) + result = _lib.distance_intspanset_int(ss_converted, i) _check_error() return result if result != _ffi.NULL else None -def spanset_union_finalfn(state: 'SpanSet *') -> 'SpanSet *': - state_converted = _ffi.cast('SpanSet *', state) - result = _lib.spanset_union_finalfn(state_converted) +def distance_period_timestamp(s: 'const Span *', t: int) -> 'double': + s_converted = _ffi.cast('const Span *', s) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.distance_period_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def spanset_union_transfn(state: 'SpanSet *', ss: 'const SpanSet *') -> 'SpanSet *': - state_converted = _ffi.cast('SpanSet *', state) +def distance_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'double': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.spanset_union_transfn(state_converted, ss_converted) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.distance_periodset_timestamp(ss_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def text_union_transfn(state: 'Set *', txt: str) -> 'Set *': - state_converted = _ffi.cast('Set *', state) - txt_converted = cstring2text(txt) - result = _lib.text_union_transfn(state_converted, txt_converted) +def distance_set_set(s1: 'const Set *', s2: 'const Set *') -> 'double': + s1_converted = _ffi.cast('const Set *', s1) + s2_converted = _ffi.cast('const Set *', s2) + result = _lib.distance_set_set(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def timestamp_extent_transfn(p: "Optional['Span *']", t: int) -> 'Span *': - p_converted = _ffi.cast('Span *', p) if p is not None else _ffi.NULL - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.timestamp_extent_transfn(p_converted, t_converted) +def distance_span_span(s1: 'const Span *', s2: 'const Span *') -> 'double': + s1_converted = _ffi.cast('const Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.distance_span_span(s1_converted, s2_converted) _check_error() return result if result != _ffi.NULL else None -def timestamp_tcount_transfn(state: "Optional['SkipList *']", t: int) -> 'SkipList *': - state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.timestamp_tcount_transfn(state_converted, t_converted) +def distance_spanset_span(ss: 'const SpanSet *', s: 'const Span *') -> 'double': + ss_converted = _ffi.cast('const SpanSet *', ss) + s_converted = _ffi.cast('const Span *', s) + result = _lib.distance_spanset_span(ss_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def timestamp_union_transfn(state: 'Set *', t: int) -> 'Set *': - state_converted = _ffi.cast('Set *', state) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.timestamp_union_transfn(state_converted, t_converted) +def distance_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'double': + ss1_converted = _ffi.cast('const SpanSet *', ss1) + ss2_converted = _ffi.cast('const SpanSet *', ss2) + result = _lib.distance_spanset_spanset(ss1_converted, ss2_converted) _check_error() return result if result != _ffi.NULL else None -def timestampset_tcount_transfn(state: "Optional['SkipList *']", ts: 'const Set *') -> 'SkipList *': - state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.timestampset_tcount_transfn(state_converted, ts_converted) +def distance_timestampset_timestamp(s: 'const Set *', t: int) -> 'double': + s_converted = _ffi.cast('const Set *', s) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.distance_timestampset_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None @@ -3582,6 +3420,168 @@ def spanset_ne(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bool': return result if result != _ffi.NULL else None +def bigint_extent_transfn(s: 'Span *', i: int) -> 'Span *': + s_converted = _ffi.cast('Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.bigint_extent_transfn(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def bigint_union_transfn(state: 'Set *', i: int) -> 'Set *': + state_converted = _ffi.cast('Set *', state) + i_converted = _ffi.cast('int64', i) + result = _lib.bigint_union_transfn(state_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def float_extent_transfn(s: 'Span *', d: float) -> 'Span *': + s_converted = _ffi.cast('Span *', s) + result = _lib.float_extent_transfn(s_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + +def float_union_transfn(state: 'Set *', d: float) -> 'Set *': + state_converted = _ffi.cast('Set *', state) + result = _lib.float_union_transfn(state_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + +def int_extent_transfn(s: 'Span *', i: int) -> 'Span *': + s_converted = _ffi.cast('Span *', s) + result = _lib.int_extent_transfn(s_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + +def int_union_transfn(state: 'Set *', i: int) -> 'Set *': + state_converted = _ffi.cast('Set *', state) + result = _lib.int_union_transfn(state_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + +def period_tcount_transfn(state: "Optional['SkipList *']", p: 'const Span *') -> 'SkipList *': + state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL + p_converted = _ffi.cast('const Span *', p) + result = _lib.period_tcount_transfn(state_converted, p_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def periodset_tcount_transfn(state: "Optional['SkipList *']", ps: 'const SpanSet *') -> 'SkipList *': + state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.periodset_tcount_transfn(state_converted, ps_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def set_extent_transfn(span: 'Span *', set: 'const Set *') -> 'Span *': + span_converted = _ffi.cast('Span *', span) + set_converted = _ffi.cast('const Set *', set) + result = _lib.set_extent_transfn(span_converted, set_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def set_union_finalfn(state: 'Set *') -> 'Set *': + state_converted = _ffi.cast('Set *', state) + result = _lib.set_union_finalfn(state_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def set_union_transfn(state: 'Set *', set: 'Set *') -> 'Set *': + state_converted = _ffi.cast('Set *', state) + set_converted = _ffi.cast('Set *', set) + result = _lib.set_union_transfn(state_converted, set_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def span_extent_transfn(s1: 'Span *', s2: 'const Span *') -> 'Span *': + s1_converted = _ffi.cast('Span *', s1) + s2_converted = _ffi.cast('const Span *', s2) + result = _lib.span_extent_transfn(s1_converted, s2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def span_union_transfn(state: 'SpanSet *', span: 'const Span *') -> 'SpanSet *': + state_converted = _ffi.cast('SpanSet *', state) + span_converted = _ffi.cast('const Span *', span) + result = _lib.span_union_transfn(state_converted, span_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def spanset_extent_transfn(s: 'Span *', ss: 'const SpanSet *') -> 'Span *': + s_converted = _ffi.cast('Span *', s) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.spanset_extent_transfn(s_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def spanset_union_finalfn(state: 'SpanSet *') -> 'SpanSet *': + state_converted = _ffi.cast('SpanSet *', state) + result = _lib.spanset_union_finalfn(state_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def spanset_union_transfn(state: 'SpanSet *', ss: 'const SpanSet *') -> 'SpanSet *': + state_converted = _ffi.cast('SpanSet *', state) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.spanset_union_transfn(state_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def text_union_transfn(state: 'Set *', txt: str) -> 'Set *': + state_converted = _ffi.cast('Set *', state) + txt_converted = cstring2text(txt) + result = _lib.text_union_transfn(state_converted, txt_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def timestamp_extent_transfn(p: "Optional['Span *']", t: int) -> 'Span *': + p_converted = _ffi.cast('Span *', p) if p is not None else _ffi.NULL + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.timestamp_extent_transfn(p_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def timestamp_tcount_transfn(state: "Optional['SkipList *']", t: int) -> 'SkipList *': + state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.timestamp_tcount_transfn(state_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def timestamp_union_transfn(state: 'Set *', t: int) -> 'Set *': + state_converted = _ffi.cast('Set *', state) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.timestamp_union_transfn(state_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def timestampset_tcount_transfn(state: "Optional['SkipList *']", ts: 'const Set *') -> 'SkipList *': + state_converted = _ffi.cast('SkipList *', state) if state is not None else _ffi.NULL + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.timestampset_tcount_transfn(state_converted, ts_converted) + _check_error() + return result if result != _ffi.NULL else None + + def tbox_in(string: str) -> 'TBox *': string_converted = string.encode('utf-8') result = _lib.tbox_in(string_converted) @@ -3678,123 +3678,118 @@ def stbox_out(box: 'const STBox *', maxdd: int) -> str: return result if result != _ffi.NULL else None -def stbox_make(hasx: bool, hasz: bool, geodetic: bool, srid: int, xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, p: "Optional['const Span *']") -> 'STBox *': - srid_converted = _ffi.cast('int32', srid) - p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL - result = _lib.stbox_make(hasx, hasz, geodetic, srid_converted, xmin, xmax, ymin, ymax, zmin, zmax, p_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def stbox_copy(box: 'const STBox *') -> 'STBox *': - box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_copy(box_converted) +def float_period_to_tbox(d: float, p: 'const Span *') -> 'TBox *': + p_converted = _ffi.cast('const Span *', p) + result = _lib.float_period_to_tbox(d, p_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_make(s: "Optional['const Span *']", p: "Optional['const Span *']") -> 'TBox *': - s_converted = _ffi.cast('const Span *', s) if s is not None else _ffi.NULL - p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL - result = _lib.tbox_make(s_converted, p_converted) +def float_timestamp_to_tbox(d: float, t: int) -> 'TBox *': + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.float_timestamp_to_tbox(d, t_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_copy(box: 'const TBox *') -> 'TBox *': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_copy(box_converted) +def geo_period_to_stbox(gs: 'const GSERIALIZED *', p: 'const Span *') -> 'STBox *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + p_converted = _ffi.cast('const Span *', p) + result = _lib.geo_period_to_stbox(gs_converted, p_converted) _check_error() return result if result != _ffi.NULL else None -def int_to_tbox(i: int) -> 'TBox *': - result = _lib.int_to_tbox(i) +def geo_timestamp_to_stbox(gs: 'const GSERIALIZED *', t: int) -> 'STBox *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.geo_timestamp_to_stbox(gs_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def float_to_tbox(d: float) -> 'TBox *': - result = _lib.float_to_tbox(d) +def int_period_to_tbox(i: int, p: 'const Span *') -> 'TBox *': + p_converted = _ffi.cast('const Span *', p) + result = _lib.int_period_to_tbox(i, p_converted) _check_error() return result if result != _ffi.NULL else None -def timestamp_to_tbox(t: int) -> 'TBox *': +def int_timestamp_to_tbox(i: int, t: int) -> 'TBox *': t_converted = _ffi.cast('TimestampTz', t) - result = _lib.timestamp_to_tbox(t_converted) + result = _lib.int_timestamp_to_tbox(i, t_converted) _check_error() return result if result != _ffi.NULL else None -def timestampset_to_tbox(ss: 'const Set *') -> 'TBox *': - ss_converted = _ffi.cast('const Set *', ss) - result = _lib.timestampset_to_tbox(ss_converted) +def span_period_to_tbox(span: 'const Span *', p: 'const Span *') -> 'TBox *': + span_converted = _ffi.cast('const Span *', span) + p_converted = _ffi.cast('const Span *', p) + result = _lib.span_period_to_tbox(span_converted, p_converted) _check_error() return result if result != _ffi.NULL else None -def period_to_tbox(p: 'const Span *') -> 'TBox *': - p_converted = _ffi.cast('const Span *', p) - result = _lib.period_to_tbox(p_converted) +def span_timestamp_to_tbox(span: 'const Span *', t: int) -> 'TBox *': + span_converted = _ffi.cast('const Span *', span) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.span_timestamp_to_tbox(span_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def periodset_to_tbox(ps: 'const SpanSet *') -> 'TBox *': - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.periodset_to_tbox(ps_converted) +def stbox_copy(box: 'const STBox *') -> 'STBox *': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_copy(box_converted) _check_error() return result if result != _ffi.NULL else None -def int_timestamp_to_tbox(i: int, t: int) -> 'TBox *': - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.int_timestamp_to_tbox(i, t_converted) +def stbox_make(hasx: bool, hasz: bool, geodetic: bool, srid: int, xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, p: "Optional['const Span *']") -> 'STBox *': + srid_converted = _ffi.cast('int32', srid) + p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL + result = _lib.stbox_make(hasx, hasz, geodetic, srid_converted, xmin, xmax, ymin, ymax, zmin, zmax, p_converted) _check_error() return result if result != _ffi.NULL else None -def float_period_to_tbox(d: float, p: 'const Span *') -> 'TBox *': - p_converted = _ffi.cast('const Span *', p) - result = _lib.float_period_to_tbox(d, p_converted) +def tbox_copy(box: 'const TBox *') -> 'TBox *': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_copy(box_converted) _check_error() return result if result != _ffi.NULL else None -def float_timestamp_to_tbox(d: float, t: int) -> 'TBox *': - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.float_timestamp_to_tbox(d, t_converted) +def tbox_make(s: "Optional['const Span *']", p: "Optional['const Span *']") -> 'TBox *': + s_converted = _ffi.cast('const Span *', s) if s is not None else _ffi.NULL + p_converted = _ffi.cast('const Span *', p) if p is not None else _ffi.NULL + result = _lib.tbox_make(s_converted, p_converted) _check_error() return result if result != _ffi.NULL else None -def geo_period_to_stbox(gs: 'const GSERIALIZED *', p: 'const Span *') -> 'STBox *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - p_converted = _ffi.cast('const Span *', p) - result = _lib.geo_period_to_stbox(gs_converted, p_converted) +def float_to_tbox(d: float) -> 'TBox *': + result = _lib.float_to_tbox(d) _check_error() return result if result != _ffi.NULL else None -def geo_timestamp_to_stbox(gs: 'const GSERIALIZED *', t: int) -> 'STBox *': +def geo_to_stbox(gs: 'const GSERIALIZED *') -> 'STBox *': gs_converted = _ffi.cast('const GSERIALIZED *', gs) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.geo_timestamp_to_stbox(gs_converted, t_converted) + result = _lib.geo_to_stbox(gs_converted) _check_error() return result if result != _ffi.NULL else None -def geo_to_stbox(gs: 'const GSERIALIZED *') -> 'STBox *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.geo_to_stbox(gs_converted) +def int_to_tbox(i: int) -> 'TBox *': + result = _lib.int_to_tbox(i) _check_error() return result if result != _ffi.NULL else None -def int_period_to_tbox(i: int, p: 'const Span *') -> 'TBox *': - p_converted = _ffi.cast('const Span *', p) - result = _lib.int_period_to_tbox(i, p_converted) +def numset_to_tbox(s: 'const Set *') -> 'TBox *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.numset_to_tbox(s_converted) _check_error() return result if result != _ffi.NULL else None @@ -3806,212 +3801,194 @@ def numspan_to_tbox(s: 'const Span *') -> 'TBox *': return result if result != _ffi.NULL else None -def span_timestamp_to_tbox(span: 'const Span *', t: int) -> 'TBox *': - span_converted = _ffi.cast('const Span *', span) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.span_timestamp_to_tbox(span_converted, t_converted) +def numspanset_to_tbox(ss: 'const SpanSet *') -> 'TBox *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.numspanset_to_tbox(ss_converted) _check_error() return result if result != _ffi.NULL else None -def span_period_to_tbox(span: 'const Span *', p: 'const Span *') -> 'TBox *': - span_converted = _ffi.cast('const Span *', span) +def period_to_stbox(p: 'const Span *') -> 'STBox *': p_converted = _ffi.cast('const Span *', p) - result = _lib.span_period_to_tbox(span_converted, p_converted) + result = _lib.period_to_stbox(p_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_to_floatspan(box: 'const TBox *') -> 'Span *': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_to_floatspan(box_converted) +def period_to_tbox(p: 'const Span *') -> 'TBox *': + p_converted = _ffi.cast('const Span *', p) + result = _lib.period_to_tbox(p_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_to_period(box: 'const TBox *') -> 'Span *': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_to_period(box_converted) +def periodset_to_stbox(ps: 'const SpanSet *') -> 'STBox *': + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.periodset_to_stbox(ps_converted) _check_error() return result if result != _ffi.NULL else None -def stbox_to_period(box: 'const STBox *') -> 'Span *': - box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_to_period(box_converted) +def periodset_to_tbox(ps: 'const SpanSet *') -> 'TBox *': + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.periodset_to_tbox(ps_converted) _check_error() return result if result != _ffi.NULL else None -def tnumber_to_tbox(temp: 'const Temporal *') -> 'TBox *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tnumber_to_tbox(temp_converted) +def stbox_to_geo(box: 'const STBox *') -> 'GSERIALIZED *': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_to_geo(box_converted) _check_error() return result if result != _ffi.NULL else None -def stbox_to_geo(box: 'const STBox *') -> 'GSERIALIZED *': +def stbox_to_period(box: 'const STBox *') -> 'Span *': box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_to_geo(box_converted) + result = _lib.stbox_to_period(box_converted) _check_error() return result if result != _ffi.NULL else None -def tpoint_to_stbox(temp: 'const Temporal *') -> 'STBox *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tpoint_to_stbox(temp_converted) +def tbox_to_floatspan(box: 'const TBox *') -> 'Span *': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_to_floatspan(box_converted) _check_error() return result if result != _ffi.NULL else None -def timestamp_to_stbox(t: int) -> 'STBox *': - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.timestamp_to_stbox(t_converted) +def tbox_to_period(box: 'const TBox *') -> 'Span *': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_to_period(box_converted) _check_error() return result if result != _ffi.NULL else None -def timestampset_to_stbox(ts: 'const Set *') -> 'STBox *': - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.timestampset_to_stbox(ts_converted) +def timestamp_to_stbox(t: int) -> 'STBox *': + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.timestamp_to_stbox(t_converted) _check_error() return result if result != _ffi.NULL else None -def period_to_stbox(p: 'const Span *') -> 'STBox *': - p_converted = _ffi.cast('const Span *', p) - result = _lib.period_to_stbox(p_converted) +def timestamp_to_tbox(t: int) -> 'TBox *': + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.timestamp_to_tbox(t_converted) _check_error() return result if result != _ffi.NULL else None -def periodset_to_stbox(ps: 'const SpanSet *') -> 'STBox *': - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.periodset_to_stbox(ps_converted) +def timestampset_to_stbox(ts: 'const Set *') -> 'STBox *': + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.timestampset_to_stbox(ts_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_hasx(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_hasx(box_converted) +def timestampset_to_tbox(ss: 'const Set *') -> 'TBox *': + ss_converted = _ffi.cast('const Set *', ss) + result = _lib.timestampset_to_tbox(ss_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_hast(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_hast(box_converted) +def tnumber_to_tbox(temp: 'const Temporal *') -> 'TBox *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tnumber_to_tbox(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_xmin(box: 'const TBox *') -> 'double': - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('double *') - result = _lib.tbox_xmin(box_converted, out_result) +def tpoint_to_stbox(temp: 'const Temporal *') -> 'STBox *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tpoint_to_stbox(temp_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_xmin_inc(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('bool *') - result = _lib.tbox_xmin_inc(box_converted, out_result) +def stbox_hast(box: 'const STBox *') -> 'bool': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_hast(box_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_xmax(box: 'const TBox *') -> 'double': - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('double *') - result = _lib.tbox_xmax(box_converted, out_result) +def stbox_hasx(box: 'const STBox *') -> 'bool': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_hasx(box_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_xmax_inc(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('bool *') - result = _lib.tbox_xmax_inc(box_converted, out_result) +def stbox_hasz(box: 'const STBox *') -> 'bool': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_hasz(box_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_tmin(box: 'const TBox *') -> int: - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('TimestampTz *') - result = _lib.tbox_tmin(box_converted, out_result) +def stbox_isgeodetic(box: 'const STBox *') -> 'bool': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_isgeodetic(box_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_tmin_inc(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) - out_result = _ffi.new('bool *') - result = _lib.tbox_tmin_inc(box_converted, out_result) +def stbox_srid(box: 'const STBox *') -> 'int32': + box_converted = _ffi.cast('const STBox *', box) + result = _lib.stbox_srid(box_converted) _check_error() - if result: - return out_result[0] if out_result[0] != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbox_tmax(box: 'const TBox *') -> int: - box_converted = _ffi.cast('const TBox *', box) +def stbox_tmax(box: 'const STBox *') -> int: + box_converted = _ffi.cast('const STBox *', box) out_result = _ffi.new('TimestampTz *') - result = _lib.tbox_tmax(box_converted, out_result) + result = _lib.stbox_tmax(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def tbox_tmax_inc(box: 'const TBox *') -> 'bool': - box_converted = _ffi.cast('const TBox *', box) +def stbox_tmax_inc(box: 'const STBox *') -> 'bool': + box_converted = _ffi.cast('const STBox *', box) out_result = _ffi.new('bool *') - result = _lib.tbox_tmax_inc(box_converted, out_result) + result = _lib.stbox_tmax_inc(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_hasx(box: 'const STBox *') -> 'bool': - box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_hasx(box_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def stbox_hasz(box: 'const STBox *') -> 'bool': +def stbox_tmin(box: 'const STBox *') -> int: box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_hasz(box_converted) + out_result = _ffi.new('TimestampTz *') + result = _lib.stbox_tmin(box_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def stbox_hast(box: 'const STBox *') -> 'bool': +def stbox_tmin_inc(box: 'const STBox *') -> 'bool': box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_hast(box_converted) + out_result = _ffi.new('bool *') + result = _lib.stbox_tmin_inc(box_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None -def stbox_isgeodetic(box: 'const STBox *') -> 'bool': +def stbox_xmax(box: 'const STBox *') -> 'double': box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_isgeodetic(box_converted) + out_result = _ffi.new('double *') + result = _lib.stbox_xmax(box_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None def stbox_xmin(box: 'const STBox *') -> 'double': @@ -4024,10 +4001,10 @@ def stbox_xmin(box: 'const STBox *') -> 'double': return None -def stbox_xmax(box: 'const STBox *') -> 'double': +def stbox_ymax(box: 'const STBox *') -> 'double': box_converted = _ffi.cast('const STBox *', box) out_result = _ffi.new('double *') - result = _lib.stbox_xmax(box_converted, out_result) + result = _lib.stbox_ymax(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None @@ -4044,10 +4021,10 @@ def stbox_ymin(box: 'const STBox *') -> 'double': return None -def stbox_ymax(box: 'const STBox *') -> 'double': +def stbox_zmax(box: 'const STBox *') -> 'double': box_converted = _ffi.cast('const STBox *', box) out_result = _ffi.new('double *') - result = _lib.stbox_ymax(box_converted, out_result) + result = _lib.stbox_zmax(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None @@ -4064,61 +4041,98 @@ def stbox_zmin(box: 'const STBox *') -> 'double': return None -def stbox_zmax(box: 'const STBox *') -> 'double': - box_converted = _ffi.cast('const STBox *', box) - out_result = _ffi.new('double *') - result = _lib.stbox_zmax(box_converted, out_result) +def tbox_hast(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_hast(box_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tbox_hasx(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) + result = _lib.tbox_hasx(box_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tbox_tmax(box: 'const TBox *') -> int: + box_converted = _ffi.cast('const TBox *', box) + out_result = _ffi.new('TimestampTz *') + result = _lib.tbox_tmax(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_tmin(box: 'const STBox *') -> int: - box_converted = _ffi.cast('const STBox *', box) +def tbox_tmax_inc(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) + out_result = _ffi.new('bool *') + result = _lib.tbox_tmax_inc(box_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def tbox_tmin(box: 'const TBox *') -> int: + box_converted = _ffi.cast('const TBox *', box) out_result = _ffi.new('TimestampTz *') - result = _lib.stbox_tmin(box_converted, out_result) + result = _lib.tbox_tmin(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_tmin_inc(box: 'const STBox *') -> 'bool': - box_converted = _ffi.cast('const STBox *', box) +def tbox_tmin_inc(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) out_result = _ffi.new('bool *') - result = _lib.stbox_tmin_inc(box_converted, out_result) + result = _lib.tbox_tmin_inc(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_tmax(box: 'const STBox *') -> int: - box_converted = _ffi.cast('const STBox *', box) - out_result = _ffi.new('TimestampTz *') - result = _lib.stbox_tmax(box_converted, out_result) +def tbox_xmax(box: 'const TBox *') -> 'double': + box_converted = _ffi.cast('const TBox *', box) + out_result = _ffi.new('double *') + result = _lib.tbox_xmax(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_tmax_inc(box: 'const STBox *') -> 'bool': - box_converted = _ffi.cast('const STBox *', box) +def tbox_xmax_inc(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) out_result = _ffi.new('bool *') - result = _lib.stbox_tmax_inc(box_converted, out_result) + result = _lib.tbox_xmax_inc(box_converted, out_result) _check_error() if result: return out_result[0] if out_result[0] != _ffi.NULL else None return None -def stbox_srid(box: 'const STBox *') -> 'int32': - box_converted = _ffi.cast('const STBox *', box) - result = _lib.stbox_srid(box_converted) +def tbox_xmin(box: 'const TBox *') -> 'double': + box_converted = _ffi.cast('const TBox *', box) + out_result = _ffi.new('double *') + result = _lib.tbox_xmin(box_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None + + +def tbox_xmin_inc(box: 'const TBox *') -> 'bool': + box_converted = _ffi.cast('const TBox *', box) + out_result = _ffi.new('bool *') + result = _lib.tbox_xmin_inc(box_converted, out_result) + _check_error() + if result: + return out_result[0] if out_result[0] != _ffi.NULL else None + return None def stbox_expand_space(box: 'const STBox *', d: float) -> 'STBox *': @@ -4167,18 +4181,18 @@ def stbox_shift_scale_time(box: 'const STBox *', shift: "Optional['const Interva return result if result != _ffi.NULL else None -def tbox_expand_value(box: 'const TBox *', d: 'const double') -> 'TBox *': +def tbox_expand_time(box: 'const TBox *', interval: 'const Interval *') -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) - d_converted = _ffi.cast('const double', d) - result = _lib.tbox_expand_value(box_converted, d_converted) + interval_converted = _ffi.cast('const Interval *', interval) + result = _lib.tbox_expand_time(box_converted, interval_converted) _check_error() return result if result != _ffi.NULL else None -def tbox_expand_time(box: 'const TBox *', interval: 'const Interval *') -> 'TBox *': +def tbox_expand_value(box: 'const TBox *', d: 'const double') -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) - interval_converted = _ffi.cast('const Interval *', interval) - result = _lib.tbox_expand_time(box_converted, interval_converted) + d_converted = _ffi.cast('const double', d) + result = _lib.tbox_expand_value(box_converted, d_converted) _check_error() return result if result != _ffi.NULL else None @@ -4190,16 +4204,16 @@ def tbox_round(box: 'const TBox *', maxdd: int) -> 'TBox *': return result if result != _ffi.NULL else None -def tbox_shift_scale_int(box: 'const TBox *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'TBox *': +def tbox_shift_scale_float(box: 'const TBox *', shift: float, width: float, hasshift: bool, haswidth: bool) -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_shift_scale_int(box_converted, shift, width, hasshift, haswidth) + result = _lib.tbox_shift_scale_float(box_converted, shift, width, hasshift, haswidth) _check_error() return result if result != _ffi.NULL else None -def tbox_shift_scale_float(box: 'const TBox *', shift: float, width: float, hasshift: bool, haswidth: bool) -> 'TBox *': +def tbox_shift_scale_int(box: 'const TBox *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'TBox *': box_converted = _ffi.cast('const TBox *', box) - result = _lib.tbox_shift_scale_float(box_converted, shift, width, hasshift, haswidth) + result = _lib.tbox_shift_scale_int(box_converted, shift, width, hasshift, haswidth) _check_error() return result if result != _ffi.NULL else None @@ -4213,6 +4227,60 @@ def tbox_shift_scale_time(box: 'const TBox *', shift: "Optional['const Interval return result if result != _ffi.NULL else None +def union_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *', strict: bool) -> 'TBox *': + box1_converted = _ffi.cast('const TBox *', box1) + box2_converted = _ffi.cast('const TBox *', box2) + result = _lib.union_tbox_tbox(box1_converted, box2_converted, strict) + _check_error() + return result if result != _ffi.NULL else None + + +def inter_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'TBox *': + box1_converted = _ffi.cast('const TBox *', box1) + box2_converted = _ffi.cast('const TBox *', box2) + out_result = _ffi.new('TBox *') + result = _lib.inter_tbox_tbox(box1_converted, box2_converted, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def intersection_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'TBox *': + box1_converted = _ffi.cast('const TBox *', box1) + box2_converted = _ffi.cast('const TBox *', box2) + result = _lib.intersection_tbox_tbox(box1_converted, box2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def union_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *', strict: bool) -> 'STBox *': + box1_converted = _ffi.cast('const STBox *', box1) + box2_converted = _ffi.cast('const STBox *', box2) + result = _lib.union_stbox_stbox(box1_converted, box2_converted, strict) + _check_error() + return result if result != _ffi.NULL else None + + +def inter_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'STBox *': + box1_converted = _ffi.cast('const STBox *', box1) + box2_converted = _ffi.cast('const STBox *', box2) + out_result = _ffi.new('STBox *') + result = _lib.inter_stbox_stbox(box1_converted, box2_converted, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + +def intersection_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'STBox *': + box1_converted = _ffi.cast('const STBox *', box1) + box2_converted = _ffi.cast('const STBox *', box2) + result = _lib.intersection_stbox_stbox(box1_converted, box2_converted) + _check_error() + return result if result != _ffi.NULL else None + + def contains_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'bool': box1_converted = _ffi.cast('const TBox *', box1) box2_converted = _ffi.cast('const TBox *', box2) @@ -4456,85 +4524,31 @@ def overback_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool' def before_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': box1_converted = _ffi.cast('const STBox *', box1) box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.before_stbox_stbox(box1_converted, box2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overbefore_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': - box1_converted = _ffi.cast('const STBox *', box1) - box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.overbefore_stbox_stbox(box1_converted, box2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def after_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': - box1_converted = _ffi.cast('const STBox *', box1) - box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.after_stbox_stbox(box1_converted, box2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def overafter_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': - box1_converted = _ffi.cast('const STBox *', box1) - box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.overafter_stbox_stbox(box1_converted, box2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def union_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *', strict: bool) -> 'TBox *': - box1_converted = _ffi.cast('const TBox *', box1) - box2_converted = _ffi.cast('const TBox *', box2) - result = _lib.union_tbox_tbox(box1_converted, box2_converted, strict) - _check_error() - return result if result != _ffi.NULL else None - - -def inter_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'TBox *': - box1_converted = _ffi.cast('const TBox *', box1) - box2_converted = _ffi.cast('const TBox *', box2) - out_result = _ffi.new('TBox *') - result = _lib.inter_tbox_tbox(box1_converted, box2_converted, out_result) - _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None - - -def intersection_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'TBox *': - box1_converted = _ffi.cast('const TBox *', box1) - box2_converted = _ffi.cast('const TBox *', box2) - result = _lib.intersection_tbox_tbox(box1_converted, box2_converted) + result = _lib.before_stbox_stbox(box1_converted, box2_converted) _check_error() return result if result != _ffi.NULL else None -def union_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *', strict: bool) -> 'STBox *': +def overbefore_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': box1_converted = _ffi.cast('const STBox *', box1) box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.union_stbox_stbox(box1_converted, box2_converted, strict) + result = _lib.overbefore_stbox_stbox(box1_converted, box2_converted) _check_error() return result if result != _ffi.NULL else None -def inter_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'STBox *': +def after_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': box1_converted = _ffi.cast('const STBox *', box1) box2_converted = _ffi.cast('const STBox *', box2) - out_result = _ffi.new('STBox *') - result = _lib.inter_stbox_stbox(box1_converted, box2_converted, out_result) + result = _lib.after_stbox_stbox(box1_converted, box2_converted) _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def intersection_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'STBox *': +def overafter_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'bool': box1_converted = _ffi.cast('const STBox *', box1) box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.intersection_stbox_stbox(box1_converted, box2_converted) + result = _lib.overafter_stbox_stbox(box1_converted, box2_converted) _check_error() return result if result != _ffi.NULL else None @@ -5395,6 +5409,14 @@ def ttext_values(temp: 'const Temporal *') -> "Tuple['text **', 'int']": return result if result != _ffi.NULL else None, count[0] +def temporal_scale_time(temp: 'const Temporal *', duration: 'const Interval *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + duration_converted = _ffi.cast('const Interval *', duration) + result = _lib.temporal_scale_time(temp_converted, duration_converted) + _check_error() + return result if result != _ffi.NULL else None + + def temporal_set_interp(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) interp_converted = _ffi.cast('interpType', interp) @@ -5403,6 +5425,46 @@ def temporal_set_interp(temp: 'const Temporal *', interp: 'interpType') -> 'Temp return result if result != _ffi.NULL else None +def temporal_shift_scale_time(temp: 'const Temporal *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL + duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL + result = _lib.temporal_shift_scale_time(temp_converted, shift_converted, duration_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_shift_time(temp: 'const Temporal *', shift: 'const Interval *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + shift_converted = _ffi.cast('const Interval *', shift) + result = _lib.temporal_shift_time(temp_converted, shift_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_to_tinstant(temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.temporal_to_tinstant(temp_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_to_tsequence(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + interp_converted = _ffi.cast('interpType', interp) + result = _lib.temporal_to_tsequence(temp_converted, interp_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_to_tsequenceset(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + interp_converted = _ffi.cast('interpType', interp) + result = _lib.temporal_to_tsequenceset(temp_converted, interp_converted) + _check_error() + return result if result != _ffi.NULL else None + + def tfloat_scale_value(temp: 'const Temporal *', width: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) result = _lib.tfloat_scale_value(temp_converted, width) @@ -5445,68 +5507,82 @@ def tint_shift_value(temp: 'const Temporal *', shift: int) -> 'Temporal *': return result if result != _ffi.NULL else None -def temporal_scale_time(temp: 'const Temporal *', duration: 'const Interval *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - duration_converted = _ffi.cast('const Interval *', duration) - result = _lib.temporal_scale_time(temp_converted, duration_converted) +def temporal_append_tinstant(temp: 'Temporal *', inst: 'const TInstant *', maxdist: float, maxt: "Optional['Interval *']", expand: bool) -> 'Temporal *': + temp_converted = _ffi.cast('Temporal *', temp) + inst_converted = _ffi.cast('const TInstant *', inst) + maxt_converted = _ffi.cast('Interval *', maxt) if maxt is not None else _ffi.NULL + result = _lib.temporal_append_tinstant(temp_converted, inst_converted, maxdist, maxt_converted, expand) _check_error() return result if result != _ffi.NULL else None -def temporal_shift_scale_time(temp: 'const Temporal *', shift: "Optional['const Interval *']", duration: "Optional['const Interval *']") -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - shift_converted = _ffi.cast('const Interval *', shift) if shift is not None else _ffi.NULL - duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL - result = _lib.temporal_shift_scale_time(temp_converted, shift_converted, duration_converted) +def temporal_append_tsequence(temp: 'Temporal *', seq: 'const TSequence *', expand: bool) -> 'Temporal *': + temp_converted = _ffi.cast('Temporal *', temp) + seq_converted = _ffi.cast('const TSequence *', seq) + result = _lib.temporal_append_tsequence(temp_converted, seq_converted, expand) _check_error() return result if result != _ffi.NULL else None -def temporal_shift_time(temp: 'const Temporal *', shift: 'const Interval *') -> 'Temporal *': +def temporal_delete_period(temp: 'const Temporal *', p: 'const Span *', connect: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - shift_converted = _ffi.cast('const Interval *', shift) - result = _lib.temporal_shift_time(temp_converted, shift_converted) + p_converted = _ffi.cast('const Span *', p) + result = _lib.temporal_delete_period(temp_converted, p_converted, connect) _check_error() return result if result != _ffi.NULL else None -def temporal_to_tinstant(temp: 'const Temporal *') -> 'Temporal *': +def temporal_delete_periodset(temp: 'const Temporal *', ps: 'const SpanSet *', connect: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.temporal_to_tinstant(temp_converted) + ps_converted = _ffi.cast('const SpanSet *', ps) + result = _lib.temporal_delete_periodset(temp_converted, ps_converted, connect) _check_error() return result if result != _ffi.NULL else None -def temporal_to_tsequence(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': +def temporal_delete_timestamp(temp: 'const Temporal *', t: int, connect: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - interp_converted = _ffi.cast('interpType', interp) - result = _lib.temporal_to_tsequence(temp_converted, interp_converted) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.temporal_delete_timestamp(temp_converted, t_converted, connect) _check_error() return result if result != _ffi.NULL else None -def temporal_to_tsequenceset(temp: 'const Temporal *', interp: 'interpType') -> 'Temporal *': +def temporal_delete_timestampset(temp: 'const Temporal *', ts: 'const Set *', connect: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - interp_converted = _ffi.cast('interpType', interp) - result = _lib.temporal_to_tsequenceset(temp_converted, interp_converted) + ts_converted = _ffi.cast('const Set *', ts) + result = _lib.temporal_delete_timestampset(temp_converted, ts_converted, connect) _check_error() return result if result != _ffi.NULL else None -def temporal_tprecision(temp: 'const Temporal *', duration: 'const Interval *', origin: int) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - duration_converted = _ffi.cast('const Interval *', duration) - origin_converted = _ffi.cast('TimestampTz', origin) - result = _lib.temporal_tprecision(temp_converted, duration_converted, origin_converted) +def temporal_insert(temp1: 'const Temporal *', temp2: 'const Temporal *', connect: bool) -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_insert(temp1_converted, temp2_converted, connect) _check_error() return result if result != _ffi.NULL else None -def temporal_tsample(temp: 'const Temporal *', duration: 'const Interval *', origin: int) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - duration_converted = _ffi.cast('const Interval *', duration) - origin_converted = _ffi.cast('TimestampTz', origin) - result = _lib.temporal_tsample(temp_converted, duration_converted, origin_converted) +def temporal_merge(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_merge(temp1_converted, temp2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_merge_array(temparr: 'Temporal **', count: int) -> 'Temporal *': + temparr_converted = [_ffi.cast('Temporal *', x) for x in temparr] + result = _lib.temporal_merge_array(temparr_converted, count) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_update(temp1: 'const Temporal *', temp2: 'const Temporal *', connect: bool) -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_update(temp1_converted, temp2_converted, connect) _check_error() return result if result != _ffi.NULL else None @@ -5832,1131 +5908,1051 @@ def ttext_value_at_timestamp(temp: 'const Temporal *', t: int, strict: bool) -> return None -def temporal_append_tinstant(temp: 'Temporal *', inst: 'const TInstant *', maxdist: float, maxt: "Optional['Interval *']", expand: bool) -> 'Temporal *': - temp_converted = _ffi.cast('Temporal *', temp) - inst_converted = _ffi.cast('const TInstant *', inst) - maxt_converted = _ffi.cast('Interval *', maxt) if maxt is not None else _ffi.NULL - result = _lib.temporal_append_tinstant(temp_converted, inst_converted, maxdist, maxt_converted, expand) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_append_tsequence(temp: 'Temporal *', seq: 'const TSequence *', expand: bool) -> 'Temporal *': - temp_converted = _ffi.cast('Temporal *', temp) - seq_converted = _ffi.cast('const TSequence *', seq) - result = _lib.temporal_append_tsequence(temp_converted, seq_converted, expand) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_delete_period(temp: 'const Temporal *', p: 'const Span *', connect: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - p_converted = _ffi.cast('const Span *', p) - result = _lib.temporal_delete_period(temp_converted, p_converted, connect) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_delete_periodset(temp: 'const Temporal *', ps: 'const SpanSet *', connect: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.temporal_delete_periodset(temp_converted, ps_converted, connect) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_delete_timestamp(temp: 'const Temporal *', t: int, connect: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - t_converted = _ffi.cast('TimestampTz', t) - result = _lib.temporal_delete_timestamp(temp_converted, t_converted, connect) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_delete_timestampset(temp: 'const Temporal *', ts: 'const Set *', connect: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - ts_converted = _ffi.cast('const Set *', ts) - result = _lib.temporal_delete_timestampset(temp_converted, ts_converted, connect) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_insert(temp1: 'const Temporal *', temp2: 'const Temporal *', connect: bool) -> 'Temporal *': +def temporal_cmp(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'int': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_insert(temp1_converted, temp2_converted, connect) + result = _lib.temporal_cmp(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_merge(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def temporal_eq(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_merge(temp1_converted, temp2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_merge_array(temparr: 'Temporal **', count: int) -> 'Temporal *': - temparr_converted = [_ffi.cast('Temporal *', x) for x in temparr] - result = _lib.temporal_merge_array(temparr_converted, count) + result = _lib.temporal_eq(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_update(temp1: 'const Temporal *', temp2: 'const Temporal *', connect: bool) -> 'Temporal *': +def temporal_ge(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_update(temp1_converted, temp2_converted, connect) - _check_error() - return result if result != _ffi.NULL else None - - -def tand_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tand_bool_tbool(b, temp_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def tand_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tand_tbool_bool(temp_converted, b) + result = _lib.temporal_ge(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tand_tbool_tbool(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def temporal_gt(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tand_tbool_tbool(temp1_converted, temp2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def tbool_when_true(temp: 'const Temporal *') -> 'SpanSet *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tbool_when_true(temp_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def tnot_tbool(temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tnot_tbool(temp_converted) + result = _lib.temporal_gt(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tor_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tor_bool_tbool(b, temp_converted) +def temporal_le(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_le(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tor_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tor_tbool_bool(temp_converted, b) +def temporal_lt(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_lt(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tor_tbool_tbool(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def temporal_ne(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tor_tbool_tbool(temp1_converted, temp2_converted) + result = _lib.temporal_ne(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def add_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_float_tfloat(d, tnumber_converted) +def tbool_always_eq(temp: 'const Temporal *', b: bool) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tbool_always_eq(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def add_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_int_tint(i, tnumber_converted) +def tbool_ever_eq(temp: 'const Temporal *', b: bool) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tbool_ever_eq(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def add_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_tfloat_float(tnumber_converted, d) +def tfloat_always_eq(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_always_eq(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def add_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.add_tint_int(tnumber_converted, i) +def tfloat_always_le(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_always_le(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def add_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': - tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) - tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) - result = _lib.add_tnumber_tnumber(tnumber1_converted, tnumber2_converted) +def tfloat_always_lt(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_always_lt(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def div_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_float_tfloat(d, tnumber_converted) +def tfloat_ever_eq(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_ever_eq(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def div_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_int_tint(i, tnumber_converted) +def tfloat_ever_le(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_ever_le(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def div_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_tfloat_float(tnumber_converted, d) +def tfloat_ever_lt(temp: 'const Temporal *', d: float) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_ever_lt(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def div_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.div_tint_int(tnumber_converted, i) +def tint_always_eq(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_always_eq(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def div_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': - tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) - tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) - result = _lib.div_tnumber_tnumber(tnumber1_converted, tnumber2_converted) +def tint_always_le(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_always_le(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def float_degrees(value: float, normalize: bool) -> 'double': - result = _lib.float_degrees(value, normalize) +def tint_always_lt(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_always_lt(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def mult_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_float_tfloat(d, tnumber_converted) +def tint_ever_eq(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_ever_eq(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def mult_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_int_tint(i, tnumber_converted) +def tint_ever_le(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_ever_le(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def mult_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_tfloat_float(tnumber_converted, d) +def tint_ever_lt(temp: 'const Temporal *', i: int) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tint_ever_lt(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def mult_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.mult_tint_int(tnumber_converted, i) +def tpoint_always_eq(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.tpoint_always_eq(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def mult_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': - tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) - tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) - result = _lib.mult_tnumber_tnumber(tnumber1_converted, tnumber2_converted) +def tpoint_ever_eq(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.tpoint_ever_eq(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def sub_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_float_tfloat(d, tnumber_converted) +def ttext_always_eq(temp: 'const Temporal *', txt: str) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.ttext_always_eq(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def sub_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_int_tint(i, tnumber_converted) +def ttext_always_le(temp: 'const Temporal *', txt: str) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.ttext_always_le(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def sub_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_tfloat_float(tnumber_converted, d) +def ttext_always_lt(temp: 'const Temporal *', txt: str) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.ttext_always_lt(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def sub_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': - tnumber_converted = _ffi.cast('const Temporal *', tnumber) - result = _lib.sub_tint_int(tnumber_converted, i) +def ttext_ever_eq(temp: 'const Temporal *', txt: str) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.ttext_ever_eq(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def sub_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': - tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) - tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) - result = _lib.sub_tnumber_tnumber(tnumber1_converted, tnumber2_converted) +def ttext_ever_le(temp: 'const Temporal *', txt: str) -> 'bool': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.ttext_ever_le(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_round(temp: 'const Temporal *', maxdd: int) -> 'Temporal *': +def ttext_ever_lt(temp: 'const Temporal *', txt: str) -> 'bool': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_round(temp_converted, maxdd) + txt_converted = cstring2text(txt) + result = _lib.ttext_ever_lt(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_degrees(temp: 'const Temporal *', normalize: bool) -> 'Temporal *': +def teq_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_degrees(temp_converted, normalize) + result = _lib.teq_bool_tbool(b, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_derivative(temp: 'const Temporal *') -> 'Temporal *': +def teq_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_derivative(temp_converted) + result = _lib.teq_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_radians(temp: 'const Temporal *') -> 'Temporal *': +def teq_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_radians(temp_converted) + result = _lib.teq_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tnumber_abs(temp: 'const Temporal *') -> 'Temporal *': +def teq_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tnumber_abs(temp_converted) + result = _lib.teq_point_tpoint(gs_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tnumber_angular_difference(temp: 'const Temporal *') -> 'Temporal *': +def teq_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tnumber_angular_difference(temp_converted) + result = _lib.teq_tbool_bool(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def tnumber_delta_value(temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tnumber_delta_value(temp_converted) +def teq_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.teq_temporal_temporal(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def textcat_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': +def teq_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.textcat_text_ttext(txt_converted, temp_converted) + result = _lib.teq_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def textcat_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': +def teq_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.textcat_ttext_text(temp_converted, txt_converted) + result = _lib.teq_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def textcat_ttext_ttext(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.textcat_ttext_ttext(temp1_converted, temp2_converted) +def teq_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.teq_tpoint_point(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def ttext_upper(temp: 'const Temporal *') -> 'Temporal *': +def teq_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.ttext_upper(temp_converted) + result = _lib.teq_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def ttext_lower(temp: 'const Temporal *') -> 'Temporal *': +def teq_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.ttext_lower(temp_converted) + txt_converted = cstring2text(txt) + result = _lib.teq_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def distance_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': +def tge_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.distance_tfloat_float(temp_converted, d) + result = _lib.tge_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def distance_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': +def tge_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.distance_tint_int(temp_converted, i) + result = _lib.tge_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def distance_tnumber_tnumber(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def tge_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.distance_tnumber_tnumber(temp1_converted, temp2_converted) + result = _lib.tge_temporal_temporal(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def distance_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': +def tge_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.distance_tpoint_point(temp_converted, gs_converted) + result = _lib.tge_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def distance_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.distance_tpoint_tpoint(temp1_converted, temp2_converted) +def tge_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tge_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def nad_stbox_geo(box: 'const STBox *', gs: 'const GSERIALIZED *') -> 'double': - box_converted = _ffi.cast('const STBox *', box) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.nad_stbox_geo(box_converted, gs_converted) +def tge_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tge_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def nad_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'double': - box1_converted = _ffi.cast('const STBox *', box1) - box2_converted = _ffi.cast('const STBox *', box2) - result = _lib.nad_stbox_stbox(box1_converted, box2_converted) +def tge_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.tge_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'double': - box1_converted = _ffi.cast('const TBox *', box1) - box2_converted = _ffi.cast('const TBox *', box2) - result = _lib.nad_tbox_tbox(box1_converted, box2_converted) +def tgt_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tgt_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tfloat_float(temp: 'const Temporal *', d: float) -> 'double': +def tgt_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.nad_tfloat_float(temp_converted, d) + result = _lib.tgt_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tfloat_tfloat(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': +def tgt_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.nad_tfloat_tfloat(temp1_converted, temp2_converted) + result = _lib.tgt_temporal_temporal(temp1_converted, temp2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tgt_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tgt_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tint_int(temp: 'const Temporal *', i: int) -> 'int': +def tgt_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.nad_tint_int(temp_converted, i) + result = _lib.tgt_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def nad_tint_tint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'int': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.nad_tint_tint(temp1_converted, temp2_converted) +def tgt_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tgt_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def nad_tnumber_tbox(temp: 'const Temporal *', box: 'const TBox *') -> 'double': +def tgt_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - box_converted = _ffi.cast('const TBox *', box) - result = _lib.nad_tnumber_tbox(temp_converted, box_converted) + txt_converted = cstring2text(txt) + result = _lib.tgt_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'double': +def tle_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.nad_tpoint_geo(temp_converted, gs_converted) + result = _lib.tle_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tpoint_stbox(temp: 'const Temporal *', box: 'const STBox *') -> 'double': +def tle_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - box_converted = _ffi.cast('const STBox *', box) - result = _lib.nad_tpoint_stbox(temp_converted, box_converted) + result = _lib.tle_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nad_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': +def tle_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.nad_tpoint_tpoint(temp1_converted, temp2_converted) + result = _lib.tle_temporal_temporal(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def nai_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'TInstant *': +def tle_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.nai_tpoint_geo(temp_converted, gs_converted) + result = _lib.tle_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def nai_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'TInstant *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.nai_tpoint_tpoint(temp1_converted, temp2_converted) +def tle_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tle_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def shortestline_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'GSERIALIZED **': +def tle_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - out_result = _ffi.new('GSERIALIZED **') - result = _lib.shortestline_tpoint_geo(temp_converted, gs_converted, out_result) + result = _lib.tle_tint_int(temp_converted, i) _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def shortestline_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'GSERIALIZED **': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - out_result = _ffi.new('GSERIALIZED **') - result = _lib.shortestline_tpoint_tpoint(temp1_converted, temp2_converted, out_result) +def tle_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + txt_converted = cstring2text(txt) + result = _lib.tle_ttext_text(temp_converted, txt_converted) _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None + return result if result != _ffi.NULL else None -def tbool_always_eq(temp: 'const Temporal *', b: bool) -> 'bool': +def tlt_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tbool_always_eq(temp_converted, b) + result = _lib.tlt_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tbool_ever_eq(temp: 'const Temporal *', b: bool) -> 'bool': +def tlt_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tbool_ever_eq(temp_converted, b) + result = _lib.tlt_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_always_eq(temp: 'const Temporal *', d: float) -> 'bool': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_always_eq(temp_converted, d) +def tlt_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.tlt_temporal_temporal(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_always_le(temp: 'const Temporal *', d: float) -> 'bool': +def tlt_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_always_le(temp_converted, d) + result = _lib.tlt_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_always_lt(temp: 'const Temporal *', d: float) -> 'bool': +def tlt_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_always_lt(temp_converted, d) + result = _lib.tlt_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def tfloat_ever_eq(temp: 'const Temporal *', d: float) -> 'bool': +def tlt_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_ever_eq(temp_converted, d) + result = _lib.tlt_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def tfloat_ever_le(temp: 'const Temporal *', d: float) -> 'bool': +def tlt_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_ever_le(temp_converted, d) + txt_converted = cstring2text(txt) + result = _lib.tlt_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def tfloat_ever_lt(temp: 'const Temporal *', d: float) -> 'bool': +def tne_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tfloat_ever_lt(temp_converted, d) + result = _lib.tne_bool_tbool(b, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tint_always_eq(temp: 'const Temporal *', i: int) -> 'bool': +def tne_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_always_eq(temp_converted, i) + result = _lib.tne_float_tfloat(d, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tint_always_le(temp: 'const Temporal *', i: int) -> 'bool': +def tne_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_always_le(temp_converted, i) + result = _lib.tne_int_tint(i, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tint_always_lt(temp: 'const Temporal *', i: int) -> 'bool': +def tne_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_always_lt(temp_converted, i) + result = _lib.tne_point_tpoint(gs_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tint_ever_eq(temp: 'const Temporal *', i: int) -> 'bool': +def tne_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_ever_eq(temp_converted, i) + result = _lib.tne_tbool_bool(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def tint_ever_le(temp: 'const Temporal *', i: int) -> 'bool': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_ever_le(temp_converted, i) +def tne_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.tne_temporal_temporal(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tint_ever_lt(temp: 'const Temporal *', i: int) -> 'bool': +def tne_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tint_ever_lt(temp_converted, i) + result = _lib.tne_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tpoint_always_eq(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'bool': +def tne_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.tpoint_always_eq(temp_converted, gs_converted) + result = _lib.tne_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def tpoint_ever_eq(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'bool': +def tne_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.tpoint_ever_eq(temp_converted, gs_converted) + result = _lib.tne_tpoint_point(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def ttext_always_eq(temp: 'const Temporal *', txt: str) -> 'bool': +def tne_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.ttext_always_eq(temp_converted, txt_converted) + result = _lib.tne_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def ttext_always_le(temp: 'const Temporal *', txt: str) -> 'bool': +def tne_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) txt_converted = cstring2text(txt) - result = _lib.ttext_always_le(temp_converted, txt_converted) + result = _lib.tne_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def ttext_always_lt(temp: 'const Temporal *', txt: str) -> 'bool': +def tand_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.ttext_always_lt(temp_converted, txt_converted) + result = _lib.tand_bool_tbool(b, temp_converted) _check_error() return result if result != _ffi.NULL else None -def ttext_ever_eq(temp: 'const Temporal *', txt: str) -> 'bool': +def tand_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.ttext_ever_eq(temp_converted, txt_converted) + result = _lib.tand_tbool_bool(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def ttext_ever_le(temp: 'const Temporal *', txt: str) -> 'bool': - temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.ttext_ever_le(temp_converted, txt_converted) +def tand_tbool_tbool(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.tand_tbool_tbool(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def ttext_ever_lt(temp: 'const Temporal *', txt: str) -> 'bool': +def tbool_when_true(temp: 'const Temporal *') -> 'SpanSet *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.ttext_ever_lt(temp_converted, txt_converted) + result = _lib.tbool_when_true(temp_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_cmp(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'int': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_cmp(temp1_converted, temp2_converted) +def tnot_tbool(temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tnot_tbool(temp_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_eq(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_eq(temp1_converted, temp2_converted) +def tor_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tor_bool_tbool(b, temp_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_ge(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_ge(temp1_converted, temp2_converted) +def tor_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tor_tbool_bool(temp_converted, b) _check_error() return result if result != _ffi.NULL else None -def temporal_gt(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': +def tor_tbool_tbool(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_gt(temp1_converted, temp2_converted) + result = _lib.tor_tbool_tbool(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_le(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_le(temp1_converted, temp2_converted) +def add_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.add_float_tfloat(d, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_lt(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_lt(temp1_converted, temp2_converted) +def add_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.add_int_tint(i, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def temporal_ne(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'bool': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_ne(temp1_converted, temp2_converted) +def add_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.add_tfloat_float(tnumber_converted, d) _check_error() return result if result != _ffi.NULL else None -def teq_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_bool_tbool(b, temp_converted) +def add_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.add_tint_int(tnumber_converted, i) _check_error() return result if result != _ffi.NULL else None -def teq_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_float_tfloat(d, temp_converted) +def add_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': + tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) + tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) + result = _lib.add_tnumber_tnumber(tnumber1_converted, tnumber2_converted) _check_error() return result if result != _ffi.NULL else None -def teq_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_int_tint(i, temp_converted) +def div_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.div_float_tfloat(d, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def teq_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_point_tpoint(gs_converted, temp_converted) +def div_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.div_int_tint(i, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def teq_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_tbool_bool(temp_converted, b) +def div_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.div_tfloat_float(tnumber_converted, d) _check_error() return result if result != _ffi.NULL else None -def teq_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.teq_temporal_temporal(temp1_converted, temp2_converted) +def div_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.div_tint_int(tnumber_converted, i) _check_error() return result if result != _ffi.NULL else None -def teq_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_text_ttext(txt_converted, temp_converted) +def div_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': + tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) + tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) + result = _lib.div_tnumber_tnumber(tnumber1_converted, tnumber2_converted) _check_error() return result if result != _ffi.NULL else None -def teq_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_tfloat_float(temp_converted, d) +def float_degrees(value: float, normalize: bool) -> 'double': + result = _lib.float_degrees(value, normalize) _check_error() return result if result != _ffi.NULL else None -def teq_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.teq_tpoint_point(temp_converted, gs_converted) +def mult_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.mult_float_tfloat(d, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def teq_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.teq_tint_int(temp_converted, i) +def mult_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.mult_int_tint(i, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def teq_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.teq_ttext_text(temp_converted, txt_converted) +def mult_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.mult_tfloat_float(tnumber_converted, d) _check_error() return result if result != _ffi.NULL else None -def tge_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tge_float_tfloat(d, temp_converted) +def mult_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.mult_tint_int(tnumber_converted, i) _check_error() return result if result != _ffi.NULL else None -def tge_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tge_int_tint(i, temp_converted) +def mult_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': + tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) + tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) + result = _lib.mult_tnumber_tnumber(tnumber1_converted, tnumber2_converted) _check_error() return result if result != _ffi.NULL else None -def tge_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tge_temporal_temporal(temp1_converted, temp2_converted) +def sub_float_tfloat(d: float, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.sub_float_tfloat(d, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def tge_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tge_text_ttext(txt_converted, temp_converted) +def sub_int_tint(i: int, tnumber: 'const Temporal *') -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.sub_int_tint(i, tnumber_converted) _check_error() return result if result != _ffi.NULL else None -def tge_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tge_tfloat_float(temp_converted, d) +def sub_tfloat_float(tnumber: 'const Temporal *', d: float) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.sub_tfloat_float(tnumber_converted, d) _check_error() return result if result != _ffi.NULL else None -def tge_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tge_tint_int(temp_converted, i) +def sub_tint_int(tnumber: 'const Temporal *', i: int) -> 'Temporal *': + tnumber_converted = _ffi.cast('const Temporal *', tnumber) + result = _lib.sub_tint_int(tnumber_converted, i) _check_error() return result if result != _ffi.NULL else None -def tge_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.tge_ttext_text(temp_converted, txt_converted) +def sub_tnumber_tnumber(tnumber1: 'const Temporal *', tnumber2: 'const Temporal *') -> 'Temporal *': + tnumber1_converted = _ffi.cast('const Temporal *', tnumber1) + tnumber2_converted = _ffi.cast('const Temporal *', tnumber2) + result = _lib.sub_tnumber_tnumber(tnumber1_converted, tnumber2_converted) _check_error() return result if result != _ffi.NULL else None -def tgt_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': +def tfloat_round(temp: 'const Temporal *', maxdd: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgt_float_tfloat(d, temp_converted) + result = _lib.tfloat_round(temp_converted, maxdd) _check_error() return result if result != _ffi.NULL else None -def tgt_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': +def tfloat_degrees(temp: 'const Temporal *', normalize: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgt_int_tint(i, temp_converted) + result = _lib.tfloat_degrees(temp_converted, normalize) _check_error() return result if result != _ffi.NULL else None -def tgt_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tgt_temporal_temporal(temp1_converted, temp2_converted) +def tfloat_derivative(temp: 'const Temporal *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tfloat_derivative(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tgt_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) +def tfloat_radians(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgt_text_ttext(txt_converted, temp_converted) + result = _lib.tfloat_radians(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tgt_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': +def tnumber_abs(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgt_tfloat_float(temp_converted, d) + result = _lib.tnumber_abs(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tgt_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': +def tnumber_angular_difference(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgt_tint_int(temp_converted, i) + result = _lib.tnumber_angular_difference(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tgt_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': +def tnumber_delta_value(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.tgt_ttext_text(temp_converted, txt_converted) + result = _lib.tnumber_delta_value(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tle_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': +def textcat_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': + txt_converted = cstring2text(txt) temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tle_float_tfloat(d, temp_converted) + result = _lib.textcat_text_ttext(txt_converted, temp_converted) _check_error() return result if result != _ffi.NULL else None -def tle_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': +def textcat_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tle_int_tint(i, temp_converted) + txt_converted = cstring2text(txt) + result = _lib.textcat_ttext_text(temp_converted, txt_converted) _check_error() return result if result != _ffi.NULL else None -def tle_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def textcat_ttext_ttext(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tle_temporal_temporal(temp1_converted, temp2_converted) + result = _lib.textcat_ttext_ttext(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tle_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) +def ttext_upper(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tle_text_ttext(txt_converted, temp_converted) + result = _lib.ttext_upper(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tle_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': +def ttext_lower(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tle_tfloat_float(temp_converted, d) + result = _lib.ttext_lower(temp_converted) _check_error() return result if result != _ffi.NULL else None -def tle_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': +def distance_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tle_tint_int(temp_converted, i) + result = _lib.distance_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def tle_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': +def distance_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.tle_ttext_text(temp_converted, txt_converted) + result = _lib.distance_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def tlt_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tlt_float_tfloat(d, temp_converted) +def distance_tnumber_tnumber(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.distance_tnumber_tnumber(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': +def distance_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tlt_int_tint(i, temp_converted) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.distance_tpoint_point(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': +def distance_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': temp1_converted = _ffi.cast('const Temporal *', temp1) temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tlt_temporal_temporal(temp1_converted, temp2_converted) + result = _lib.distance_tpoint_tpoint(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tlt_text_ttext(txt_converted, temp_converted) +def nad_stbox_geo(box: 'const STBox *', gs: 'const GSERIALIZED *') -> 'double': + box_converted = _ffi.cast('const STBox *', box) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.nad_stbox_geo(box_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tlt_tfloat_float(temp_converted, d) +def nad_stbox_stbox(box1: 'const STBox *', box2: 'const STBox *') -> 'double': + box1_converted = _ffi.cast('const STBox *', box1) + box2_converted = _ffi.cast('const STBox *', box2) + result = _lib.nad_stbox_stbox(box1_converted, box2_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tlt_tint_int(temp_converted, i) +def nad_tbox_tbox(box1: 'const TBox *', box2: 'const TBox *') -> 'double': + box1_converted = _ffi.cast('const TBox *', box1) + box2_converted = _ffi.cast('const TBox *', box2) + result = _lib.nad_tbox_tbox(box1_converted, box2_converted) _check_error() return result if result != _ffi.NULL else None -def tlt_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': +def nad_tfloat_float(temp: 'const Temporal *', d: float) -> 'double': temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.tlt_ttext_text(temp_converted, txt_converted) + result = _lib.nad_tfloat_float(temp_converted, d) _check_error() return result if result != _ffi.NULL else None -def tne_bool_tbool(b: bool, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_bool_tbool(b, temp_converted) +def nad_tfloat_tfloat(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.nad_tfloat_tfloat(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tne_float_tfloat(d: float, temp: 'const Temporal *') -> 'Temporal *': +def nad_tint_int(temp: 'const Temporal *', i: int) -> 'int': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_float_tfloat(d, temp_converted) + result = _lib.nad_tint_int(temp_converted, i) _check_error() return result if result != _ffi.NULL else None -def tne_int_tint(i: int, temp: 'const Temporal *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_int_tint(i, temp_converted) +def nad_tint_tint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'int': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.nad_tint_tint(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tne_point_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'Temporal *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) +def nad_tnumber_tbox(temp: 'const Temporal *', box: 'const TBox *') -> 'double': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_point_tpoint(gs_converted, temp_converted) + box_converted = _ffi.cast('const TBox *', box) + result = _lib.nad_tnumber_tbox(temp_converted, box_converted) _check_error() return result if result != _ffi.NULL else None -def tne_tbool_bool(temp: 'const Temporal *', b: bool) -> 'Temporal *': +def nad_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'double': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_tbool_bool(temp_converted, b) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.nad_tpoint_geo(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def tne_temporal_temporal(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'Temporal *': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.tne_temporal_temporal(temp1_converted, temp2_converted) +def nad_tpoint_stbox(temp: 'const Temporal *', box: 'const STBox *') -> 'double': + temp_converted = _ffi.cast('const Temporal *', temp) + box_converted = _ffi.cast('const STBox *', box) + result = _lib.nad_tpoint_stbox(temp_converted, box_converted) _check_error() return result if result != _ffi.NULL else None -def tne_text_ttext(txt: str, temp: 'const Temporal *') -> 'Temporal *': - txt_converted = cstring2text(txt) - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_text_ttext(txt_converted, temp_converted) +def nad_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.nad_tpoint_tpoint(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tne_tfloat_float(temp: 'const Temporal *', d: float) -> 'Temporal *': +def nai_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'TInstant *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_tfloat_float(temp_converted, d) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.nai_tpoint_geo(temp_converted, gs_converted) _check_error() return result if result != _ffi.NULL else None -def tne_tpoint_point(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.tne_tpoint_point(temp_converted, gs_converted) +def nai_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'TInstant *': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.nai_tpoint_tpoint(temp1_converted, temp2_converted) _check_error() return result if result != _ffi.NULL else None -def tne_tint_int(temp: 'const Temporal *', i: int) -> 'Temporal *': +def shortestline_tpoint_geo(temp: 'const Temporal *', gs: 'const GSERIALIZED *') -> 'GSERIALIZED **': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tne_tint_int(temp_converted, i) + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.shortestline_tpoint_geo(temp_converted, gs_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result if out_result != _ffi.NULL else None + return None -def tne_ttext_text(temp: 'const Temporal *', txt: str) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - txt_converted = cstring2text(txt) - result = _lib.tne_ttext_text(temp_converted, txt_converted) +def shortestline_tpoint_tpoint(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'GSERIALIZED **': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.shortestline_tpoint_tpoint(temp1_converted, temp2_converted, out_result) _check_error() - return result if result != _ffi.NULL else None + if result: + return out_result if out_result != _ffi.NULL else None + return None def bearing_point_point(gs1: 'const GSERIALIZED *', gs2: 'const GSERIALIZED *') -> 'double': @@ -7095,9 +7091,16 @@ def geo_expand_space(gs: 'const GSERIALIZED *', d: float) -> 'STBox *': return result if result != _ffi.NULL else None -def tpoint_expand_space(temp: 'const Temporal *', d: float) -> 'STBox *': +def geo_to_tpoint(gs: 'const GSERIALIZED *') -> 'Temporal *': + gs_converted = _ffi.cast('const GSERIALIZED *', gs) + result = _lib.geo_to_tpoint(gs_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def tgeogpoint_to_tgeompoint(temp: 'const Temporal *') -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tpoint_expand_space(temp_converted, d) + result = _lib.tgeogpoint_to_tgeompoint(temp_converted) _check_error() return result if result != _ffi.NULL else None @@ -7109,16 +7112,22 @@ def tgeompoint_to_tgeogpoint(temp: 'const Temporal *') -> 'Temporal *': return result if result != _ffi.NULL else None -def tgeogpoint_to_tgeompoint(temp: 'const Temporal *') -> 'Temporal *': +def tpoint_AsMVTGeom(temp: 'const Temporal *', bounds: 'const STBox *', extent: 'int32_t', buffer: 'int32_t', clip_geom: bool, gsarr: 'GSERIALIZED **', timesarr: 'int64 **') -> "Tuple['bool', 'int']": temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tgeogpoint_to_tgeompoint(temp_converted) + bounds_converted = _ffi.cast('const STBox *', bounds) + extent_converted = _ffi.cast('int32_t', extent) + buffer_converted = _ffi.cast('int32_t', buffer) + gsarr_converted = [_ffi.cast('GSERIALIZED *', x) for x in gsarr] + timesarr_converted = [_ffi.cast('int64 *', x) for x in timesarr] + count = _ffi.new('int *') + result = _lib.tpoint_AsMVTGeom(temp_converted, bounds_converted, extent_converted, buffer_converted, clip_geom, gsarr_converted, timesarr_converted, count) _check_error() - return result if result != _ffi.NULL else None + return result if result != _ffi.NULL else None, count[0] -def tpoint_round(temp: 'const Temporal *', maxdd: int) -> 'Temporal *': +def tpoint_expand_space(temp: 'const Temporal *', d: float) -> 'STBox *': temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.tpoint_round(temp_converted, maxdd) + result = _lib.tpoint_expand_space(temp_converted, d) _check_error() return result if result != _ffi.NULL else None @@ -7131,6 +7140,13 @@ def tpoint_make_simple(temp: 'const Temporal *') -> "Tuple['Temporal **', 'int'] return result if result != _ffi.NULL else None, count[0] +def tpoint_round(temp: 'const Temporal *', maxdd: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.tpoint_round(temp_converted, maxdd) + _check_error() + return result if result != _ffi.NULL else None + + def tpoint_set_srid(temp: 'const Temporal *', srid: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) srid_converted = _ffi.cast('int32', srid) @@ -7139,6 +7155,17 @@ def tpoint_set_srid(temp: 'const Temporal *', srid: int) -> 'Temporal *': return result if result != _ffi.NULL else None +def tpoint_to_geo_meas(tpoint: 'const Temporal *', measure: 'const Temporal *', segmentize: bool) -> 'GSERIALIZED **': + tpoint_converted = _ffi.cast('const Temporal *', tpoint) + measure_converted = _ffi.cast('const Temporal *', measure) + out_result = _ffi.new('GSERIALIZED **') + result = _lib.tpoint_to_geo_meas(tpoint_converted, measure_converted, segmentize, out_result) + _check_error() + if result: + return out_result if out_result != _ffi.NULL else None + return None + + def econtains_geo_tpoint(gs: 'const GSERIALIZED *', temp: 'const Temporal *') -> 'int': gs_converted = _ffi.cast('const GSERIALIZED *', gs) temp_converted = _ffi.cast('const Temporal *', temp) @@ -7421,6 +7448,95 @@ def ttext_tmin_transfn(state: "Optional['SkipList *']", temp: 'const Temporal *' return result if result != _ffi.NULL else None +def temporal_simplify_min_dist(temp: 'const Temporal *', dist: float) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.temporal_simplify_min_dist(temp_converted, dist) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_simplify_min_tdelta(temp: 'const Temporal *', mint: 'const Interval *') -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + mint_converted = _ffi.cast('const Interval *', mint) + result = _lib.temporal_simplify_min_tdelta(temp_converted, mint_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_simplify_dp(temp: 'const Temporal *', eps_dist: float, synchronized: bool) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.temporal_simplify_dp(temp_converted, eps_dist, synchronized) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_simplify_max_dist(temp: 'const Temporal *', eps_dist: float, synchronized: bool) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + result = _lib.temporal_simplify_max_dist(temp_converted, eps_dist, synchronized) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_tprecision(temp: 'const Temporal *', duration: 'const Interval *', origin: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + duration_converted = _ffi.cast('const Interval *', duration) + origin_converted = _ffi.cast('TimestampTz', origin) + result = _lib.temporal_tprecision(temp_converted, duration_converted, origin_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_tsample(temp: 'const Temporal *', duration: 'const Interval *', origin: int) -> 'Temporal *': + temp_converted = _ffi.cast('const Temporal *', temp) + duration_converted = _ffi.cast('const Interval *', duration) + origin_converted = _ffi.cast('TimestampTz', origin) + result = _lib.temporal_tsample(temp_converted, duration_converted, origin_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_dyntimewarp_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_dyntimewarp_distance(temp1_converted, temp2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_dyntimewarp_path(temp1: 'const Temporal *', temp2: 'const Temporal *') -> "Tuple['Match *', 'int']": + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + count = _ffi.new('int *') + result = _lib.temporal_dyntimewarp_path(temp1_converted, temp2_converted, count) + _check_error() + return result if result != _ffi.NULL else None, count[0] + + +def temporal_frechet_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_frechet_distance(temp1_converted, temp2_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def temporal_frechet_path(temp1: 'const Temporal *', temp2: 'const Temporal *') -> "Tuple['Match *', 'int']": + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + count = _ffi.new('int *') + result = _lib.temporal_frechet_path(temp1_converted, temp2_converted, count) + _check_error() + return result if result != _ffi.NULL else None, count[0] + + +def temporal_hausdorff_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': + temp1_converted = _ffi.cast('const Temporal *', temp1) + temp2_converted = _ffi.cast('const Temporal *', temp2) + result = _lib.temporal_hausdorff_distance(temp1_converted, temp2_converted) + _check_error() + return result if result != _ffi.NULL else None + + def float_bucket(value: float, size: float, origin: float) -> 'double': result = _lib.float_bucket(value, size, origin) _check_error() @@ -7537,105 +7653,3 @@ def tint_value_time_split(temp: 'Temporal *', size: int, vorigin: int, duration: return result if result != _ffi.NULL else None, newcount[0] -def temporal_dyntimewarp_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_dyntimewarp_distance(temp1_converted, temp2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_dyntimewarp_path(temp1: 'const Temporal *', temp2: 'const Temporal *') -> "Tuple['Match *', 'int']": - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - count = _ffi.new('int *') - result = _lib.temporal_dyntimewarp_path(temp1_converted, temp2_converted, count) - _check_error() - return result if result != _ffi.NULL else None, count[0] - - -def temporal_frechet_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_frechet_distance(temp1_converted, temp2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_frechet_path(temp1: 'const Temporal *', temp2: 'const Temporal *') -> "Tuple['Match *', 'int']": - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - count = _ffi.new('int *') - result = _lib.temporal_frechet_path(temp1_converted, temp2_converted, count) - _check_error() - return result if result != _ffi.NULL else None, count[0] - - -def temporal_hausdorff_distance(temp1: 'const Temporal *', temp2: 'const Temporal *') -> 'double': - temp1_converted = _ffi.cast('const Temporal *', temp1) - temp2_converted = _ffi.cast('const Temporal *', temp2) - result = _lib.temporal_hausdorff_distance(temp1_converted, temp2_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def geo_to_tpoint(gs: 'const GSERIALIZED *') -> 'Temporal *': - gs_converted = _ffi.cast('const GSERIALIZED *', gs) - result = _lib.geo_to_tpoint(gs_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_simplify_min_dist(temp: 'const Temporal *', dist: float) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.temporal_simplify_min_dist(temp_converted, dist) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_simplify_min_tdelta(temp: 'const Temporal *', mint: 'const Interval *') -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - mint_converted = _ffi.cast('const Interval *', mint) - result = _lib.temporal_simplify_min_tdelta(temp_converted, mint_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_simplify_dp(temp: 'const Temporal *', eps_dist: float, synchronized: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.temporal_simplify_dp(temp_converted, eps_dist, synchronized) - _check_error() - return result if result != _ffi.NULL else None - - -def temporal_simplify_max_dist(temp: 'const Temporal *', eps_dist: float, synchronized: bool) -> 'Temporal *': - temp_converted = _ffi.cast('const Temporal *', temp) - result = _lib.temporal_simplify_max_dist(temp_converted, eps_dist, synchronized) - _check_error() - return result if result != _ffi.NULL else None - - -def tpoint_AsMVTGeom(temp: 'const Temporal *', bounds: 'const STBox *', extent: 'int32_t', buffer: 'int32_t', clip_geom: bool, gsarr: 'GSERIALIZED **', timesarr: 'int64 **') -> "Tuple['bool', 'int']": - temp_converted = _ffi.cast('const Temporal *', temp) - bounds_converted = _ffi.cast('const STBox *', bounds) - extent_converted = _ffi.cast('int32_t', extent) - buffer_converted = _ffi.cast('int32_t', buffer) - gsarr_converted = [_ffi.cast('GSERIALIZED *', x) for x in gsarr] - timesarr_converted = [_ffi.cast('int64 *', x) for x in timesarr] - count = _ffi.new('int *') - result = _lib.tpoint_AsMVTGeom(temp_converted, bounds_converted, extent_converted, buffer_converted, clip_geom, gsarr_converted, timesarr_converted, count) - _check_error() - return result if result != _ffi.NULL else None, count[0] - - -def tpoint_to_geo_meas(tpoint: 'const Temporal *', measure: 'const Temporal *', segmentize: bool) -> 'GSERIALIZED **': - tpoint_converted = _ffi.cast('const Temporal *', tpoint) - measure_converted = _ffi.cast('const Temporal *', measure) - out_result = _ffi.new('GSERIALIZED **') - result = _lib.tpoint_to_geo_meas(tpoint_converted, measure_converted, segmentize, out_result) - _check_error() - if result: - return out_result if out_result != _ffi.NULL else None - return None - - From 30d242e961af3f49428f30a05f9d0c07f2dadddc Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 21 Sep 2023 13:43:22 +0200 Subject: [PATCH 031/101] Fix errors (mainly documentation and typing) in set.py --- pymeos/pymeos/collections/base/set.py | 98 ++++++++----------- .../pymeos/collections/time/timestampset.py | 4 +- pymeos/tests/time/timestampset_test.py | 6 +- 3 files changed, 46 insertions(+), 62 deletions(-) diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py index aecf8e79..227e6949 100644 --- a/pymeos/pymeos/collections/base/set.py +++ b/pymeos/pymeos/collections/base/set.py @@ -1,7 +1,6 @@ from __future__ import annotations from abc import ABC, abstractmethod -from datetime import datetime from typing import Optional, Union, List from typing import TypeVar, Type, Callable, Any, TYPE_CHECKING, Iterable @@ -29,18 +28,18 @@ class Set(Collection[T], ABC): _make_function: Callable[[Iterable[Any]], 'CData'] = None # ------------------------- Constructors ---------------------------------- - def __init__(self, string: Optional[str] = None, *, timestamp_list: Optional[List[Union[str, datetime]]] = None, + def __init__(self, string: Optional[str] = None, *, elements: Optional[List[Union[str, T]]] = None, _inner=None): super().__init__() - assert (_inner is not None) or ((string is not None) != (timestamp_list is not None)), \ - "Either string must be not None or timestamp_list must be not" + assert (_inner is not None) or ((string is not None) != (elements is not None)), \ + "Either string must be not None or elements must be not" if _inner is not None: self._inner = _inner elif string is not None: - self._inner = timestampset_in(string) + self._inner = self.__class__._parse_function(string) else: - times = [self.__class__._parse_value_function(ts) for ts in timestamp_list] - self._inner = self.__class__._make_function(times) + parsed_elements = [self.__class__._parse_value_function(ts) for ts in elements] + self._inner = self.__class__._make_function(parsed_elements) def __copy__(self: Self) -> Self: """ @@ -58,12 +57,12 @@ def __copy__(self: Self) -> Self: @classmethod def from_wkb(cls: Type[Self], wkb: bytes) -> Self: """ - Returns a `TimestampSet` from its WKB representation. + Returns a `Set` from its WKB representation. Args: wkb: WKB representation Returns: - A new :class:`TimestampSet` instance + A new :class:`Set` instance MEOS Functions: set_from_wkb @@ -73,12 +72,12 @@ def from_wkb(cls: Type[Self], wkb: bytes) -> Self: @classmethod def from_hexwkb(cls: Type[Self], hexwkb: str) -> Self: """ - Returns a `TimestampSet` from its WKB representation in hex-encoded ASCII. + Returns a `Set` from its WKB representation in hex-encoded ASCII. Args: hexwkb: WKB representation in hex-encoded ASCII Returns: - A new :class:`TimestampSet` instance + A new :class:`Set` instance MEOS Functions: set_from_hexwkb @@ -93,9 +92,6 @@ def __str__(self): Returns: A new :class:`str` instance - - MEOS Functions: - timestampset_out """ raise NotImplementedError() @@ -138,7 +134,7 @@ def as_hexwkb(self) -> str: @abstractmethod def to_spanset(self) -> SpanSet: """ - Returns a PeriodSet that contains a Period for each Timestamp in ``self``. + Returns a SpanSet that contains a Span for each element in ``self``. Returns: A new :class:`PeriodSet` instance @@ -165,7 +161,7 @@ def to_span(self) -> Span: def num_elements(self) -> int: """ - Returns the number of timestamps in ``self``. + Returns the number of elements in ``self``. Returns: An :class:`int` @@ -177,36 +173,27 @@ def num_elements(self) -> int: @abstractmethod def start_element(self) -> T: """ - Returns the first timestamp in ``self``. + Returns the first element in ``self``. Returns: - A :class:`datetime` instance - - MEOS Functions: - timestampset_start_timestamp + A :class:`T` instance """ raise NotImplementedError() @abstractmethod def end_element(self) -> T: """ - Returns the last timestamp in ``self``. + Returns the last element in ``self``. Returns: - A :class:`datetime` instance - - MEOS Functions: - timestampset_end_timestamp + A :class:`T` instance """ raise NotImplementedError() @abstractmethod def element_n(self, n: int) -> T: """ - Returns the n-th timestamp in ``self``. + Returns the n-th element in ``self``. Returns: - A :class:`datetime` instance - - MEOS Functions: - timestampset_timestamp_n + A :class:`T` instance """ if n < 0 or n >= self.num_elements(): raise IndexError(f'Index {n} out of bounds') @@ -214,12 +201,9 @@ def element_n(self, n: int) -> T: @abstractmethod def elements(self) -> List[T]: """ - Returns the list of distinct timestamps in ``self``. + Returns the list of distinct elements in ``self``. Returns: - A :class:`list[datetime]` instance - - MEOS Functions: - timestampset_timestamps + A :class:`list[T]` instance """ raise NotImplementedError() @@ -382,7 +366,7 @@ def is_left(self, other) -> bool: def is_over_or_left(self, other) -> bool: """ Returns whether ``self`` is to the left of ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). + at the same value). Args: other: temporal object to compare with @@ -391,7 +375,7 @@ def is_over_or_left(self, other) -> bool: True if before, False otherwise MEOS Functions: - overbefore_period_timestamp, overleft_span_span, overleft_span_spanset + overleft_span_span, overleft_span_spanset """ from .span import Span from .spanset import SpanSet @@ -406,17 +390,17 @@ def is_over_or_left(self, other) -> bool: def is_over_or_right(self, other) -> bool: """ - Returns whether ``self`` is to the right of ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is to the right of ``other`` allowing overlap. That is, ``self`` starts after ``other`` + starts (or at the same value). Args: other: temporal object to compare with Returns: - True if overlapping or after, False otherwise + True if overlapping or to the right, False otherwise MEOS Functions: - overafter_period_timestamp, overright_span_span, overright_span_spanset + overright_span_span, overright_span_spanset """ from .span import Span from .spanset import SpanSet @@ -431,17 +415,17 @@ def is_over_or_right(self, other) -> bool: def is_right(self, other) -> bool: """ - Returns whether ``self`` is strictly to the right of ``other``. That is, the first timestamp in ``self`` - is after ``other``. + Returns whether ``self`` is strictly to the right of ``other``. That is, the first element in ``self`` + is to the right ``other``. Args: other: temporal object to compare with Returns: - True if after, False otherwise + True if right, False otherwise MEOS Functions: - overbefore_timestamp_timestampset, right_set_set, right_span_span, right_span_spanset + right_set_set, right_span_span, right_span_spanset """ from .span import Span from .spanset import SpanSet @@ -463,7 +447,7 @@ def distance(self, other) -> float: other: object to compare with Returns: - A :class:`datetime.float` instance + A :class:`float` instance MEOS Functions: distance_set_set, distance_span_span, distance_spanset_span @@ -489,7 +473,7 @@ def intersection(self, other): other: temporal object to intersect with Returns: - A :class:`Time` instance. The actual class depends on ``other``. + A :class:`Collection` instance. The actual class depends on ``other``. MEOS Functions: intersection_set_set, intersection_spanset_span, intersection_spanset_spanset @@ -513,7 +497,7 @@ def __mul__(self, other): other: temporal object to intersect with Returns: - A :class:`Time` instance. The actual class depends on ``other``. + A :class:`Collection` instance. The actual class depends on ``other``. MEOS Functions: intersection_set_set, intersection_spanset_span, intersection_spanset_spanset @@ -529,10 +513,10 @@ def minus(self, other): other: temporal object to diff with Returns: - A :class:`Time` instance. The actual class depends on ``other``. + A :class:`Collection` instance. The actual class depends on ``other``. MEOS Functions: - minus_timestampset_timestamp, minus_set_set, minus_spanset_span, minus_spanset_spanset + minus_set_set, minus_spanset_span, minus_spanset_spanset """ from .span import Span from .spanset import SpanSet @@ -553,10 +537,10 @@ def __sub__(self, other): other: temporal object to diff with Returns: - A :class:`Time` instance. The actual class depends on ``other``. + A :class:`Collection` instance. The actual class depends on ``other``. MEOS Functions: - minus_timestampset_timestamp, minus_set_set, minus_spanset_span, minus_spanset_spanset + minus_set_set, minus_spanset_span, minus_spanset_spanset """ return self.minus(other) @@ -569,10 +553,10 @@ def union(self, other): other: temporal object to merge with Returns: - A :class:`Time` instance. The actual class depends on ``other``. + A :class:`Collection` instance. The actual class depends on ``other``. MEOS Functions: - union_timestampset_timestamp, union_set_set, union_spanset_span, union_spanset_spanset + union_set_set, union_spanset_span, union_spanset_spanset """ from .span import Span from .spanset import SpanSet @@ -593,10 +577,10 @@ def __add__(self, other): other: temporal object to merge with Returns: - A :class:`Time` instance. The actual class depends on ``other``. + A :class:`Collection` instance. The actual class depends on ``other``. MEOS Functions: - union_timestampset_timestamp, union_set_set, union_spanset_span, union_spanset_spanset + union_set_set, union_spanset_span, union_spanset_spanset """ return self.union(other) diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py index a0290d9d..2c940799 100644 --- a/pymeos/pymeos/collections/time/timestampset.py +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -30,8 +30,8 @@ class TimestampSet(Set[datetime], TimeCollection): which can be instances of ``str`` or ``datetime``. The composing timestamps must be given in increasing order. - >>> TimestampSet(timestamp_list=['2019-09-08 00:00:00+01', '2019-09-10 00:00:00+01', '2019-09-11 00:00:00+01']) - >>> TimestampSet(timestamp_list=[parse('2019-09-08 00:00:00+01'), parse('2019-09-10 00:00:00+01'), parse('2019-09-11 00:00:00+01')]) + >>> TimestampSet(elements=['2019-09-08 00:00:00+01', '2019-09-10 00:00:00+01', '2019-09-11 00:00:00+01']) + >>> TimestampSet(elements=[parse('2019-09-08 00:00:00+01'), parse('2019-09-10 00:00:00+01'), parse('2019-09-11 00:00:00+01')]) """ diff --git a/pymeos/tests/time/timestampset_test.py b/pymeos/tests/time/timestampset_test.py index 489b751d..cacd4bfe 100644 --- a/pymeos/tests/time/timestampset_test.py +++ b/pymeos/tests/time/timestampset_test.py @@ -26,9 +26,9 @@ def test_string_constructor(self): datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) def test_list_constructor(self): - ts_set = TimestampSet(timestamp_list=[datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) + ts_set = TimestampSet(elements=[datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) self.assert_timestampset_equality(ts_set, [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) From 1afdd1c95f4ec7e2a2ee9bbef0c770a1313f724e Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 21 Sep 2023 17:43:23 +0200 Subject: [PATCH 032/101] Improve tests --- pymeos/pymeos/boxes/stbox.py | 8 +++--- pymeos/pymeos/boxes/tbox.py | 8 +++--- pymeos/pymeos/main/tfloat.py | 25 ++++++++++------- pymeos/pymeos/main/tint.py | 25 ++++++++++------- pymeos/pymeos/main/tpoint.py | 40 +++++++++++++++++----------- pymeos/pymeos/temporal/temporal.py | 18 +++++++------ pymeos/tests/main/tbool_test.py | 2 +- pymeos/tests/main/tfloat_test.py | 22 ++++++++++++--- pymeos/tests/main/tgeogpoint_test.py | 2 +- pymeos/tests/main/tgeompoint_test.py | 4 +-- pymeos/tests/main/tint_test.py | 20 ++++++++++++-- pymeos/tests/main/ttext_test.py | 2 +- 12 files changed, 115 insertions(+), 61 deletions(-) diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index 3df54bbb..1df00dfc 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -1238,8 +1238,7 @@ def tile(self, size: Optional[float] = None, origin: The origin of the spatial tiling. If not provided, the origin will be (0, 0, 0). start: The start time of the temporal tiling. If not provided, - the start time will be the starting time of the `STBox` - time dimension. + the start time used by default is Monday, January 3, 2000. Returns: A 4D matrix (XxYxZxT) of `STBox` instances. @@ -1254,7 +1253,7 @@ def tile(self, size: Optional[float] = None, else None st = datetime_to_timestamptz(start) if isinstance(start, datetime) \ else pg_timestamptz_in(start, -1) if isinstance(start, str) \ - else datetime_to_timestamptz(self.tmin()) if self.has_t() \ + else pg_timestamptz_in('2000-01-03', -1) if self.has_t() \ else 0 gs = geo_to_gserialized(origin, self.geodetic()) if origin is not None \ else pgis_geography_in('Point(0 0 0)', -1) if self.geodetic() \ @@ -1289,8 +1288,7 @@ def tile_flat(self, size: float, origin: The origin of the spatial tiling. If not provided, the origin will be (0, 0, 0). start: The start time of the temporal tiling. If not provided, the - start time will be the starting time of the `STBox` time - dimension. + start time used by default is Monday, January 3, 2000. Returns: A flat list of `STBox` instances. diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index a9674ac0..493c5512 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -1069,7 +1069,8 @@ def tile(self, size: float, duration: Union[timedelta, str], size: size of the numeric dimension of the tiles duration: size of the temporal dimenstion of the tiles origin: origin of the numeric dimension of the tiles - start: origin of the temporal dimension of the tiles + start: origin of the temporal dimension of the tiles. If None, the + start time used by default is Monday, January 3, 2000. Returns: A 2d array of :class:`TBox` instances. @@ -1081,7 +1082,7 @@ def tile(self, size: float, duration: Union[timedelta, str], else pg_interval_in(duration, -1) st = datetime_to_timestamptz(start) if isinstance(start, datetime) \ else pg_timestamptz_in(start, -1) if isinstance(start, str) \ - else tbox_tmin(self._inner) + else pg_timestamptz_in('2000-01-03', -1) tiles, rows, columns = tbox_tile_list(self._inner, size, dt, origin, st) return [[TBox(_inner=tiles + (c * rows + r)) for c in range(columns)] for r in range(rows)] @@ -1096,7 +1097,8 @@ def tile_flat(self, size: float, duration: Union[timedelta, str], size: size of the numeric dimension of the tiles duration: size of the temporal dimenstion of the tiles origin: origin of the numeric dimension of the tiles - start: origin of the temporal dimension of the tiles + start: origin of the temporal dimension of the tiles. If None, the + start time used by default is Monday, January 3, 2000. Returns: An array of :class:`TBox` instances. diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index 01018304..a1fdec45 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -805,7 +805,7 @@ def round(self, maxdd : int = 0) -> TFloat: maxdd)) # ------------------------- Split Operations ------------------------------ - def value_split(self, size: float, start: Optional[float] = 0) -> \ + def value_split(self, size: float, start: Optional[float] = 0.0) -> \ List[Temporal]: """ Splits `self` into fragments with respect to value buckets @@ -825,17 +825,21 @@ def value_split(self, size: float, start: Optional[float] = 0) -> \ return [_TemporalFactory.create_temporal(tiles[i]) for i in \ range(new_count)] - def time_value_split(self, value_start: float, value_size: float, - time_start: Union[str, datetime], - duration: Union[str, timedelta]) -> List[Temporal]: + def value_time_split(self, value_size: float, + duration: Union[str, timedelta], + value_start: Optional[float] = 0.0, + time_start: Optional[Union[str, datetime]] = None) -> \ + List[Temporal]: """ Splits `self` into fragments with respect to value and period buckets. Args: - value_start: Start value of the first value bucket. value_size: Size of the value buckets. - time_start: Start time of the first period bucket. duration: Duration of the period buckets. + value_start: Start value of the first value bucket. If None, the + start value used by default is 0 + time_start: Start time of the first period bucket. If None, the + start time used by default is Monday, January 3, 2000. Returns: A list of temporal floats. @@ -843,9 +847,12 @@ def time_value_split(self, value_start: float, value_size: float, MEOS Functions: tfloat_value_time_split """ - st = datetime_to_timestamptz(time_start) \ - if isinstance(time_start, datetime) \ - else pg_timestamptz_in(time_start, -1) + if time_start is None: + st = pg_timestamptz_in('2000-01-03', -1) + else: + st = datetime_to_timestamptz(time_start) \ + if isinstance(time_start, datetime) \ + else pg_timestamptz_in(time_start, -1) dt = timedelta_to_interval(duration) \ if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index e01fbd43..c29d6377 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -749,27 +749,34 @@ def value_split(self, size: int, start: Optional[int] = 0) -> List[TInt]: tiles, new_count = tint_value_split(self._inner, size, start) return [Temporal._factory(tiles[i]) for i in range(new_count)] - def time_value_split(self, value_start: int, value_size: int, - time_start: Union[str, datetime], - duration: Union[str, timedelta]) -> List[TInt]: + def value_time_split(self, value_size: int, + duration: Union[str, timedelta], + value_start: Optional[int] = 0, + time_start: Optional[Union[str, datetime]] = None) -> \ + List[TInt]: """ Splits `self` into fragments with respect to value and period buckets. Args: - value_start: Start value of the first value bucket. value_size: Size of the value buckets. - time_start: Start time of the first period bucket. duration: Duration of the period buckets. + value_start: Start value of the first value bucket. If None, the + start value used by default is 0 + time_start: Start time of the first period bucket. If None, the + start time used by default is Monday, January 3, 2000. Returns: - A list of temporal ints. + A list of temporal integers. MEOS Functions: tint_value_time_split """ - st = datetime_to_timestamptz(time_start) \ - if isinstance(time_start, datetime) \ - else pg_timestamptz_in(time_start, -1) + if time_start is None: + st = pg_timestamptz_in('2000-01-03', -1) + else: + st = datetime_to_timestamptz(time_start) \ + if isinstance(time_start, datetime) \ + else pg_timestamptz_in(time_start, -1) dt = timedelta_to_interval(duration) \ if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 072d1473..9c739355 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -1023,16 +1023,20 @@ def tile(self, size: float, duration: Optional[Union[timedelta, str]] = None, origin: Optional[Union[shpb.BaseGeometry, pg.Geometry]] = None, start: Union[datetime, str, None] = None) -> List[List[List[List[TG]]]]: """ - Split the temporal point into segments following the tiling of the bounding box. + Split the temporal point into segments following the tiling of the + bounding box. Args: - size: The size of the spatial tiles. If `self` has a spatial dimension and this - argument is not provided, the tiling will be only temporal. - duration: The duration of the temporal tiles. If `self` has a time dimension and this - argument is not provided, the tiling will be only spatial. - origin: The origin of the spatial tiling. If not provided, the origin will be (0, 0, 0). - start: The start time of the temporal tiling. If not provided, the start time will be the starting time of - the `STBox` time dimension. + size: The size of the spatial tiles. If `self` has a spatial + dimension and this argument is not provided, the tiling will be + only temporal. + duration: The duration of the temporal tiles. If `self` has a time + dimension and this argument is not provided, the tiling will be + only spatial. + origin: The origin of the spatial tiling. If not provided, the + origin will be (0, 0, 0). + start: The start time of the temporal tiling. If not provided, + the start time used by default is Monday, January 3, 2000. Returns: A 4D matrix (XxYxZxT) of :class:`TPoint` objects. @@ -1050,16 +1054,20 @@ def tile_flat(self, size: float, duration: Optional[Union[timedelta, str]] = Non origin: Optional[Union[shpb.BaseGeometry, pg.Geometry]] = None, start: Union[datetime, str, None] = None) -> List[TG]: """ - Split the temporal point into segments following the tiling of the bounding box. + Split the temporal point into segments following the tiling of the + bounding box. Args: - size: The size of the spatial tiles. If `self` has a spatial dimension and this - argument is not provided, the tiling will be only temporal. - duration: The duration of the temporal tiles. If `self` has a time dimension and this - argument is not provided, the tiling will be only spatial. - origin: The origin of the spatial tiling. If not provided, the origin will be (0, 0, 0). - start: The start time of the temporal tiling. If not provided, the start time will be the starting time of - the `STBox` time dimension. + size: The size of the spatial tiles. If `self` has a spatial + dimension and this argument is not provided, the tiling will be + only temporal. + duration: The duration of the temporal tiles. If `self` has a time + dimension and this argument is not provided, the tiling will be + only spatial. + origin: The origin of the spatial tiling. If not provided, the + origin will be (0, 0, 0). + start: The start time of the temporal tiling. If not provided, the + the start time used by default is Monday, January 3, 2000. Returns: A :class:`list` of :class:`TPoint` objects. diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index 81df5889..51ee13f9 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -557,13 +557,13 @@ def temporal_sample(self, duration: Union[str, timedelta], duration: A :class:`str` or :class:`timedelta` with the duration of the temporal tiles. start: A :class:`str` or :class:`datetime` with the start time of - the temporal tiles. If None, the start time of `self` is used. - + the temporal tiles. If None, the start time used by default is + Monday, January 3, 2000. MEOS Functions: temporal_tsample """ if start is None: - st = temporal_start_timestamp(self._inner) + st = pg_timestamptz_in('2000-01-03', -1) elif isinstance(start, datetime): st = datetime_to_timestamptz(start) else: @@ -584,8 +584,8 @@ def temporal_precision(self, duration: Union[str, timedelta], duration: A :class:`str` or :class:`timedelta` with the duration of the temporal tiles. start: A :class:`str` or :class:`datetime` with the start time of - the temporal tiles. If None, the start time Monday, January 3, - 2000 is used. + the temporal tiles. If None, the start time used by default is + Monday, January 3, 2000. MEOS Functions: temporal_tprecision @@ -1177,7 +1177,8 @@ def time_split(self, duration: Union[str, timedelta], duration: A :class:`str` or :class:`timedelta` with the duration of the temporal tiles. start: A :class:`str` or :class:`datetime` with the start time of - the temporal tiles. If None, the start time of `self` is used. + the temporal tiles. If None, the start time used by default is + Monday, January 3, 2000. Returns: A list of temporal objects of the same subtype as `self`. @@ -1186,7 +1187,7 @@ def time_split(self, duration: Union[str, timedelta], temporal_time_split """ if start is None: - st = temporal_start_timestamp(self._inner) + st = pg_timestamptz_in('2000-01-03', -1) else: st = datetime_to_timestamptz(start) \ if isinstance(start, datetime) \ @@ -1217,7 +1218,8 @@ def time_split_n(self, n: int) -> List[TG]: if self.end_timestamp() == self.start_timestamp(): return [self] st = temporal_start_timestamp(self._inner) - dt = timedelta_to_interval((self.end_timestamp() - self.start_timestamp()) / n) + dt = timedelta_to_interval((self.end_timestamp() - + self.start_timestamp()) / n) tiles, new_count = temporal_time_split(self._inner, dt, st) from ..factory import _TemporalFactory return [_TemporalFactory.create_temporal(tiles[i]) \ diff --git a/pymeos/tests/main/tbool_test.py b/pymeos/tests/main/tbool_test.py index d4605492..d1418127 100644 --- a/pymeos/tests/main/tbool_test.py +++ b/pymeos/tests/main/tbool_test.py @@ -877,7 +877,7 @@ def test_shift_scale_time(self): 'Sequence Set days', 'Sequence Set hours'] ) def test_temporal_sample(self, tint, delta, expected): - assert tint.temporal_sample(delta) == expected + assert tint.temporal_sample(delta, '2019-09-01') == expected class TestTBoolModifications(TestTBool): diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index 1355cd5f..ee4a735e 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -1100,7 +1100,7 @@ def test_shift_scale_time(self): 'Sequence Set days', 'Sequence Set hours'] ) def test_temporal_sample(self, tfloat, delta, expected): - assert tfloat.temporal_sample(delta) == expected + assert tfloat.temporal_sample(delta, '2019-09-01') == expected @pytest.mark.parametrize( 'tfloat, delta, expected', @@ -2043,8 +2043,6 @@ class TestTFloatSplitOperations(TestTFloat): def test_value_split(self, temporal, expected): assert temporal.value_split(2) == expected - ## The PyMEOS function uses as default origin the initial timestamp of the - ## temporal value while in MEOS the default origin is Monday Janury 3, 2000 @pytest.mark.parametrize( 'temporal, expected', [ @@ -2057,7 +2055,7 @@ def test_value_split(self, temporal, expected): ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_time_split(self, temporal, expected): - assert temporal.time_split(timedelta(days=2)) == expected + assert temporal.time_split(timedelta(days=2), '2019-09-01') == expected @pytest.mark.parametrize( 'temporal, expected', @@ -2075,6 +2073,22 @@ def test_time_split(self, temporal, expected): def test_time_split_n(self, temporal, expected): assert temporal.time_split_n(2) == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tfi, [TFloatInst('1@2019-09-01')]), + (tfds, [TFloatSeq('{1@2019-09-01}'), TFloatSeq('{2@2019-09-02}')]), + (tfs, [TFloatSeq('[1@2019-09-01, 2@2019-09-02)'), + TFloatSeq('[2@2019-09-02]')]), + (tfss, [TFloatSeq('{[1@2019-09-01, 2@2019-09-02)}'), + TFloatSeq('{[1@2019-09-03, 1@2019-09-05]}'), + TFloatSeq('{[2@2019-09-02]}')]), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_value_time_split(self, temporal, expected): + assert temporal.value_time_split(2.0, timedelta(days=2), 0.0, '2019-09-01') == expected + class TestTFloatComparisons(TestTFloat): tf = TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]') diff --git a/pymeos/tests/main/tgeogpoint_test.py b/pymeos/tests/main/tgeogpoint_test.py index 4b232b1e..1fa135e1 100644 --- a/pymeos/tests/main/tgeogpoint_test.py +++ b/pymeos/tests/main/tgeogpoint_test.py @@ -1226,7 +1226,7 @@ def test_shift_scale_time(self): ] ) def test_temporal_sample(self, tpoint, delta, expected): - assert tpoint.temporal_sample(delta).round(1) == expected + assert tpoint.temporal_sample(delta, '2019-09-01').round(1) == expected @pytest.mark.parametrize( 'tpoint, delta, expected', diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index 8592b2e3..655093f4 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -1278,7 +1278,7 @@ def test_shift_scale_time(self): ] ) def test_temporal_sample(self, tpoint, delta, expected): - assert tpoint.temporal_sample(delta) == expected + assert tpoint.temporal_sample(delta, '2019-09-01') == expected @pytest.mark.parametrize( 'tpoint, delta, expected', @@ -2146,7 +2146,7 @@ class TestTGeomPointSplitOperations(TestTGeomPoint): ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_time_split(self, temporal, expected): - assert temporal.time_split(timedelta(days=2)) == expected + assert temporal.time_split(timedelta(days=2), '2019-09-01') == expected @pytest.mark.parametrize( 'temporal, expected', diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index 6cf88e65..2340ac21 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -1057,7 +1057,7 @@ def test_shift_scale_time(self): 'Sequence Set days', 'Sequence Set hours'] ) def test_temporal_sample(self, tint, delta, expected): - assert tint.temporal_sample(delta) == expected + assert tint.temporal_sample(delta, '2019-09-01') == expected @pytest.mark.parametrize( 'temporal, expected', @@ -1917,7 +1917,23 @@ def test_value_split(self, temporal, expected): ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_time_split(self, temporal, expected): - assert temporal.time_split(timedelta(days=2)) == expected + assert temporal.time_split(timedelta(days=2), '2019-09-01') == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tii, [TIntInst('1@2019-09-01')]), + (tids, [TIntSeq('{1@2019-09-01}'), TIntSeq('{2@2019-09-02}')]), + (tis, [TIntSeq('[1@2019-09-01, 1@2019-09-02)'), + TIntSeq('[2@2019-09-02]')]), + (tiss, [TIntSeq('{[1@2019-09-01, 1@2019-09-02)}'), + TIntSeq('{[1@2019-09-03, 1@2019-09-05]}'), + TIntSeq('{[2@2019-09-02]}')]), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_value_time_split(self, temporal, expected): + assert temporal.value_time_split(2, timedelta(days=2), 0, '2019-09-01') == expected class TestTIntComparisons(TestTInt): diff --git a/pymeos/tests/main/ttext_test.py b/pymeos/tests/main/ttext_test.py index ea7d57c2..0f72c129 100644 --- a/pymeos/tests/main/ttext_test.py +++ b/pymeos/tests/main/ttext_test.py @@ -876,7 +876,7 @@ def test_shift_scale(self): 'Sequence Set days', 'Sequence Set hours'] ) def test_temporal_sample(self, tint, delta, expected): - assert tint.temporal_sample(delta) == expected + assert tint.temporal_sample(delta, '2019-09-01') == expected class TestTTextModifications(TestTText): From 854d4b4fb52c4577c13a4e38949ac16335b6d010 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sat, 23 Sep 2023 10:16:57 +0200 Subject: [PATCH 033/101] Improve tests --- pymeos/pymeos/main/tfloat.py | 12 +-- pymeos/pymeos/main/tint.py | 10 +- pymeos/pymeos/main/tpoint.py | 76 +++++++++++++ pymeos/pymeos/temporal/temporal.py | 14 +-- pymeos/tests/main/tgeompoint_test.py | 37 ++++++- pymeos_cffi/pymeos_cffi/__init__.py | 2 + .../builder/build_pymeos_functions.py | 30 ++++-- pymeos_cffi/pymeos_cffi/builder/meos.h | 19 ++-- pymeos_cffi/pymeos_cffi/functions.py | 100 ++++++++++++------ 9 files changed, 230 insertions(+), 70 deletions(-) diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index a1fdec45..4dbd0d0a 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -820,10 +820,10 @@ def value_split(self, size: float, start: Optional[float] = 0.0) -> \ MEOS Functions: tfloat_value_split """ - tiles, new_count = tfloat_value_split(self._inner, size, start) + fragments, values, count = tfloat_value_split(self._inner, size, start) from ..factory import _TemporalFactory - return [_TemporalFactory.create_temporal(tiles[i]) for i in \ - range(new_count)] + return [_TemporalFactory.create_temporal(fragments[i]) for i in \ + range(count)] def value_time_split(self, value_size: float, duration: Union[str, timedelta], @@ -856,9 +856,9 @@ def value_time_split(self, value_size: float, dt = timedelta_to_interval(duration) \ if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) - tiles, new_count = tfloat_value_time_split(self._inner, value_size, - value_start, dt, st) - return [Temporal._factory(tiles[i]) for i in range(new_count)] + fragments, values, times, count = tfloat_value_time_split(self._inner, value_size, + dt, value_start, st) + return [Temporal._factory(fragments[i]) for i in range(count)] # ------------------------- Database Operations --------------------------- @staticmethod diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index c29d6377..00b98368 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -746,8 +746,8 @@ def value_split(self, size: int, start: Optional[int] = 0) -> List[TInt]: MEOS Functions: tint_value_split """ - tiles, new_count = tint_value_split(self._inner, size, start) - return [Temporal._factory(tiles[i]) for i in range(new_count)] + fragments, values, count = tint_value_split(self._inner, size, start) + return [Temporal._factory(fragments[i]) for i in range(count)] def value_time_split(self, value_size: int, duration: Union[str, timedelta], @@ -780,9 +780,9 @@ def value_time_split(self, value_size: int, dt = timedelta_to_interval(duration) \ if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) - tiles, new_count = tint_value_time_split(self._inner, value_size, - value_start, dt, st) - return [Temporal._factory(tiles[i]) for i in range(new_count)] + fragments, values, times, count = tint_value_time_split(self._inner, + value_size, dt, value_start, st) + return [Temporal._factory(fragments[i]) for i in range(count)] # ------------------------- Database Operations --------------------------- @staticmethod diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 9c739355..251e9d90 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -1080,6 +1080,82 @@ def tile_flat(self, size: float, duration: Optional[Union[timedelta, str]] = Non tiles = bbox.tile_flat(size, duration, origin, start) return [x for x in (self.at(tile) for tile in tiles) if x] + # ------------------------- Split Operations ------------------------------ + def space_split(self, xsize: float, ysize: Optional[float] = None, + zsize: Optional[float] = None, origin: Optional[Geometry] = None, + bitmatrix: Optional[bool] = False) -> List[Temporal]: + """ + Splits `self` into fragments with respect to space buckets + + Args: + xsize: Size of the x dimension. + ysize: Size of the y dimension. + zsize: Size of the z dimension. + origin: The origin of the spatial tiling. If not provided, the + origin will be (0, 0, 0). + + Returns: + A list of temporal points. + + MEOS Functions: + tpoint_value_split + """ + ysz = ysize if ysize is not None else xsize + zsz = zsize if zsize is not None else xsize + gs = geo_to_gserialized(origin, self.geodetic()) if origin is not None \ + else pgis_geography_in('Point(0 0 0)', -1) \ + if isinstance(self, TGeogPoint) \ + else pgis_geometry_in('Point(0 0 0)', -1) + fragments, values, times, count = tpoint_space_split(self._inner, + xsize, ysz, zsz, gs, bitmatrix) + from ..factory import _TemporalFactory + return [_TemporalFactory.create_temporal(fragments[i]) for i in \ + range(count)] + + def space_time_split(self, xsize: float, duration: Union[str, timedelta], + ysize: Optional[float] = None, zsize: Optional[float] = None, + origin: Optional[Geometry] = None, + time_start: Optional[Union[str, datetime]] = None, + bitmatrix: Optional[bool] = False) -> List[Temporal]: + """ + Splits `self` into fragments with respect to space and period buckets. + + Args: + xsize: Size of the x dimension. + ysize: Size of the y dimension. + zsize: Size of the z dimension. + duration: Duration of the period buckets. + origin: The origin of the spatial tiling. If not provided, the + origin will be (0, 0, 0). + time_start: Start time of the first period bucket. If None, the + start time used by default is Monday, January 3, 2000. + + Returns: + A list of temporal floats. + + MEOS Functions: + tfloat_value_time_split + """ + ysz = ysize if ysize is not None else xsize + zsz = zsize if zsize is not None else xsize + dt = timedelta_to_interval(duration) \ + if isinstance(duration, timedelta) \ + else pg_interval_in(duration, -1) + gs = geo_to_gserialized(origin, self.geodetic()) if origin is not None \ + else pgis_geography_in('Point(0 0 0)', -1) \ + if isinstance(self, TGeogPoint) \ + else pgis_geometry_in('Point(0 0 0)', -1) + if time_start is None: + st = pg_timestamptz_in('2000-01-03', -1) + else: + st = datetime_to_timestamptz(time_start) \ + if isinstance(time_start, datetime) \ + else pg_timestamptz_in(time_start, -1) + fragments, points, times, count = tpoint_space_time_split(self._inner, + xsize, ysz, zsz, dt, gs, st, bitmatrix) + return [Temporal._factory(fragments[i]) for i in range(count)] + + class TPointInst(TInstant[shpb.BaseGeometry, TG, TI, TS, TSS], TPoint[TG, TI, TS, TSS], ABC): """ diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index 51ee13f9..b217d90c 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -1195,10 +1195,10 @@ def time_split(self, duration: Union[str, timedelta], dt = timedelta_to_interval(duration) \ if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) - tiles, new_count = temporal_time_split(self._inner, dt, st) + fragments, times, count = temporal_time_split(self._inner, dt, st) from ..factory import _TemporalFactory - return [_TemporalFactory.create_temporal(tiles[i]) \ - for i in range(new_count)] + return [_TemporalFactory.create_temporal(fragments[i]) \ + for i in range(count)] def time_split_n(self, n: int) -> List[TG]: """ @@ -1219,11 +1219,11 @@ def time_split_n(self, n: int) -> List[TG]: return [self] st = temporal_start_timestamp(self._inner) dt = timedelta_to_interval((self.end_timestamp() - - self.start_timestamp()) / n) - tiles, new_count = temporal_time_split(self._inner, dt, st) + self.start_timestamp()) / n) + fragments, times, count = temporal_time_split(self._inner, dt, st) from ..factory import _TemporalFactory - return [_TemporalFactory.create_temporal(tiles[i]) \ - for i in range(new_count)] + return [_TemporalFactory.create_temporal(fragments[i]) \ + for i in range(count)] def stops(self, max_distance: Optional[float] = 0.0, min_duration: Optional[timedelta] = timedelta()) -> TSS: diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index 655093f4..8a06f36e 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -2132,8 +2132,6 @@ class TestTGeomPointSplitOperations(TestTGeomPoint): tps = TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') tpss = TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') - # The PyMEOS function uses as default origin the initial timestamp of the - # temporal value while in MEOS the default origin is Monday Janury 3, 2000 @pytest.mark.parametrize( 'temporal, expected', [ @@ -2164,6 +2162,41 @@ def test_time_split(self, temporal, expected): def test_time_split_n(self, temporal, expected): assert temporal.time_split_n(2) == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, [TGeomPointInst('Point(1 1)@2019-09-01')]), + (tpds, [TGeomPointSeq('{Point(1 1)@2019-09-01}'), + TGeomPointSeq('{Point(2 2)@2019-09-02}')]), + (tps, [TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02)'), + TGeomPointSeq('[Point(2 2)@2019-09-02]')]), + (tpss, [TGeomPointSeqSet('{[POINT(1 1)@2019-09-01, POINT(2 2)@2019-09-02),' + '[POINT(1 1)@2019-09-03, POINT(1 1)@2019-09-05]}'), + TGeomPointSeqSet('{[POINT(2 2)@2019-09-02]}')]), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_space_split(self, temporal, expected): + assert temporal.space_split(1.0) == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, [TGeomPointInst('Point(1 1)@2019-09-01')]), + (tpds, [TGeomPointSeq('{Point(1 1)@2019-09-01}'), + TGeomPointSeq('{Point(2 2)@2019-09-02}')]), + (tps, [TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02)'), + TGeomPointSeq('[Point(2 2)@2019-09-02]')]), + (tpss, [TGeomPointSeqSet('{[POINT(1 1)@2019-09-01, POINT(2 2)@2019-09-02),' + '[POINT(1 1)@2019-09-03, POINT(1 1)@2019-09-05]}'), + TGeomPointSeqSet('{[POINT(2 2)@2019-09-02]}')]), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_space_time_split(self, temporal, expected): + assert temporal.space_time_split(1.0, timedelta(days=2), + time_start='2019-09-01') == expected + class TestTGeomPointComparisons(TestTGeomPoint): tp = TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 59eee3f4..bc17e3d6 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -1011,4 +1011,6 @@ 'timestamptz_bucket', 'tint_value_split', 'tint_value_time_split', + 'tpoint_space_split', + 'tpoint_space_time_split', ] diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index dd58f07e..9247c8fc 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -91,17 +91,27 @@ def __init__(self, ctype: str, ptype: str, conversion: Optional[str]) -> None: ('tpoint_value_at_timestamp', 'value'), } -# List of output function parameters in tuples of (function, parameter). All parameters named result are assumed -# to be output parameters, and it's not necessary to list them here. +# List of output function parameters in tuples of (function, parameter). +# All parameters named result are assumed to be output parameters, and it is +# not necessary to list them here. output_parameters = { - ('temporal_time_split', 'buckets'), - ('temporal_time_split', 'newcount'), - ('tint_value_split', 'buckets'), - ('tint_value_split', 'newcount'), - ('tfloat_value_split', 'buckets'), - ('tfloat_value_split', 'newcount'), - ('tint_value_time_split', 'newcount'), - ('tfloat_value_time_split', 'newcount'), + ('temporal_time_split', 'time_buckets'), + ('temporal_time_split', 'count'), + ('tint_value_split', 'value_buckets'), + ('tint_value_split', 'count'), + ('tfloat_value_split', 'value_buckets'), + ('tfloat_value_split', 'count'), + ('tint_value_time_split', 'value_buckets'), + ('tint_value_time_split', 'time_buckets'), + ('tint_value_time_split', 'count'), + ('tfloat_value_time_split', 'value_buckets'), + ('tfloat_value_time_split', 'time_buckets'), + ('tfloat_value_time_split', 'count'), + ('tpoint_space_split', 'space_buckets'), + ('tpoint_space_split', 'count'), + ('tpoint_space_time_split', 'space_buckets'), + ('tpoint_space_time_split', 'time_buckets'), + ('tpoint_space_time_split', 'count'), ('tbox_as_hexwkb', 'size'), ('stbox_as_hexwkb', 'size'), ('tbox_tile_list', 'rows'), diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 5e173e2b..74eb77fb 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -1930,18 +1930,21 @@ extern double temporal_hausdorff_distance(const Temporal *temp1, const Temporal extern double float_bucket(double value, double size, double origin); -extern Span *floatspan_bucket_list(const Span *bounds, double size, double origin, int *newcount); +extern Span *floatspan_bucket_list(const Span *bounds, double size, double origin, int *count); extern int int_bucket(int value, int size, int origin); -extern Span *intspan_bucket_list(const Span *bounds, int size, int origin, int *newcount); -extern Span *period_bucket_list(const Span *bounds, const Interval *duration, TimestampTz origin, int *newcount); +extern Span *intspan_bucket_list(const Span *bounds, int size, int origin, int *count); +extern Span *period_bucket_list(const Span *bounds, const Interval *duration, TimestampTz origin, int *count); extern STBox *stbox_tile_list(const STBox *bounds, double xsize, double ysize, double zsize, const Interval *duration, GSERIALIZED *sorigin, TimestampTz torigin, int **cellcount); extern TBox *tbox_tile_list(const TBox *bounds, double xsize, const Interval *duration, double xorigin, TimestampTz torigin, int *rows, int *columns); -extern Temporal **temporal_time_split(Temporal *temp, Interval *duration, TimestampTz torigin, int *newcount); -extern Temporal **tfloat_value_split(Temporal *temp, double size, double origin, int *newcount); -extern Temporal **tfloat_value_time_split(Temporal *temp, double size, double vorigin, Interval *duration, TimestampTz torigin, int *newcount); +extern Temporal **temporal_time_split(Temporal *temp, Interval *duration, TimestampTz torigin, TimestampTz **time_buckets, int *count); +extern Temporal **tfloat_value_split(Temporal *temp, double size, double origin, double **value_buckets, int *count); +extern Temporal **tfloat_value_time_split(Temporal *temp, double size, Interval *duration, double vorigin, TimestampTz torigin, double **value_buckets, TimestampTz **time_buckets, int *count); extern TimestampTz timestamptz_bucket(TimestampTz timestamp, const Interval *duration, TimestampTz origin); -extern Temporal **tint_value_split(Temporal *temp, int size, int origin, int *newcount); -extern Temporal **tint_value_time_split(Temporal *temp, int size, int vorigin, Interval *duration, TimestampTz torigin, int *newcount); +extern Temporal **tint_value_split(Temporal *temp, int size, int origin, int **value_buckets, int *count); +extern Temporal **tint_value_time_split(Temporal *temp, int size, Interval *duration, int vorigin, TimestampTz torigin, int **value_buckets, TimestampTz **time_buckets, int *count); +extern Temporal **tpoint_space_split(Temporal *temp, float xsize, float ysize, float zsize, GSERIALIZED *sorigin, bool bitmatrix, GSERIALIZED ***space_buckets, int *count); +extern Temporal **tpoint_space_time_split(Temporal *temp, float xsize, float ysize, float zsize, Interval *duration, GSERIALIZED *sorigin, TimestampTz torigin, bool bitmatrix, GSERIALIZED ***space_buckets, TimestampTz **time_buckets, int *count); + diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index bea22af4..8824b336 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -7543,12 +7543,12 @@ def float_bucket(value: float, size: float, origin: float) -> 'double': return result if result != _ffi.NULL else None -def floatspan_bucket_list(bounds: 'const Span *', size: float, origin: float, newcount: 'int *') -> 'Span *': +def floatspan_bucket_list(bounds: 'const Span *', size: float, origin: float) -> "Tuple['Span *', 'int']": bounds_converted = _ffi.cast('const Span *', bounds) - newcount_converted = _ffi.cast('int *', newcount) - result = _lib.floatspan_bucket_list(bounds_converted, size, origin, newcount_converted) + count = _ffi.new('int *') + result = _lib.floatspan_bucket_list(bounds_converted, size, origin, count) _check_error() - return result if result != _ffi.NULL else None + return result if result != _ffi.NULL else None, count[0] def int_bucket(value: int, size: int, origin: int) -> 'int': @@ -7557,22 +7557,22 @@ def int_bucket(value: int, size: int, origin: int) -> 'int': return result if result != _ffi.NULL else None -def intspan_bucket_list(bounds: 'const Span *', size: int, origin: int, newcount: 'int *') -> 'Span *': +def intspan_bucket_list(bounds: 'const Span *', size: int, origin: int) -> "Tuple['Span *', 'int']": bounds_converted = _ffi.cast('const Span *', bounds) - newcount_converted = _ffi.cast('int *', newcount) - result = _lib.intspan_bucket_list(bounds_converted, size, origin, newcount_converted) + count = _ffi.new('int *') + result = _lib.intspan_bucket_list(bounds_converted, size, origin, count) _check_error() - return result if result != _ffi.NULL else None + return result if result != _ffi.NULL else None, count[0] -def period_bucket_list(bounds: 'const Span *', duration: 'const Interval *', origin: int, newcount: 'int *') -> 'Span *': +def period_bucket_list(bounds: 'const Span *', duration: 'const Interval *', origin: int) -> "Tuple['Span *', 'int']": bounds_converted = _ffi.cast('const Span *', bounds) duration_converted = _ffi.cast('const Interval *', duration) origin_converted = _ffi.cast('TimestampTz', origin) - newcount_converted = _ffi.cast('int *', newcount) - result = _lib.period_bucket_list(bounds_converted, duration_converted, origin_converted, newcount_converted) + count = _ffi.new('int *') + result = _lib.period_bucket_list(bounds_converted, duration_converted, origin_converted, count) _check_error() - return result if result != _ffi.NULL else None + return result if result != _ffi.NULL else None, count[0] def stbox_tile_list(bounds: 'const STBox *', xsize: float, ysize: float, zsize: float, duration: "Optional['const Interval *']", sorigin: 'GSERIALIZED *', torigin: int) -> "Tuple['STBox *', 'int *']": @@ -7598,32 +7598,36 @@ def tbox_tile_list(bounds: 'const TBox *', xsize: float, duration: 'const Interv return result if result != _ffi.NULL else None, rows[0], columns[0] -def temporal_time_split(temp: 'Temporal *', duration: 'Interval *', torigin: int) -> "Tuple['Temporal **', 'int']": +def temporal_time_split(temp: 'Temporal *', duration: 'Interval *', torigin: int) -> "Tuple['Temporal **', 'TimestampTz *', 'int']": temp_converted = _ffi.cast('Temporal *', temp) duration_converted = _ffi.cast('Interval *', duration) torigin_converted = _ffi.cast('TimestampTz', torigin) - newcount = _ffi.new('int *') - result = _lib.temporal_time_split(temp_converted, duration_converted, torigin_converted, newcount) + time_buckets = _ffi.new('TimestampTz **') + count = _ffi.new('int *') + result = _lib.temporal_time_split(temp_converted, duration_converted, torigin_converted, time_buckets, count) _check_error() - return result if result != _ffi.NULL else None, newcount[0] + return result if result != _ffi.NULL else None, time_buckets[0], count[0] -def tfloat_value_split(temp: 'Temporal *', size: float, origin: float) -> "Tuple['Temporal **', 'int']": +def tfloat_value_split(temp: 'Temporal *', size: float, origin: float) -> "Tuple['Temporal **', 'double *', 'int']": temp_converted = _ffi.cast('Temporal *', temp) - newcount = _ffi.new('int *') - result = _lib.tfloat_value_split(temp_converted, size, origin, newcount) + value_buckets = _ffi.new('double **') + count = _ffi.new('int *') + result = _lib.tfloat_value_split(temp_converted, size, origin, value_buckets, count) _check_error() - return result if result != _ffi.NULL else None, newcount[0] + return result if result != _ffi.NULL else None, value_buckets[0], count[0] -def tfloat_value_time_split(temp: 'Temporal *', size: float, vorigin: float, duration: 'Interval *', torigin: int) -> "Tuple['Temporal **', 'int']": +def tfloat_value_time_split(temp: 'Temporal *', size: float, duration: 'Interval *', vorigin: float, torigin: int) -> "Tuple['Temporal **', 'double *', 'TimestampTz *', 'int']": temp_converted = _ffi.cast('Temporal *', temp) duration_converted = _ffi.cast('Interval *', duration) torigin_converted = _ffi.cast('TimestampTz', torigin) - newcount = _ffi.new('int *') - result = _lib.tfloat_value_time_split(temp_converted, size, vorigin, duration_converted, torigin_converted, newcount) + value_buckets = _ffi.new('double **') + time_buckets = _ffi.new('TimestampTz **') + count = _ffi.new('int *') + result = _lib.tfloat_value_time_split(temp_converted, size, duration_converted, vorigin, torigin_converted, value_buckets, time_buckets, count) _check_error() - return result if result != _ffi.NULL else None, newcount[0] + return result if result != _ffi.NULL else None, value_buckets[0], time_buckets[0], count[0] def timestamptz_bucket(timestamp: int, duration: 'const Interval *', origin: int) -> 'TimestampTz': @@ -7635,21 +7639,53 @@ def timestamptz_bucket(timestamp: int, duration: 'const Interval *', origin: int return result if result != _ffi.NULL else None -def tint_value_split(temp: 'Temporal *', size: int, origin: int) -> "Tuple['Temporal **', 'int']": +def tint_value_split(temp: 'Temporal *', size: int, origin: int) -> "Tuple['Temporal **', 'int *', 'int']": + temp_converted = _ffi.cast('Temporal *', temp) + value_buckets = _ffi.new('int **') + count = _ffi.new('int *') + result = _lib.tint_value_split(temp_converted, size, origin, value_buckets, count) + _check_error() + return result if result != _ffi.NULL else None, value_buckets[0], count[0] + + +def tint_value_time_split(temp: 'Temporal *', size: int, duration: 'Interval *', vorigin: int, torigin: int) -> "Tuple['Temporal **', 'int *', 'TimestampTz *', 'int']": + temp_converted = _ffi.cast('Temporal *', temp) + duration_converted = _ffi.cast('Interval *', duration) + torigin_converted = _ffi.cast('TimestampTz', torigin) + value_buckets = _ffi.new('int **') + time_buckets = _ffi.new('TimestampTz **') + count = _ffi.new('int *') + result = _lib.tint_value_time_split(temp_converted, size, duration_converted, vorigin, torigin_converted, value_buckets, time_buckets, count) + _check_error() + return result if result != _ffi.NULL else None, value_buckets[0], time_buckets[0], count[0] + + +def tpoint_space_split(temp: 'Temporal *', xsize: 'float', ysize: 'float', zsize: 'float', sorigin: 'GSERIALIZED *', bitmatrix: bool) -> "Tuple['Temporal **', 'GSERIALIZED **', 'int']": temp_converted = _ffi.cast('Temporal *', temp) - newcount = _ffi.new('int *') - result = _lib.tint_value_split(temp_converted, size, origin, newcount) + xsize_converted = _ffi.cast('float', xsize) + ysize_converted = _ffi.cast('float', ysize) + zsize_converted = _ffi.cast('float', zsize) + sorigin_converted = _ffi.cast('GSERIALIZED *', sorigin) + space_buckets = _ffi.new('GSERIALIZED **') + count = _ffi.new('int *') + result = _lib.tpoint_space_split(temp_converted, xsize_converted, ysize_converted, zsize_converted, sorigin_converted, bitmatrix, space_buckets, count) _check_error() - return result if result != _ffi.NULL else None, newcount[0] + return result if result != _ffi.NULL else None, space_buckets[0], count[0] -def tint_value_time_split(temp: 'Temporal *', size: int, vorigin: int, duration: 'Interval *', torigin: int) -> "Tuple['Temporal **', 'int']": +def tpoint_space_time_split(temp: 'Temporal *', xsize: 'float', ysize: 'float', zsize: 'float', duration: 'Interval *', sorigin: 'GSERIALIZED *', torigin: int, bitmatrix: bool) -> "Tuple['Temporal **', 'GSERIALIZED **', 'TimestampTz *', 'int']": temp_converted = _ffi.cast('Temporal *', temp) + xsize_converted = _ffi.cast('float', xsize) + ysize_converted = _ffi.cast('float', ysize) + zsize_converted = _ffi.cast('float', zsize) duration_converted = _ffi.cast('Interval *', duration) + sorigin_converted = _ffi.cast('GSERIALIZED *', sorigin) torigin_converted = _ffi.cast('TimestampTz', torigin) - newcount = _ffi.new('int *') - result = _lib.tint_value_time_split(temp_converted, size, vorigin, duration_converted, torigin_converted, newcount) + space_buckets = _ffi.new('GSERIALIZED **') + time_buckets = _ffi.new('TimestampTz **') + count = _ffi.new('int *') + result = _lib.tpoint_space_time_split(temp_converted, xsize_converted, ysize_converted, zsize_converted, duration_converted, sorigin_converted, torigin_converted, bitmatrix, space_buckets, time_buckets, count) _check_error() - return result if result != _ffi.NULL else None, newcount[0] + return result if result != _ffi.NULL else None, space_buckets[0], time_buckets[0], count[0] From 251c64f65273f514443e2fbadb0407763eb960c4 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Sun, 24 Sep 2023 17:03:23 +0200 Subject: [PATCH 034/101] Fix parameter generation for more than 2 levels of indirection. --- pymeos_cffi/pymeos_cffi/__init__.py | 3 +- .../builder/build_pymeos_functions.py | 7 ++-- pymeos_cffi/pymeos_cffi/builder/meos.h | 5 ++- pymeos_cffi/pymeos_cffi/functions.py | 40 +++++++++++-------- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index bc17e3d6..0efe0b74 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -1004,7 +1004,8 @@ 'intspan_bucket_list', 'period_bucket_list', 'stbox_tile_list', - 'tbox_tile_list', + 'tintbox_tile_list', + 'tfloatbox_tile_list', 'temporal_time_split', 'tfloat_value_split', 'tfloat_value_time_split', diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index 9247c8fc..30b5e1c1 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -297,11 +297,10 @@ def get_param(function: str, inner_param: str) -> Optional[Parameter]: param_type = ' '.join(split[:-1]) # Check if parameter is pointer and fix type and name accordingly - if split[-1].startswith('**'): - param_type += ' **' - elif split[-1].startswith('*'): - param_type += ' *' param_name = split[-1].lstrip('*') + pointer_level = len(split[-1]) - len(param_name) + if pointer_level > 0: + param_type += ' ' + '*' * pointer_level # Check if the parameter name is a reserved word and change it if necessary reserved_words = { diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 74eb77fb..07f6acc5 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -1934,8 +1934,9 @@ extern Span *floatspan_bucket_list(const Span *bounds, double size, double origi extern int int_bucket(int value, int size, int origin); extern Span *intspan_bucket_list(const Span *bounds, int size, int origin, int *count); extern Span *period_bucket_list(const Span *bounds, const Interval *duration, TimestampTz origin, int *count); -extern STBox *stbox_tile_list(const STBox *bounds, double xsize, double ysize, double zsize, const Interval *duration, GSERIALIZED *sorigin, TimestampTz torigin, int **cellcount); -extern TBox *tbox_tile_list(const TBox *bounds, double xsize, const Interval *duration, double xorigin, TimestampTz torigin, int *rows, int *columns); +extern STBox *stbox_tile_list(const STBox *bounds, double xsize, double ysize, double zsize, const Interval *duration, GSERIALIZED *sorigin, TimestampTz torigin, int *count); +extern TBox *tintbox_tile_list(const TBox *box, int xsize, const Interval *duration, int xorigin, TimestampTz torigin, int *count); +extern TBox *tfloatbox_tile_list(const TBox *box, double xsize, const Interval *duration, double xorigin, TimestampTz torigin, int *count); extern Temporal **temporal_time_split(Temporal *temp, Interval *duration, TimestampTz torigin, TimestampTz **time_buckets, int *count); extern Temporal **tfloat_value_split(Temporal *temp, double size, double origin, double **value_buckets, int *count); extern Temporal **tfloat_value_time_split(Temporal *temp, double size, Interval *duration, double vorigin, TimestampTz torigin, double **value_buckets, TimestampTz **time_buckets, int *count); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 8824b336..c9bf02a6 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -7575,27 +7575,35 @@ def period_bucket_list(bounds: 'const Span *', duration: 'const Interval *', ori return result if result != _ffi.NULL else None, count[0] -def stbox_tile_list(bounds: 'const STBox *', xsize: float, ysize: float, zsize: float, duration: "Optional['const Interval *']", sorigin: 'GSERIALIZED *', torigin: int) -> "Tuple['STBox *', 'int *']": +def stbox_tile_list(bounds: 'const STBox *', xsize: float, ysize: float, zsize: float, duration: "Optional['const Interval *']", sorigin: 'GSERIALIZED *', torigin: int) -> "Tuple['STBox *', 'int']": bounds_converted = _ffi.cast('const STBox *', bounds) duration_converted = _ffi.cast('const Interval *', duration) if duration is not None else _ffi.NULL sorigin_converted = _ffi.cast('GSERIALIZED *', sorigin) torigin_converted = _ffi.cast('TimestampTz', torigin) - cellcount = _ffi.new('int **') - result = _lib.stbox_tile_list(bounds_converted, xsize, ysize, zsize, duration_converted, sorigin_converted, torigin_converted, cellcount) + count = _ffi.new('int *') + result = _lib.stbox_tile_list(bounds_converted, xsize, ysize, zsize, duration_converted, sorigin_converted, torigin_converted, count) + _check_error() + return result if result != _ffi.NULL else None, count[0] + + +def tintbox_tile_list(box: 'const TBox *', xsize: int, duration: 'const Interval *', xorigin: int, torigin: int) -> "Tuple['TBox *', 'int']": + box_converted = _ffi.cast('const TBox *', box) + duration_converted = _ffi.cast('const Interval *', duration) + torigin_converted = _ffi.cast('TimestampTz', torigin) + count = _ffi.new('int *') + result = _lib.tintbox_tile_list(box_converted, xsize, duration_converted, xorigin, torigin_converted, count) _check_error() - return result if result != _ffi.NULL else None, cellcount[0] + return result if result != _ffi.NULL else None, count[0] -def tbox_tile_list(bounds: 'const TBox *', xsize: float, duration: 'const Interval *', xorigin: 'Optional[float]', torigin: "Optional[int]") -> "Tuple['TBox *', 'int', 'int']": - bounds_converted = _ffi.cast('const TBox *', bounds) +def tfloatbox_tile_list(box: 'const TBox *', xsize: float, duration: 'const Interval *', xorigin: float, torigin: int) -> "Tuple['TBox *', 'int']": + box_converted = _ffi.cast('const TBox *', box) duration_converted = _ffi.cast('const Interval *', duration) - xorigin_converted = xorigin if xorigin is not None else _ffi.NULL - torigin_converted = _ffi.cast('TimestampTz', torigin) if torigin is not None else _ffi.NULL - rows = _ffi.new('int *') - columns = _ffi.new('int *') - result = _lib.tbox_tile_list(bounds_converted, xsize, duration_converted, xorigin_converted, torigin_converted, rows, columns) + torigin_converted = _ffi.cast('TimestampTz', torigin) + count = _ffi.new('int *') + result = _lib.tfloatbox_tile_list(box_converted, xsize, duration_converted, xorigin, torigin_converted, count) _check_error() - return result if result != _ffi.NULL else None, rows[0], columns[0] + return result if result != _ffi.NULL else None, count[0] def temporal_time_split(temp: 'Temporal *', duration: 'Interval *', torigin: int) -> "Tuple['Temporal **', 'TimestampTz *', 'int']": @@ -7660,20 +7668,20 @@ def tint_value_time_split(temp: 'Temporal *', size: int, duration: 'Interval *', return result if result != _ffi.NULL else None, value_buckets[0], time_buckets[0], count[0] -def tpoint_space_split(temp: 'Temporal *', xsize: 'float', ysize: 'float', zsize: 'float', sorigin: 'GSERIALIZED *', bitmatrix: bool) -> "Tuple['Temporal **', 'GSERIALIZED **', 'int']": +def tpoint_space_split(temp: 'Temporal *', xsize: 'float', ysize: 'float', zsize: 'float', sorigin: 'GSERIALIZED *', bitmatrix: bool) -> "Tuple['Temporal **', 'GSERIALIZED ***', 'int']": temp_converted = _ffi.cast('Temporal *', temp) xsize_converted = _ffi.cast('float', xsize) ysize_converted = _ffi.cast('float', ysize) zsize_converted = _ffi.cast('float', zsize) sorigin_converted = _ffi.cast('GSERIALIZED *', sorigin) - space_buckets = _ffi.new('GSERIALIZED **') + space_buckets = _ffi.new('GSERIALIZED ***') count = _ffi.new('int *') result = _lib.tpoint_space_split(temp_converted, xsize_converted, ysize_converted, zsize_converted, sorigin_converted, bitmatrix, space_buckets, count) _check_error() return result if result != _ffi.NULL else None, space_buckets[0], count[0] -def tpoint_space_time_split(temp: 'Temporal *', xsize: 'float', ysize: 'float', zsize: 'float', duration: 'Interval *', sorigin: 'GSERIALIZED *', torigin: int, bitmatrix: bool) -> "Tuple['Temporal **', 'GSERIALIZED **', 'TimestampTz *', 'int']": +def tpoint_space_time_split(temp: 'Temporal *', xsize: 'float', ysize: 'float', zsize: 'float', duration: 'Interval *', sorigin: 'GSERIALIZED *', torigin: int, bitmatrix: bool) -> "Tuple['Temporal **', 'GSERIALIZED ***', 'TimestampTz *', 'int']": temp_converted = _ffi.cast('Temporal *', temp) xsize_converted = _ffi.cast('float', xsize) ysize_converted = _ffi.cast('float', ysize) @@ -7681,7 +7689,7 @@ def tpoint_space_time_split(temp: 'Temporal *', xsize: 'float', ysize: 'float', duration_converted = _ffi.cast('Interval *', duration) sorigin_converted = _ffi.cast('GSERIALIZED *', sorigin) torigin_converted = _ffi.cast('TimestampTz', torigin) - space_buckets = _ffi.new('GSERIALIZED **') + space_buckets = _ffi.new('GSERIALIZED ***') time_buckets = _ffi.new('TimestampTz **') count = _ffi.new('int *') result = _lib.tpoint_space_time_split(temp_converted, xsize_converted, ysize_converted, zsize_converted, duration_converted, sorigin_converted, torigin_converted, bitmatrix, space_buckets, time_buckets, count) From 5371abdc521910ccfb9ad59554ba86a949d21507 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Sun, 24 Sep 2023 17:03:37 +0200 Subject: [PATCH 035/101] Remove extra returned value in space split --- pymeos/pymeos/main/tpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 251e9d90..360e449b 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -1106,7 +1106,7 @@ def space_split(self, xsize: float, ysize: Optional[float] = None, else pgis_geography_in('Point(0 0 0)', -1) \ if isinstance(self, TGeogPoint) \ else pgis_geometry_in('Point(0 0 0)', -1) - fragments, values, times, count = tpoint_space_split(self._inner, + fragments, values, count = tpoint_space_split(self._inner, xsize, ysz, zsz, gs, bitmatrix) from ..factory import _TemporalFactory return [_TemporalFactory.create_temporal(fragments[i]) for i in \ From 19a760ad645ef6b517929efa43ee19c759d10f36 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sun, 24 Sep 2023 17:32:45 +0200 Subject: [PATCH 036/101] Improve tests --- pymeos/pymeos/main/tpoint.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 251e9d90..4da37423 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -1156,7 +1156,6 @@ def space_time_split(self, xsize: float, duration: Union[str, timedelta], return [Temporal._factory(fragments[i]) for i in range(count)] - class TPointInst(TInstant[shpb.BaseGeometry, TG, TI, TS, TSS], TPoint[TG, TI, TS, TSS], ABC): """ Abstract class for temporal point instants. From e6154cb35f7294f37eaba39d788f68f8852883bf Mon Sep 17 00:00:00 2001 From: Diviloper Date: Sun, 24 Sep 2023 20:13:01 +0200 Subject: [PATCH 037/101] Add textset and len for sets --- pymeos/pymeos/collections/base/set.py | 11 ++ pymeos/pymeos/collections/text/textset.py | 184 ++++++++++++++++++++++ pymeos/tests/time/timestampset_test.py | 1 + 3 files changed, 196 insertions(+) create mode 100644 pymeos/pymeos/collections/text/textset.py diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py index 227e6949..edfdf347 100644 --- a/pymeos/pymeos/collections/base/set.py +++ b/pymeos/pymeos/collections/base/set.py @@ -170,6 +170,17 @@ def num_elements(self) -> int: """ return set_num_values(self._inner) + def __len__(self): + """ + Returns the number of elements in ``self``. + Returns: + An :class:`int` + + MEOS Functions: + set_num_values + """ + return self.num_elements() + @abstractmethod def start_element(self) -> T: """ diff --git a/pymeos/pymeos/collections/text/textset.py b/pymeos/pymeos/collections/text/textset.py new file mode 100644 index 00000000..992907a7 --- /dev/null +++ b/pymeos/pymeos/collections/text/textset.py @@ -0,0 +1,184 @@ +from __future__ import annotations + +from typing import Optional, overload, Union + +from pymeos_cffi import * + +from .. import Span, SpanSet +from ..base import Set + + +class TextSet(Set[str]): + """ + Class for representing a set of text values. + + ``TextSet`` objects can be created with a single argument of type string as in MobilityDB. + + >>> TextSet(string='{a, b, c, def}') + + Another possibility is to create a ``TextSet`` object from a list of strings. + + >>> TextSet(elements=['a', 'b', 'c', 'def']) + + + """ + + __slots__ = ['_inner'] + + _parse_function = textset_in + _parse_value_function = lambda x: x + _make_function = textset_make + + # ------------------------- Constructors ---------------------------------- + + # ------------------------- Output ---------------------------------------- + + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + textset_out + """ + return textset_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + + def to_spanset(self) -> SpanSet: + raise NotImplementedError() + + def to_span(self) -> Span: + raise NotImplementedError() + + # ------------------------- Accessors ------------------------------------- + + def start_element(self): + """ + Returns the first element in ``self``. + + Returns: + A :class:`str` instance + + MEOS Functions: + textset_start_value + """ + return textset_start_value(self._inner) + + def end_element(self): + """ + Returns the last element in ``self``. + + Returns: + A :class:`str` instance + + MEOS Functions: + textset_end_value + """ + return textset_end_value(self._inner) + + def element_n(self, n: int): + """ + Returns the ``n``-th element in ``self``. + + Args: + n: The 0-based index of the element to return. + + Returns: + A :class:`str` instance + + MEOS Functions: + textset_value_n + """ + super().element_n(n) + return textset_value_n(self._inner, n) + + def elements(self): + """ + Returns the elements in ``self``. + + Returns: + A list of :class:`str` instances + + MEOS Functions: + textset_values + """ + elems = textset_values(self._inner) + return [elems[i] for i in range(self.num_elements())] + + # ------------------------- Set Operations -------------------------------- + + @overload + def intersection(self, other: str) -> Optional[str]: + ... + + @overload + def intersection(self, other: TextSet) -> Optional[TextSet]: + ... + + def intersection(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: A :class:`TextSet` or :class:`str` instance + + Returns: + An object of the same type as ``other`` or ``None`` if the intersection is empty. + + MEOS Functions: + intersection_textset_text, intersection_set_set + """ + if isinstance(other, str): + return intersection_textset_text(self._inner, other) + elif isinstance(other, TextSet): + result = super().intersection(other) + return TextSet(elements=result) if result is not None else None + else: + return super().intersection(other) + + def minus(self, other: Union[TextSet, str]) -> Optional[TextSet]: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: A :class:`TextSet` or :class:`str` instance + + Returns: + A :class:`TextSet` instance. + + MEOS Functions: + minus_textset_text, minus_set_set + """ + if isinstance(other, str): + result = minus_textset_text(self._inner, other) + return TextSet(elements=result) if result is not None else None + elif isinstance(other, TextSet): + result = super().minus(other) + return TextSet(elements=result) if result is not None else None + else: + return super().minus(other) + + def union(self, other: Union[TextSet, str]) -> TextSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: A :class:`TextSet` or :class:`str` instance + + Returns: + A :class:`TextSet` instance. + + MEOS Functions: + union_textset_text, union_set_set + """ + if isinstance(other, str): + result = union_textset_text(self._inner, other) + return TextSet(elements=result) if result is not None else None + elif isinstance(other, TextSet): + result = super().union(other) + return TextSet(elements=result) if result is not None else None + else: + return super().union(other) diff --git a/pymeos/tests/time/timestampset_test.py b/pymeos/tests/time/timestampset_test.py index cacd4bfe..c1ea3efd 100644 --- a/pymeos/tests/time/timestampset_test.py +++ b/pymeos/tests/time/timestampset_test.py @@ -82,6 +82,7 @@ def test_period(self): def test_num_timestamps(self): assert self.ts_set.num_elements() == 3 + assert len(self.ts_set) == 3 def test_start_timestamp(self): assert self.ts_set.start_element() == datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc) From 5fafe5863402c58974a613838a0ee1566c9abe97 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 26 Sep 2023 10:56:05 +0200 Subject: [PATCH 038/101] Update pymeos_cffi --- pymeos_cffi/docker/Dockerfile | 4 +++- pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pymeos_cffi/docker/Dockerfile b/pymeos_cffi/docker/Dockerfile index d6cd2a13..2e9a5b82 100644 --- a/pymeos_cffi/docker/Dockerfile +++ b/pymeos_cffi/docker/Dockerfile @@ -3,6 +3,7 @@ WORKDIR MobilityDB RUN git fetch RUN git checkout develop +RUN git pull WORKDIR build @@ -12,6 +13,7 @@ RUN make -j RUN make install COPY build_wheels.sh /build_wheels.sh +RUN sed -i -e 's/\r$//' /build_wheels.sh RUN chmod +x /build_wheels.sh RUN rm -rf /opt/python/cp36-cp36m @@ -21,4 +23,4 @@ ENV LD_LIBRARY_PATH=/usr/lib64;/lib64 WORKDIR / -CMD ["./build_wheels.sh"] \ No newline at end of file +CMD ["/build_wheels.sh"] \ No newline at end of file diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index dd58f07e..235a6dd1 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -404,7 +404,7 @@ def build_function_string(function_name: str, return_type: ReturnType, parameter if result_param.is_interoperable(): returning_object += '[0]' - # If original C function returned bool, use it to return it when result is True, or raise exception when False + # If original C function returned bool, use it to return it when result is True, or return None otherwise. if return_type.return_type == 'bool': result_manipulation = (result_manipulation or '') + \ From 2951a189bc89c640d5b28061d5343e4dad1849f4 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 26 Sep 2023 11:09:43 +0200 Subject: [PATCH 039/101] Add tests for time in collections folder and update db connectors. --- pymeos/pymeos/boxes/stbox.py | 2 + pymeos/pymeos/boxes/tbox.py | 2 + pymeos/pymeos/collections/time/period.py | 2 + pymeos/pymeos/collections/time/periodset.py | 2 + .../pymeos/collections/time/timestampset.py | 2 + pymeos/pymeos/db/asyncpg.py | 2 +- pymeos/pymeos/db/psycopg.py | 2 +- pymeos/pymeos/db/psycopg2.py | 4 +- pymeos/pymeos/main/tbool.py | 3 + pymeos/pymeos/main/tfloat.py | 2 + pymeos/pymeos/main/tint.py | 2 + pymeos/pymeos/main/tpoint.py | 2 +- pymeos/pymeos/main/ttext.py | 2 + pymeos/tests/collections/__init__.py | 0 pymeos/tests/collections/time/__init__.py | 0 pymeos/tests/collections/time/period_test.py | 363 ++++++++++++++++++ .../tests/collections/time/periodset_test.py | 341 ++++++++++++++++ .../collections/time/timestampset_test.py | 339 ++++++++++++++++ 18 files changed, 1067 insertions(+), 5 deletions(-) create mode 100644 pymeos/tests/collections/__init__.py create mode 100644 pymeos/tests/collections/time/__init__.py create mode 100644 pymeos/tests/collections/time/period_test.py create mode 100644 pymeos/tests/collections/time/periodset_test.py create mode 100644 pymeos/tests/collections/time/timestampset_test.py diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index 3df54bbb..b39c327e 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -40,6 +40,8 @@ class STBox: """ __slots__ = ['_inner'] + _mobilitydb_name = 'stbox' + def _get_box(self, other: Union[Geometry, STBox, Temporal, Time], allow_space_only: bool = True, allow_time_only: bool = False) -> STBox: diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index a9674ac0..4a6cffa0 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -37,6 +37,8 @@ class TBox: """ __slots__ = ['_inner'] + _mobilitydb_name = 'tbox' + def _inner_period(self): from pymeos_cffi.functions import _ffi return _ffi.addressof(self._inner.period) diff --git a/pymeos/pymeos/collections/time/period.py b/pymeos/pymeos/collections/time/period.py index f620b1ca..7ca8d7ac 100644 --- a/pymeos/pymeos/collections/time/period.py +++ b/pymeos/pymeos/collections/time/period.py @@ -38,6 +38,8 @@ class Period(Span[datetime], TimeCollection): __slots__ = ['_inner'] + _mobilitydb_name = 'tstzspan' + _parse_function = period_in _parse_value_function = lambda x: pg_timestamptz_in(x, -1) if isinstance(x, str) else datetime_to_timestamptz(x) _make_function = period_make diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py index 3699d094..bb6947d8 100644 --- a/pymeos/pymeos/collections/time/periodset.py +++ b/pymeos/pymeos/collections/time/periodset.py @@ -38,6 +38,8 @@ class PeriodSet(SpanSet[datetime], TimeCollection): __slots__ = ['_inner'] + _mobilitydb_name = 'tstzspanset' + _parse_function = periodset_in _parse_value_function = lambda period: period_in(period)[0] if isinstance(period, str) else period._inner[0] diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py index 2c940799..1049be96 100644 --- a/pymeos/pymeos/collections/time/timestampset.py +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -37,6 +37,8 @@ class TimestampSet(Set[datetime], TimeCollection): __slots__ = ['_inner'] + _mobilitydb_name = 'tstzset' + _parse_function = timestampset_in _parse_value_function = lambda x: pg_timestamptz_in(x, -1) if isinstance(x, str) else datetime_to_timestamptz(x) _make_function = timestampset_make diff --git a/pymeos/pymeos/db/asyncpg.py b/pymeos/pymeos/db/asyncpg.py index e2f42828..b9277f5a 100644 --- a/pymeos/pymeos/db/asyncpg.py +++ b/pymeos/pymeos/db/asyncpg.py @@ -39,4 +39,4 @@ async def register(cls, connection: asyncpg.connection.Connection) -> None: """ classes = [TimestampSet, Period, PeriodSet, TBox, TBool, TInt, TFloat, TText, STBox, TGeomPoint, TGeogPoint] for cl in classes: - await connection.set_type_codec(cl.__name__.lower(), encoder=str, decoder=cl.read_from_cursor) + await connection.set_type_codec(cl._mobilitydb_name, encoder=str, decoder=cl.read_from_cursor) diff --git a/pymeos/pymeos/db/psycopg.py b/pymeos/pymeos/db/psycopg.py index 694cfb27..5632bfb5 100644 --- a/pymeos/pymeos/db/psycopg.py +++ b/pymeos/pymeos/db/psycopg.py @@ -57,7 +57,7 @@ def register(cls, connection: psycopg.Connection): cursor = connection.cursor() classes = [TimestampSet, Period, PeriodSet, TBox, TBool, TInt, TFloat, TText, STBox, TGeomPoint, TGeogPoint] for cl in classes: - cursor.execute(f'SELECT NULL::{cl.__name__}') + cursor.execute(f'SELECT NULL::{cl._mobilitydb_name}') oid = cursor.description[0][1] connection.adapters.register_loader(oid, _pymeos_loader_factory(cl)) connection.adapters.register_dumper(cl, _PymeosDumper) diff --git a/pymeos/pymeos/db/psycopg2.py b/pymeos/pymeos/db/psycopg2.py index db397282..47cf1e4c 100644 --- a/pymeos/pymeos/db/psycopg2.py +++ b/pymeos/pymeos/db/psycopg2.py @@ -47,6 +47,6 @@ def register(cls, connection: psycopg2.extensions.connection) -> None: # Add MobilityDB types to PostgreSQL adapter and specify the reader function for each type. classes = [TimestampSet, Period, PeriodSet, TBox, TBool, TInt, TFloat, TText, STBox, TGeomPoint, TGeogPoint] for cl in classes: - cursor.execute(f'SELECT NULL::{cl.__name__}') + cursor.execute(f'SELECT NULL::{cl._mobilitydb_name}') oid = cursor.description[0][1] - extensions.register_type(extensions.new_type((oid,), cl.__name__, cl.read_from_cursor)) + extensions.register_type(extensions.new_type((oid,), cl._mobilitydb_name, cl.read_from_cursor)) diff --git a/pymeos/pymeos/main/tbool.py b/pymeos/pymeos/main/tbool.py index 21c6aab9..cd415b8c 100644 --- a/pymeos/pymeos/main/tbool.py +++ b/pymeos/pymeos/main/tbool.py @@ -14,6 +14,9 @@ class TBool(Temporal[bool, 'TBool', 'TBoolInst', 'TBoolSeq', 'TBoolSeqSet'], ABC """ Base class for temporal boolean. """ + + _mobilitydb_name = 'tbool' + BaseClass = bool _parse_function = tbool_in diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index 01018304..258ad755 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -17,6 +17,8 @@ class TFloat(TNumber[float, 'TFloat', 'TFloatInst', 'TFloatSeq', 'TFloatSeqSet'], ABC): + _mobilitydb_name = 'tfloat' + BaseClass = float _parse_function = tfloat_in diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index e01fbd43..3b82ac7f 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -17,6 +17,8 @@ class TInt(TNumber[int, 'TInt', 'TIntInst', 'TIntSeq', 'TIntSeqSet'], ABC): + _mobilitydb_name = 'tint' + BaseClass = int _parse_function = tint_in diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 072d1473..7e38301b 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -23,7 +23,7 @@ TS = TypeVar('TS', bound='TPointSeq') TSS = TypeVar('TSS', bound='TPointSeqSet') Self = TypeVar('Self', bound='TPoint') - + class TPoint(Temporal[shp.Point, TG, TI, TS, TSS], ABC): """ diff --git a/pymeos/pymeos/main/ttext.py b/pymeos/pymeos/main/ttext.py index ad5129e3..1280e6b7 100644 --- a/pymeos/pymeos/main/ttext.py +++ b/pymeos/pymeos/main/ttext.py @@ -14,6 +14,8 @@ class TText(Temporal[str, 'TText', 'TTextInst', 'TTextSeq', 'TTextSeqSet'], ABC): + _mobilitydb_name = 'ttext' + BaseClass = str _parse_function = ttext_in diff --git a/pymeos/tests/collections/__init__.py b/pymeos/tests/collections/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymeos/tests/collections/time/__init__.py b/pymeos/tests/collections/time/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymeos/tests/collections/time/period_test.py b/pymeos/tests/collections/time/period_test.py new file mode 100644 index 00000000..56bbaa7b --- /dev/null +++ b/pymeos/tests/collections/time/period_test.py @@ -0,0 +1,363 @@ +from copy import copy +from datetime import datetime, timezone, timedelta + +import pytest + +from pymeos import Period, PeriodSet, TimestampSet, TBox, STBox, TFloatInst, TFloatSeq, TFloatSeqSet + +from tests.conftest import TestPyMEOS + + +class TestPeriod(TestPyMEOS): + period = Period('(2019-09-08 00:00:00+0, 2019-09-10 00:00:00+0)') + + @staticmethod + def assert_period_equality(period: Period, + lower: datetime = None, + upper: datetime = None, + lower_inc: bool = None, + upper_inc: bool = None): + if lower is not None: + assert period.lower() == lower + if upper is not None: + assert period.upper() == upper + if lower_inc is not None: + assert period.lower_inc() == lower_inc + if upper_inc is not None: + assert period.upper_inc() == upper_inc + + +class TestPeriodConstructors(TestPeriod): + + @pytest.mark.parametrize( + 'source, params', + [ + ('(2019-09-08 00:00:00+0, 2019-09-10 00:00:00+0)', + (datetime(2019, 9, 8, tzinfo=timezone.utc), datetime(2019, 9, 10, tzinfo=timezone.utc), False, False)), + ('[2019-09-08 00:00:00+0, 2019-09-10 00:00:00+0]', + (datetime(2019, 9, 8, tzinfo=timezone.utc), datetime(2019, 9, 10, tzinfo=timezone.utc), True, True)), + ] + ) + def test_string_constructor(self, source, params): + period = Period(source) + self.assert_period_equality(period, *params) + + @pytest.mark.parametrize( + 'input_lower,input_upper,lower,upper', + [ + ('2019-09-08 00:00:00+0', '2019-09-10 00:00:00+0', + datetime(2019, 9, 8, tzinfo=timezone.utc), datetime(2019, 9, 10, tzinfo=timezone.utc)), + (datetime(2019, 9, 8, tzinfo=timezone.utc), datetime(2019, 9, 10, tzinfo=timezone.utc), + datetime(2019, 9, 8, tzinfo=timezone.utc), datetime(2019, 9, 10, tzinfo=timezone.utc)), + (datetime(2019, 9, 8, tzinfo=timezone.utc), '2019-09-10 00:00:00+0', + datetime(2019, 9, 8, tzinfo=timezone.utc), datetime(2019, 9, 10, tzinfo=timezone.utc)), + ], + ids=['string', 'datetime', 'mixed'] + ) + def test_constructor_bounds(self, input_lower, input_upper, lower, upper): + period = Period(lower=lower, upper=upper) + self.assert_period_equality(period, lower, upper) + + def test_constructor_bound_inclusivity_defaults(self): + period = Period(lower='2019-09-08 00:00:00+0', upper='2019-09-10 00:00:00+0') + self.assert_period_equality(period, lower_inc=True, upper_inc=False) + + @pytest.mark.parametrize( + 'lower,upper', + [ + (True, True), + (True, False), + (False, True), + (False, False), + ] + ) + def test_constructor_bound_inclusivity(self, lower, upper): + period = Period(lower='2019-09-08 00:00:00+0', upper='2019-09-10 00:00:00+0', lower_inc=lower, upper_inc=upper) + self.assert_period_equality(period, lower_inc=lower, upper_inc=upper) + + def test_hexwkb_constructor(self): + source = '012100000040021FFE3402000000B15A26350200' + period = Period.from_hexwkb(source) + self.assert_period_equality(period, datetime(2019, 9, 8, tzinfo=timezone.utc), + datetime(2019, 9, 10, tzinfo=timezone.utc), False, False) + + def test_from_as_constructor(self): + assert self.period == Period(str(self.period)) + assert self.period == Period.from_wkb(self.period.as_wkb()) + assert self.period == Period.from_hexwkb(self.period.as_hexwkb()) + + def test_copy_constructor(self): + other = copy(self.period) + assert self.period == other + assert self.period is not other + + +class TestPeriodOutputs(TestPeriod): + + def test_str(self): + assert str(self.period) == '(2019-09-08 00:00:00+00, 2019-09-10 00:00:00+00)' + + def test_repr(self): + assert repr(self.period) == 'Period((2019-09-08 00:00:00+00, 2019-09-10 00:00:00+00))' + + def test_hexwkb(self): + assert self.period.as_hexwkb() == '012100000040021FFE3402000000B15A26350200' + + +class TestPeriodConversions(TestPeriod): + + def test_to_periodset(self): + periodset = self.period.to_periodset() + assert isinstance(periodset, PeriodSet) + assert periodset.num_periods() == 1 + assert periodset.start_period() == self.period + + +class TestPeriodAccessors(TestPeriod): + period2 = Period('[2019-09-08 02:03:00+0, 2019-09-10 02:03:00+0]') + + def test_lower(self): + assert self.period.lower() == datetime(2019, 9, 8, tzinfo=timezone.utc) + assert self.period2.lower() == datetime(2019, 9, 8, 2, 3, tzinfo=timezone.utc) + + def test_upper(self): + assert self.period.upper() == datetime(2019, 9, 10, tzinfo=timezone.utc) + assert self.period2.upper() == datetime(2019, 9, 10, 2, 3, tzinfo=timezone.utc) + + def test_lower_inc(self): + assert not self.period.lower_inc() + assert self.period2.lower_inc() + + def test_upper_inc(self): + assert not self.period.upper_inc() + assert self.period2.upper_inc() + + def test_duration(self): + assert self.period.duration() == timedelta(days=2) + assert self.period2.duration() == timedelta(days=2) + + def test_duration_in_seconds(self): + assert self.period.duration_in_seconds() == 172800 + assert self.period2.duration_in_seconds() == 172800 + + def test_hash(self): + assert hash(self.period) == 1164402929 + + +class TestPeriodTransformationFunctions(TestPeriod): + + @pytest.mark.parametrize( + 'delta,result', + [(timedelta(days=4), + (datetime(2019, 9, 12, tzinfo=timezone.utc), datetime(2019, 9, 14, tzinfo=timezone.utc), False, False)), + (timedelta(days=-4), + (datetime(2019, 9, 4, tzinfo=timezone.utc), datetime(2019, 9, 6, tzinfo=timezone.utc), False, False)), + (timedelta(hours=2), + (datetime(2019, 9, 8, 2, tzinfo=timezone.utc), datetime(2019, 9, 10, 2, tzinfo=timezone.utc), False, False)), + (timedelta(hours=-2), + (datetime(2019, 9, 7, 22, tzinfo=timezone.utc), datetime(2019, 9, 9, 22, tzinfo=timezone.utc), False, + False)), + ], + ids=['positive days', 'negative days', 'positive hours', 'negative hours'] + ) + def test_shift(self, delta, result): + shifted = self.period.shift(delta) + self.assert_period_equality(shifted, *result) + + @pytest.mark.parametrize( + 'delta,result', + [(timedelta(days=4), + (datetime(2019, 9, 8, tzinfo=timezone.utc), datetime(2019, 9, 12, tzinfo=timezone.utc), False, False)), + (timedelta(hours=2), + (datetime(2019, 9, 8, tzinfo=timezone.utc), datetime(2019, 9, 8, 2, tzinfo=timezone.utc), False, False)), + ], + ids=['days', 'hours'] + ) + def test_scale(self, delta, result): + scaled = self.period.scale(delta) + self.assert_period_equality(scaled, *result) + + def test_shift_scale(self): + shifted_scaled = self.period.shift_scale(timedelta(days=4), timedelta(hours=4)) + self.assert_period_equality(shifted_scaled, datetime(2019, 9, 12, 0, tzinfo=timezone.utc), + datetime(2019, 9, 12, 4, tzinfo=timezone.utc), False, False) + + +class TestPeriodTopologicalPositionFunctions(TestPeriod): + period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') + periodset = PeriodSet( + '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') + timestamp = datetime(year=2020, month=1, day=1) + timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') + instant = TFloatInst('1.0@2020-01-01') + discrete_sequence = TFloatSeq('{1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31}') + stepwise_sequence = TFloatSeq('Interp=Step;(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') + continuous_sequence = TFloatSeq('(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') + sequence_set = TFloatSeqSet('{(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31], ' + '(1.0@2021-01-01, 3.0@2021-01-10, 10.0@2021-01-20, 0.0@2021-01-31]}') + tbox = TBox('TBox XT([0, 10),[2020-01-01, 2020-01-31])') + stbox = STBox('STBOX ZT(((1.0,2.0,3.0),(4.0,5.0,6.0)),[2001-01-01, 2001-01-02])') + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_adjacent(self, other): + self.period.is_adjacent(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, instant, discrete_sequence, stepwise_sequence, continuous_sequence, sequence_set, tbox, + stbox], + ids=['period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', + 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_contained_in(self, other): + self.period.is_contained_in(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_contains(self, other): + self.period.contains(other) + _ = other in self.period + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_overlaps(self, other): + self.period.overlaps(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_same(self, other): + self.period.is_same(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_before(self, other): + self.period.is_before(other) + self.period.is_left(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_over_or_before(self, other): + self.period.is_over_or_before(other) + self.period.is_over_or_left(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_after(self, other): + self.period.is_after(other) + self.period.is_right(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_over_or_after(self, other): + self.period.is_over_or_after(other) + self.period.is_over_or_right(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_distance(self, other): + self.period.distance(other) + + +class TestPeriodSetFunctions(TestPeriod): + period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') + periodset = PeriodSet( + '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') + timestamp = datetime(year=2020, month=1, day=1) + timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset], + ids=['period', 'periodset', 'timestamp', 'timestampset'] + ) + def test_intersection(self, other): + self.period.intersection(other) + self.period * other + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset], + ids=['period', 'periodset', 'timestamp', 'timestampset'] + ) + def test_union(self, other): + self.period.union(other) + self.period + other + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset], + ids=['period', 'periodset', 'timestamp', 'timestampset'] + ) + def test_minus(self, other): + self.period.minus(other) + self.period - other + + +class TestPeriodComparisons(TestPeriod): + period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') + other = Period('(2020-01-02 00:00:00+0, 2020-03-31 00:00:00+0)') + + def test_eq(self): + _ = self.period == self.other + + def test_ne(self): + _ = self.period != self.other + + def test_lt(self): + _ = self.period < self.other + + def test_le(self): + _ = self.period <= self.other + + def test_gt(self): + _ = self.period > self.other + + def test_ge(self): + _ = self.period >= self.other + diff --git a/pymeos/tests/collections/time/periodset_test.py b/pymeos/tests/collections/time/periodset_test.py new file mode 100644 index 00000000..5bb982c6 --- /dev/null +++ b/pymeos/tests/collections/time/periodset_test.py @@ -0,0 +1,341 @@ +from copy import copy +from datetime import datetime, timezone, timedelta +from typing import List + +import pytest + +from pymeos import Period, PeriodSet, TimestampSet, TFloatInst, TFloatSeq, STBox, TFloatSeqSet, TBox +from tests.conftest import TestPyMEOS + + +class TestPeriodSet(TestPyMEOS): + periodset = PeriodSet('{[2019-09-01, 2019-09-02], [2019-09-03, 2019-09-04]}') + + @staticmethod + def assert_periodset_equality(periodset: PeriodSet, periods: List[Period]): + assert periodset.num_periods() == len(periods) + assert periodset.periods() == periods + + +class TestPeriodSetConstructors(TestPeriodSet): + + def test_string_constructor(self): + self.assert_periodset_equality(self.periodset, [ + Period('[2019-09-01, 2019-09-02]'), + Period('[2019-09-03, 2019-09-04]') + ]) + + def test_period_list_constructor(self): + periodset = PeriodSet(period_list=[ + Period('[2019-09-01, 2019-09-02]'), + Period('[2019-09-03, 2019-09-04]') + ]) + self.assert_periodset_equality(periodset, [ + Period('[2019-09-01, 2019-09-02]'), + Period('[2019-09-03, 2019-09-04]') + ]) + + def test_from_as_constructor(self): + assert self.periodset == PeriodSet(str(self.periodset)) + assert self.periodset == PeriodSet.from_wkb(self.periodset.as_wkb()) + assert self.periodset == PeriodSet.from_hexwkb(self.periodset.as_hexwkb()) + + def test_copy_constructor(self): + copied = copy(self.periodset) + assert self.periodset == copied + assert self.periodset is not copied + + +class TestPeriodSetOutputs(TestPeriodSet): + + def test_str(self): + assert str(self.periodset) == '{[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00], ' \ + '[2019-09-03 00:00:00+00, 2019-09-04 00:00:00+00]}' + + def test_repr(self): + assert repr(self.periodset) == 'PeriodSet({[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00], ' \ + '[2019-09-03 00:00:00+00, 2019-09-04 00:00:00+00]})' + + def test_hexwkb(self): + assert self.periodset.as_hexwkb() == '012200020000000300A01E4E713402000000F66B85340200030060CD899934020000C0A4A7AD340200' + + +class TestPeriodSetConversions(TestPeriodSet): + + def test_to_period(self): + assert self.periodset.to_period() == Period('[2019-09-01, 2019-09-04]') + + +class TestPeriodSetAccessors(TestPeriodSet): + periodset2 = PeriodSet('{[2019-09-01, 2019-09-02), (2019-09-02, 2019-09-04]}') + + def test_duration(self): + assert self.periodset.duration() == timedelta(days=2) + assert self.periodset.duration(True) == timedelta(days=3) + + def test_num_timestamps(self): + assert self.periodset.num_timestamps() == 4 + assert self.periodset2.num_timestamps() == 3 + + def test_start_timestamp(self): + assert self.periodset.start_timestamp() == datetime(2019, 9, 1, tzinfo=timezone.utc) + + def test_end_timestamp(self): + assert self.periodset.end_timestamp() == datetime(2019, 9, 4, tzinfo=timezone.utc) + + def test_timestamp_n(self): + assert self.periodset.timestamp_n(0) == datetime(2019, 9, 1, tzinfo=timezone.utc) + assert self.periodset.timestamp_n(1) == datetime(2019, 9, 2, tzinfo=timezone.utc) + assert self.periodset.timestamp_n(2) == datetime(2019, 9, 3, tzinfo=timezone.utc) + assert self.periodset.timestamp_n(3) == datetime(2019, 9, 4, tzinfo=timezone.utc) + + def test_timestamps(self): + assert self.periodset.timestamps() == [ + datetime(2019, 9, 1, tzinfo=timezone.utc), + datetime(2019, 9, 2, tzinfo=timezone.utc), + datetime(2019, 9, 3, tzinfo=timezone.utc), + datetime(2019, 9, 4, tzinfo=timezone.utc), + ] + + def test_num_periods(self): + assert self.periodset.num_periods() == 2 + + def test_start_period(self): + assert self.periodset.start_period() == Period('[2019-09-01, 2019-09-02]') + + def test_end_period(self): + assert self.periodset.end_period() == Period('[2019-09-03, 2019-09-04]') + + def test_period_n(self): + assert self.periodset.period_n(0) == Period('[2019-09-01, 2019-09-02]') + assert self.periodset.period_n(1) == Period('[2019-09-03, 2019-09-04]') + + def test_periods(self): + assert self.periodset.periods() == [ + Period('[2019-09-01, 2019-09-02]'), + Period('[2019-09-03, 2019-09-04]') + ] + + def test_hash(self): + assert hash(self.periodset) == 552347465 + + +class TestPeriodTransformationFunctions(TestPeriodSet): + + @pytest.mark.parametrize( + 'delta,result', + [(timedelta(days=4), + [Period('[2019-09-05 00:00:00+0, 2019-09-06 00:00:00+0]'), + Period('[2019-09-07 00:00:00+0, 2019-09-08 00:00:00+0]')]), + (timedelta(days=-4), + [Period('[2019-08-28 00:00:00+0, 2019-08-29 00:00:00+0]'), + Period('[2019-08-30 00:00:00+00, 2019-08-31 00:00:00+00]')]), + (timedelta(hours=2), + [Period('[2019-09-01 02:00:00+0, 2019-09-02 02:00:00+0]'), + Period('[2019-09-03 02:00:00+0, 2019-09-04 02:00:00+0]')]), + (timedelta(hours=-2), + [Period('[2019-08-31 22:00:00+0, 2019-09-01 22:00:00+0]'), + Period('[2019-09-02 22:00:00+0, 2019-09-03 22:00:00+0]')]), + ], + ids=['positive days', 'negative days', 'positive hours', 'negative hours'] + ) + def test_shift(self, delta, result): + shifted = self.periodset.shift(delta) + self.assert_periodset_equality(shifted, result) + + @pytest.mark.parametrize( + 'delta,result', + [(timedelta(days=3), + [Period('[2019-09-01 00:00:00+0, 2019-09-02 00:00:00+0]'), + Period('[2019-09-03 00:00:00+0, 2019-09-04 00:00:00+0]')]), + (timedelta(hours=6), + [Period('[2019-09-01 00:00:00+0, 2019-09-01 02:00:00+0]'), + Period('[2019-09-01 04:00:00+0, 2019-09-01 06:00:00+0]'), ]), + ], + ids=['days', 'hours'] + ) + def test_scale(self, delta, result): + scaled = self.periodset.scale(delta) + self.assert_periodset_equality(scaled, result) + + def test_shift_scale(self): + shifted_scaled = self.periodset.shift_scale(timedelta(days=4), timedelta(hours=6)) + self.assert_periodset_equality(shifted_scaled, [Period('[2019-09-05 00:00:00+0, 2019-09-05 02:00:00+0]'), + Period('[2019-09-05 04:00:00+0, 2019-09-05 06:00:00+0]')]) + + +class TestPeriodSetTopologicalPositionFunctions(TestPeriodSet): + period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') + periodset = PeriodSet( + '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') + timestamp = datetime(year=2020, month=1, day=1) + timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') + instant = TFloatInst('1.0@2020-01-01') + discrete_sequence = TFloatSeq('{1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31}') + stepwise_sequence = TFloatSeq('Interp=Step;(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') + continuous_sequence = TFloatSeq('(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') + sequence_set = TFloatSeqSet('{(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31], ' + '(1.0@2021-01-01, 3.0@2021-01-10, 10.0@2021-01-20, 0.0@2021-01-31]}') + tbox = TBox('TBox XT([0, 10),[2020-01-01, 2020-01-31])') + stbox = STBox('STBOX ZT(((1.0,2.0,3.0),(4.0,5.0,6.0)),[2001-01-01, 2001-01-02])') + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_adjacent(self, other): + self.periodset.is_adjacent(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, instant, discrete_sequence, stepwise_sequence, continuous_sequence, sequence_set, tbox, + stbox], + ids=['period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', + 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_contained_in(self, other): + self.periodset.is_contained_in(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_contains(self, other): + self.periodset.contains(other) + _ = other in self.periodset + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_overlaps(self, other): + self.periodset.overlaps(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_same(self, other): + self.periodset.is_same(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_before(self, other): + self.periodset.is_before(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_over_or_before(self, other): + self.periodset.is_over_or_before(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_after(self, other): + self.periodset.is_after(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_over_or_after(self, other): + self.periodset.is_over_or_after(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_distance(self, other): + self.periodset.distance(other) + + +class TestPeriodSetSetFunctions(TestPeriodSet): + period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') + periodset = PeriodSet( + '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') + timestamp = datetime(year=2020, month=1, day=1) + timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset], + ids=['period', 'periodset', 'timestamp', 'timestampset'] + ) + def test_intersection(self, other): + self.periodset.intersection(other) + self.periodset * other + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset], + ids=['period', 'periodset', 'timestamp', 'timestampset'] + ) + def test_union(self, other): + self.periodset.union(other) + self.periodset + other + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset], + ids=['period', 'periodset', 'timestamp', 'timestampset'] + ) + def test_minus(self, other): + self.periodset.minus(other) + self.periodset - other + + +class TestPeriodComparisons(TestPeriodSet): + periodset = PeriodSet( + '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') + other = PeriodSet( + '{(2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0), (2022-01-01 00:00:00+0, 2022-01-31 00:00:00+0)}') + + def test_eq(self): + _ = self.periodset == self.other + + def test_ne(self): + _ = self.periodset != self.other + + def test_lt(self): + _ = self.periodset < self.other + + def test_le(self): + _ = self.periodset <= self.other + + def test_gt(self): + _ = self.periodset > self.other + + def test_ge(self): + _ = self.periodset >= self.other diff --git a/pymeos/tests/collections/time/timestampset_test.py b/pymeos/tests/collections/time/timestampset_test.py new file mode 100644 index 00000000..d0ea5fa9 --- /dev/null +++ b/pymeos/tests/collections/time/timestampset_test.py @@ -0,0 +1,339 @@ +from copy import copy +from datetime import datetime, timezone, timedelta +from typing import List + +import pytest + +from pymeos import Period, PeriodSet, TimestampSet, TFloatInst, TFloatSeq, STBox, TFloatSeqSet, TBox +from tests.conftest import TestPyMEOS + + +class TestTimestampSet(TestPyMEOS): + ts_set = TimestampSet('{2019-09-01 00:00:00+0, 2019-09-02 00:00:00+0, 2019-09-03 00:00:00+0}') + + @staticmethod + def assert_timestampset_equality(ts_set: TimestampSet, + timestamps: List[datetime]): + assert ts_set.num_elements() == len(timestamps) + assert ts_set.elements() == timestamps + + +class TestTimestampSetConstructors(TestTimestampSet): + + def test_string_constructor(self): + self.assert_timestampset_equality(self.ts_set, [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) + + def test_list_constructor(self): + ts_set = TimestampSet(elements=[datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) + self.assert_timestampset_equality(ts_set, [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) + + def test_hexwkb_constructor(self): + ts_set = TimestampSet.from_hexwkb('012000010300000000A01E4E713402000000F66B853402000060CD8999340200') + self.assert_timestampset_equality(ts_set, [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) + + def test_from_as_constructor(self): + assert self.ts_set == TimestampSet(str(self.ts_set)) + assert self.ts_set == TimestampSet.from_wkb(self.ts_set.as_wkb()) + assert self.ts_set == TimestampSet.from_hexwkb(self.ts_set.as_hexwkb()) + + def test_copy_constructor(self): + ts_set_copy = copy(self.ts_set) + assert self.ts_set == ts_set_copy + assert self.ts_set is not ts_set_copy + + +class TestTimestampSetOutputs(TestTimestampSet): + + def test_str(self): + assert str(self.ts_set) == '{"2019-09-01 00:00:00+00", "2019-09-02 00:00:00+00", "2019-09-03 00:00:00+00"}' + + def test_repr(self): + assert repr( + self.ts_set) == 'TimestampSet({"2019-09-01 00:00:00+00", "2019-09-02 00:00:00+00", "2019-09-03 00:00:00+00"})' + + def test_as_hexwkb(self): + assert self.ts_set.as_hexwkb() == '012000010300000000A01E4E713402000000F66B853402000060CD8999340200' + + +class TestTimestampConversions(TestTimestampSet): + + def test_to_periodset(self): + assert self.ts_set.to_periodset() == PeriodSet( + '{[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00], ' + '[2019-09-02 00:00:00+00, 2019-09-02 00:00:00+00], ' + '[2019-09-03 00:00:00+00, 2019-09-03 00:00:00+00]}') + + +class TestTimestampSetAccessors(TestTimestampSet): + + def test_duration(self): + assert self.ts_set.duration() == timedelta(days=2) + + def test_period(self): + assert self.ts_set.to_period() == Period('[2019-09-01 00:00:00+00, 2019-09-03 00:00:00+00]') + + def test_num_timestamps(self): + assert self.ts_set.num_elements() == 3 + assert len(self.ts_set) == 3 + + def test_start_timestamp(self): + assert self.ts_set.start_element() == datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc) + + def test_end_timestamp(self): + assert self.ts_set.end_element() == datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc) + + def test_timestamp_n(self): + assert self.ts_set.element_n(1) == datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc) + + def test_timestamp_n_out_of_range(self): + with pytest.raises(IndexError): + self.ts_set.element_n(3) + + def test_timestamps(self): + assert self.ts_set.elements() == [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), + datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc), + ] + + def test_hash(self): + assert hash(self.ts_set) == 527267058 + + +class TestTimestampSetPositionFunctions(TestTimestampSet): + period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') + periodset = PeriodSet( + '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') + timestamp = datetime(year=2020, month=1, day=1) + timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') + instant = TFloatInst('1.0@2020-01-01') + discrete_sequence = TFloatSeq('{1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31}') + stepwise_sequence = TFloatSeq('Interp=Step;(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') + continuous_sequence = TFloatSeq('(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') + sequence_set = TFloatSeqSet('{(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31], ' + '(1.0@2021-01-01, 3.0@2021-01-10, 10.0@2021-01-20, 0.0@2021-01-31]}') + tbox = TBox('TBox XT([0, 10),[2020-01-01, 2020-01-31])') + stbox = STBox('STBOX ZT(((1.0,2.0,3.0),(4.0,5.0,6.0)),[2001-01-01, 2001-01-02])') + + @pytest.mark.parametrize( + 'other', + [period, periodset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_adjacent(self, other): + self.timestampset.is_adjacent(other) + + @pytest.mark.parametrize( + 'other', + [timestampset, period, periodset, instant, discrete_sequence, stepwise_sequence, continuous_sequence, + sequence_set, tbox, + stbox], + ids=['timestampset', 'period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', + 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_contained_in(self, other): + self.timestampset.is_contained_in(other) + + @pytest.mark.parametrize( + 'other', + [timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence], + ids=['timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set'] + ) + def test_contains(self, other): + self.timestampset.contains(other) + _ = other in self.timestampset + + # + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_overlaps(self, other): + self.timestampset.overlaps(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_same(self, other): + self.periodset.is_same(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_before(self, other): + self.timestampset.is_before(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_over_or_before(self, other): + self.timestampset.is_over_or_before(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_after(self, other): + self.timestampset.is_after(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_is_over_or_after(self, other): + self.timestampset.is_over_or_after(other) + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + continuous_sequence, tbox, stbox], + ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + ) + def test_distance(self, other): + self.timestampset.distance(other) + + +class TestTimestampSetSetFunctions(TestTimestampSet): + timestamp = datetime(year=2020, month=1, day=1) + timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') + period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') + periodset = PeriodSet( + '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset], + ids=['period', 'periodset', 'timestamp', 'timestampset'] + ) + def test_intersection(self, other): + self.timestampset.intersection(other) + self.timestampset * other + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset], + ids=['period', 'periodset', 'timestamp', 'timestampset'] + ) + def test_union(self, other): + self.timestampset.union(other) + self.timestampset + other + + @pytest.mark.parametrize( + 'other', + [period, periodset, timestamp, timestampset], + ids=['period', 'periodset', 'timestamp', 'timestampset'] + ) + def test_minus(self, other): + self.timestampset.minus(other) + self.timestampset - other + + +class TestTimestampSetComparisons(TestTimestampSet): + timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') + other = TimestampSet('{2020-01-02 00:00:00+0, 2020-03-31 00:00:00+0}') + + def test_eq(self): + _ = self.timestampset == self.other + + def test_ne(self): + _ = self.timestampset != self.other + + def test_lt(self): + _ = self.timestampset < self.other + + def test_le(self): + _ = self.timestampset <= self.other + + def test_gt(self): + _ = self.timestampset > self.other + + def test_ge(self): + _ = self.timestampset >= self.other + + +class TestTimestampSetManipulationFunctions(TestTimestampSet): + timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-02 00:00:00+0, 2020-01-04 00:00:00+0}') + + @pytest.mark.parametrize( + 'delta,result', + [(timedelta(days=4), + [datetime(2020, 1, 5, tzinfo=timezone.utc), datetime(2020, 1, 6, tzinfo=timezone.utc), + datetime(2020, 1, 8, tzinfo=timezone.utc)]), + (timedelta(days=-4), + [datetime(2019, 12, 28, tzinfo=timezone.utc), datetime(2019, 12, 29, tzinfo=timezone.utc), + datetime(2019, 12, 31, tzinfo=timezone.utc)]), + (timedelta(hours=2), + [datetime(2020, 1, 1, 2, tzinfo=timezone.utc), datetime(2020, 1, 2, 2, tzinfo=timezone.utc), + datetime(2020, 1, 4, 2, tzinfo=timezone.utc)]), + (timedelta(hours=-2), + [datetime(2019, 12, 31, 22, tzinfo=timezone.utc), datetime(2020, 1, 1, 22, tzinfo=timezone.utc), + datetime(2020, 1, 3, 22, tzinfo=timezone.utc)]), + ], + ids=['positive days', 'negative days', 'positive hours', 'negative hours'] + ) + def test_shift(self, delta, result): + shifted = self.timestampset.shift(delta) + self.assert_timestampset_equality(shifted, result) + + @pytest.mark.parametrize( + 'delta,result', + [(timedelta(days=6), + [datetime(2020, 1, 1, tzinfo=timezone.utc), datetime(2020, 1, 3, tzinfo=timezone.utc), + datetime(2020, 1, 7, tzinfo=timezone.utc)]), + (timedelta(hours=3), + [datetime(2020, 1, 1, tzinfo=timezone.utc), datetime(2020, 1, 1, 1, tzinfo=timezone.utc), + datetime(2020, 1, 1, 3, tzinfo=timezone.utc)]), + ], + ids=['days', 'hours'] + ) + def test_scale(self, delta, result): + scaled = self.timestampset.scale(delta) + self.assert_timestampset_equality(scaled, result) + + def test_shift_scale(self): + shifted_scaled = self.timestampset.shift_scale(timedelta(days=4), timedelta(hours=3)) + self.assert_timestampset_equality(shifted_scaled, + [datetime(2020, 1, 5, tzinfo=timezone.utc), + datetime(2020, 1, 5, 1, tzinfo=timezone.utc), + datetime(2020, 1, 5, 3, tzinfo=timezone.utc)]) + + +class TestTimestampSetMiscFunctions(TestTimestampSet): + timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-02 00:00:00+0, 2020-01-04 00:00:00+0}') + + def test_hash(self): + hash(self.timestampset) From 6c919f808e89ce59d1575d333c2974724c69bd49 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 26 Sep 2023 11:10:53 +0200 Subject: [PATCH 040/101] Update example --- pymeos_examples/PyMEOS Examples/AIS.ipynb | 88 +++++++++++++++++------ 1 file changed, 68 insertions(+), 20 deletions(-) diff --git a/pymeos_examples/PyMEOS Examples/AIS.ipynb b/pymeos_examples/PyMEOS Examples/AIS.ipynb index b875669e..23ed2d20 100644 --- a/pymeos_examples/PyMEOS Examples/AIS.ipynb +++ b/pymeos_examples/PyMEOS Examples/AIS.ipynb @@ -32,7 +32,11 @@ "pymeos_initialize()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:54:53.346701400Z", + "start_time": "2023-09-26T08:54:52.664004300Z" + } } }, { @@ -74,7 +78,11 @@ "ais.head()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:54:56.235929900Z", + "start_time": "2023-09-26T08:54:56.093929900Z" + } } }, { @@ -92,8 +100,8 @@ "outputs": [ { "data": { - "text/plain": " t mmsi sog \n0 2021-01-08 00:00:00 265513270 0@2021-01-08 00:00:00+01 \\\n1 2021-01-08 00:00:01 219027804 0@2021-01-08 00:00:01+01 \n2 2021-01-08 00:00:01 265513270 0@2021-01-08 00:00:01+01 \n3 2021-01-08 00:00:03 219027804 0@2021-01-08 00:00:03+01 \n4 2021-01-08 00:00:04 265513270 0@2021-01-08 00:00:04+01 \n\n point \n0 0101000020E6100000643BDF4F8D874C404CA59F70768B... \n1 0101000020E61000009B38B9DFA1F84B40137D3ECA88BB... \n2 0101000020E6100000643BDF4F8D874C404CA59F70768B... \n3 0101000020E61000009B38B9DFA1F84B40137D3ECA88BB... \n4 0101000020E6100000643BDF4F8D874C404CA59F70768B... ", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
tmmsisogpoint
02021-01-08 00:00:002655132700@2021-01-08 00:00:00+010101000020E6100000643BDF4F8D874C404CA59F70768B...
12021-01-08 00:00:012190278040@2021-01-08 00:00:01+010101000020E61000009B38B9DFA1F84B40137D3ECA88BB...
22021-01-08 00:00:012655132700@2021-01-08 00:00:01+010101000020E6100000643BDF4F8D874C404CA59F70768B...
32021-01-08 00:00:032190278040@2021-01-08 00:00:03+010101000020E61000009B38B9DFA1F84B40137D3ECA88BB...
42021-01-08 00:00:042655132700@2021-01-08 00:00:04+010101000020E6100000643BDF4F8D874C404CA59F70768B...
\n
" + "text/plain": " t mmsi sog \\\n0 2021-01-08 00:00:00 265513270 0@2021-01-08 00:00:00+01 \n1 2021-01-08 00:00:01 219027804 0@2021-01-08 00:00:01+01 \n2 2021-01-08 00:00:01 265513270 0@2021-01-08 00:00:01+01 \n3 2021-01-08 00:00:03 219027804 0@2021-01-08 00:00:03+01 \n4 2021-01-08 00:00:04 265513270 0@2021-01-08 00:00:04+01 \n\n point \n0 POINT(57.059 12.272388)@2021-01-08 00:00:00+01 \n1 POINT(55.94244 11.866278)@2021-01-08 00:00:01+01 \n2 POINT(57.059 12.272388)@2021-01-08 00:00:01+01 \n3 POINT(55.94244 11.866278)@2021-01-08 00:00:03+01 \n4 POINT(57.059 12.272388)@2021-01-08 00:00:04+01 ", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
tmmsisogpoint
02021-01-08 00:00:002655132700@2021-01-08 00:00:00+01POINT(57.059 12.272388)@2021-01-08 00:00:00+01
12021-01-08 00:00:012190278040@2021-01-08 00:00:01+01POINT(55.94244 11.866278)@2021-01-08 00:00:01+01
22021-01-08 00:00:012655132700@2021-01-08 00:00:01+01POINT(57.059 12.272388)@2021-01-08 00:00:01+01
32021-01-08 00:00:032190278040@2021-01-08 00:00:03+01POINT(55.94244 11.866278)@2021-01-08 00:00:03+01
42021-01-08 00:00:042655132700@2021-01-08 00:00:04+01POINT(57.059 12.272388)@2021-01-08 00:00:04+01
\n
" }, "execution_count": 3, "metadata": {}, @@ -108,7 +116,11 @@ "ais.head()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:55:02.898319900Z", + "start_time": "2023-09-26T08:54:58.467648800Z" + } } }, { @@ -128,8 +140,8 @@ "outputs": [ { "data": { - "text/plain": " trajectory \nmmsi \n219001559 [0101000020E6100000191C25AFCECB4C400BB6114F76F... \\\n219027804 [0101000020E61000009B38B9DFA1F84B40137D3ECA88B... \n257136000 [0101000020E610000023A0C211A4744C405320B3B3E87... \n265513270 [0101000020E6100000643BDF4F8D874C404CA59F70768... \n566948000 [0101000020E61000003332C85D84C94B405E83BEF4F67... \n\n sog distance \nmmsi \n219001559 [0@2021-01-08 00:00:05+01, 0@2021-01-08 23:59:... 17760.655571 \n219027804 [0@2021-01-08 00:00:01+01, 0@2021-01-08 10:04:... 94950.742960 \n257136000 [14@2021-01-08 00:02:57+01, 13@2021-01-08 00:0... 940404.131058 \n265513270 [0@2021-01-08 00:00:00+01, 0@2021-01-08 23:59:... 1626.095588 \n566948000 [0@2021-01-08 00:00:04+01, 0@2021-01-08 16:40:... 28213.629024 ", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
trajectorysogdistance
mmsi
219001559[0101000020E6100000191C25AFCECB4C400BB6114F76F...[0@2021-01-08 00:00:05+01, 0@2021-01-08 23:59:...17760.655571
219027804[0101000020E61000009B38B9DFA1F84B40137D3ECA88B...[0@2021-01-08 00:00:01+01, 0@2021-01-08 10:04:...94950.742960
257136000[0101000020E610000023A0C211A4744C405320B3B3E87...[14@2021-01-08 00:02:57+01, 13@2021-01-08 00:0...940404.131058
265513270[0101000020E6100000643BDF4F8D874C404CA59F70768...[0@2021-01-08 00:00:00+01, 0@2021-01-08 23:59:...1626.095588
566948000[0101000020E61000003332C85D84C94B405E83BEF4F67...[0@2021-01-08 00:00:04+01, 0@2021-01-08 16:40:...28213.629024
\n
" + "text/plain": " trajectory \\\nmmsi \n219001559 [POINT(57.592245 9.975512)@2021-01-08 00:00:05... \n219027804 [POINT(55.94244 11.866278)@2021-01-08 00:00:01... \n257136000 [POINT(56.911257 7.122958)@2021-01-08 00:02:57... \n265513270 [POINT(57.059 12.272388)@2021-01-08 00:00:00+0... \n566948000 [POINT(55.574352 4.617153)@2021-01-08 00:00:04... \n\n sog distance \nmmsi \n219001559 [0.1@2021-01-08 00:00:05+01, 0.1@2021-01-08 00... 17765.588739 \n219027804 [0@2021-01-08 00:00:01+01, 0@2021-01-08 00:34:... 94963.575026 \n257136000 [14@2021-01-08 00:02:57+01, 13.9@2021-01-08 00... 940404.131239 \n265513270 [0@2021-01-08 00:00:00+01, 0@2021-01-08 00:00:... 1629.539165 \n566948000 [0.5@2021-01-08 00:00:04+01, 0.5@2021-01-08 00... 28215.190430 ", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
trajectorysogdistance
mmsi
219001559[POINT(57.592245 9.975512)@2021-01-08 00:00:05...[0.1@2021-01-08 00:00:05+01, 0.1@2021-01-08 00...17765.588739
219027804[POINT(55.94244 11.866278)@2021-01-08 00:00:01...[0@2021-01-08 00:00:01+01, 0@2021-01-08 00:34:...94963.575026
257136000[POINT(56.911257 7.122958)@2021-01-08 00:02:57...[14@2021-01-08 00:02:57+01, 13.9@2021-01-08 00...940404.131239
265513270[POINT(57.059 12.272388)@2021-01-08 00:00:00+0...[0@2021-01-08 00:00:00+01, 0@2021-01-08 00:00:...1629.539165
566948000[POINT(55.574352 4.617153)@2021-01-08 00:00:04...[0.5@2021-01-08 00:00:04+01, 0.5@2021-01-08 00...28215.190430
\n
" }, "execution_count": 4, "metadata": {}, @@ -147,7 +159,11 @@ "trajectories.head()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:55:03.452322100Z", + "start_time": "2023-09-26T08:55:02.850797400Z" + } } }, { @@ -165,8 +181,8 @@ "outputs": [ { "data": { - "text/plain": " original #points PyMEOS #points\nmmsi \n219001559 48323 36431\n219027804 38326 17087\n257136000 21770 21119\n265513270 21799 7990\n566948000 26619 24148", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
original #pointsPyMEOS #points
mmsi
2190015594832336431
2190278043832617087
2571360002177021119
265513270217997990
5669480002661924148
\n
" + "text/plain": " original #points PyMEOS #points\nmmsi \n219001559 48323 36494\n219027804 38326 17161\n257136000 21770 21120\n265513270 21799 7954\n566948000 26619 24176", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
original #pointsPyMEOS #points
mmsi
2190015594832336494
2190278043832617161
2571360002177021120
265513270217997954
5669480002661924176
\n
" }, "execution_count": 5, "metadata": {}, @@ -179,7 +195,11 @@ " axis=1)" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:55:03.892692Z", + "start_time": "2023-09-26T08:55:03.737322100Z" + } } }, { @@ -198,7 +218,7 @@ { "data": { "text/plain": "
", - "image/png": "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B34et8f/97/+/JckafEli2u834erPvT/7ZY7KLGkJaSVu9yofqOgPC4iC0UPAAAQE4p8RXro14f0wcoPJEkXdLtAEwdOVJybj0MAAABh06B5yb8BdYbHQIGgrGgZFeGpZ3cECLMre1+pbzd8W+v7eY1XkjS87fByhRMr7hp8l4ZsGCKf8aldw3Y6JPWQoDwuIgvf8gEAQNTLLMzUhO8naM7WOXK73Lpt4G26sPuFdocFAAAQeyrt2NxfAGFUQOyhzaNCev30Ot2vdA2Qnuk9gxZLi+QWuqD7BUF7PEQmih4AACCqbcrapPHfjteafWuUGJeofxzzDx3d5mi7wwIAAEBVomXEA2hLBITFxhFsFD0AAEDUWrhzoW6YeYN25+9Ws6RmeuG4F9StcTe7wwIAAIgx/JofQNVYbBzBFpwVYsrwer2655571LFjRyUmJurQQw/Vgw8+6B+uBAAAEA5fr/tal399uXbn71b3xt311ui3KHgAAAA4RblfdsdCh2c094tF874hEHUtWhiOHYRI0Ed6PPbYY5o6dapee+019ezZU/PmzdOll16q1NRU3XDDDcF+OgAAgHKMMfq/P/9Pz85/VpJ0bJtj9djRjykpPsnmyAAAAOAXqz+OZRofwK/0R/JMb4VgC3rR4+eff9app56qMWPGSJI6dOigt99+W7/99luwnwoAAKCcIm+RHvj1AX206iNJ0kXdL9KtA26Vp9IFMwEAABB+1XVuxmghJNrUqgObNo8GdS1aMNIDoRL0oseRRx6pl19+WStWrFCXLl20cOFCzZ49W0899VSl2xcUFKigoMB/OTMzU5JUVFSkoqKiYIeHECptL9otutHOsYF2jn7R2MaZhZm69cdbNW/7PLldbt12+G06p8s58nl98nl9dodni2hs52hFGwEAUFas/uo7Vvcbka7O01uVjvTg2EeQBb3occcddygzM1PdunWTx+OR1+vVww8/rAsvvLDS7R999FHdf//9Fa6fPn26kpKYhiISzZgxw+4QEAa0c2ygnaNftLTxDu8OvZnzpnb5dqme6um8pPPUYFUDfbHqC7tDc4Roaedolpuba3cIAACETmVTWTGdTYygnVG10pEeTG+FYAt60eO///2v3nzzTb311lvq2bOn/vjjD910001q1aqVLrnkkgrbT5o0SRMmTPBfzszMVNu2bXXiiSeqYcOG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efPsDiNq+Hw+u0NADNm6dav27NljdxhRgffeuiFvwRPKXMaF7JEBxBxjjFwul91hIMRo5+jn8/nkdvO7CABAaKxfv16zZ89WTk6OevfurcGDB8vlcvH+E6B169bps88+U2Zmpnr27KlTTz3V7pAiyqJFi3T66afrqquuUsuWLdW6dWu7Q4oo69at0y+//KK9e/eqW7duGj58uNxuN98RArRx40b9+uuv2rlzp/r376/BgwfbHVJEWbBggQ4//HB99dVXOvHEE+0OJyIVFRUpLi5OLpeL995a2rt3r5KSklSvXj3OeXUQ7s9/FD0QMkuXLtV3332n8ePH2x0KQig/P18+n09JSUn+Ez4n/+izaNEi/e9//9MDDzxA20apoqIixcfHS5L/Awev5ejDezMAuy1evFjDhw9Xjx49tHjxYrVt21adO3fW+++/L7fbTedLDRYtWqRRo0apb9++Wr58uVq0aCGPx6OxY8faHVpEWL16tY4//nhdeOGFmjBhgv+zTymOv+otXrxYxx13nAYPHqwlS5aoYcOGatGihT788EPVr1+fz441WLx4scaMGaNOnTpp/vz56tmzpy6++GJdc801docWERYuXKhjjjlGN998MwWPOlq2bJkmT56svXv3qn79+vroo4845wXor7/+0qWXXqrTTjtNN998sxISEjjn1YIdn/84shESf/zxhw4//HDl5OSUu54hYNHlzz//1OjRo3X00Udr0KBBevHFF7VlyxZ/pRbRYeHChRo8eHCFNuX1HD2WLl2qs88+WyNGjNBJJ52kL774Qnv27JHL5aKdowjvzQDslpOTo6uuukrnnnuuZs6cqeXLl+v222/XokWLNGjQIBUXF/u/+KKiFStW6KSTTtJll12mzz77TLNnz9bevXu1detWu0NzvNL3ujfffFPHHHOMnn76aXk8Hr300kt66KGH9Nhjj0kSnX/V2LVrly666CJddtll+uSTT/T777/rpptu0tdff60xY8YoIyOD74HVWLNmjU455RRddNFF+vzzz7V06VIdeuih+vrrr+0OLSL8+eefGjZsmMaPH68nn3xSPp9PCxYs0Oeff65FixbZHV5EWLJkiYYNG6akpCT169dPS5Ys0UUXXeS/ne8EVduwYYPOO+88rV69Wp9//rmmTp2qgoICvi8HyK7Pf7yjI+gWLlzofzO67bbbyt1GBTR6rFmzRkcffbQ6deqkG2+8UZ06ddL//d//6eqrr9aqVav4wholFi5cqKFDh2rcuHF66KGHyt1WdmQPItfKlSs1ZMgQpaamauTIkSooKNDEiRN13333afPmzXyQixK8NwNwgoKCAuXk5Gj06NGKi4tTs2bNdM455+iNN97Qnj17NGLECEnyT5WDAwoKCvTiiy9q5MiRuu++++RyudSyZUv17dtXixcv1sSJE/X000/bHaZjlb7Xbdy4UV26dJEkHXnkkXrzzTf16aef6oUXXlCPHj20adMmSaxRUZmNGzfKGKOrr75aktSoUSONGDFCXbt21eLFi3XyySdLonBUmaKiIv3nP//RgAEDNGnSJCUkJKhVq1a68sor9d1332ndunV2h+hoPp9P999/v3JycnTfffdJkk466SRdddVVOvnkk3XBBRfo/PPPtzlKZ8vOzta4ceN04YUX6p///KceeeQRXXHFFWrWrJl/G74TVM4Yo08//VStWrXS559/ri5duuidd94pV/jgPaN6dn3+490IQbV27VoNGzZMF198sf7xj3+oqKhIU6ZM0cSJE3XTTTfpr7/+UmFhod1hIgi+/PJLDRw4UC+//LIuvvhivfnmm5owYYJyc3N11VVXae3atXxhjXAbN27U0KFDdf755+sf//iHCgsL/R+Ozj//fH399dfat28fH44i3BtvvKHhw4frtdde0+23366ZM2fqoosu0ty5c3Xvvfdq27ZttHGE470ZgFM0bNhQxcXFmjlzpv+6+Ph4HXHEEXrllVe0bds23X333ZLofDmYx+PRueeeqxtuuEHx8fFyuVx6+OGH9fbbbys3N1erV6/WtGnTdN5559kdqqP5fD4tWrRI7777rtLS0vTZZ59p5syZmjNnjho2bKgzzzxTEh33Vdm7d68WL17sv5yTk6PExEQ9++yz2rJli5566ikbo3O2Ro0aadSoUUpJSfEfXy1atJDb7eZzWA3cbremTJmiAQMGaODAgTr66KNVr149vfDCC1q2bJluueUWzZ8/X+PGjbM7VMfKzs7W3r17/WtAuVwubdq0SV9//bWGDBmiYcOG6eeff5bEjxoP5nK5dOqpp+qKK67QEUccoWnTpqlnz556++239eKLLyovL4++rxrY9fmPd3IE1TfffKMmTZqoQYMG2rZtm8aOHau3335b8+bN0xdffKExY8bogw8+kNfrtTtUWJSVlaXly5crKyvLf92FF17o/6Dx97//XZmZmXxhjWALFy5Up06dlJGRoQ0bNujUU0/V559/rr1792rNmjW66aab9OKLL1aYKgeRJS8vT1u3blVBQYH/ukmTJuncc8/VkiVL9Nprr/FFLMLx3gzAKVwul8466yz9+uuv+uqrr8pdP3ToUJ100kmaN2+eiouLbYzSmeLi4tS/f3/17dtXUslUV88//7w++eQTvfrqq/rggw908803a968eVq5cqW9wTrYxRdfrF27dunZZ59V+/bt1bBhQyUmJqply5Z65plntHXrVv3+++92h+lIzZs31yGHHKLXX39dTz31lL766isNGTJEw4cP1/nnn68BAwZo+fLldofpOMYYxcfH629/+5suv/xySQdGErVo0UJNmzZVXNyB5XbLdgrigBYtWuizzz5TcnKydu/erRdeeEFHHHGEunTpoosuukhnnXWW5s6dq927d9sdqiOlpaUpPz9fTz75pFasWKE777xTr7zyii677DLdcsstatSokc477zzt2rWLPpxKtGrVyl8Uj4+P1wsvvKBevXrpnXfe0bRp05Sfny+Xy6U33njD5kidya7PfxQ9EFRXXnmlbrrpJv3000867LDD5Ha79f777+ubb77RihUr1K9fP911113Kzc21O1RY1LNnTzVo0EC//fZbuYr2mWeeqTFjxmjGjBnauXOnjRHCqrFjx2ry5Mnas2ePOnfuLJfLpQ8//FD/+9//NGfOHI0aNUovvfSSduzYYXeosKBNmzbat2+ffzqH0g8aN910kwYNGqSXXnpJeXl5doYIi3hvBmCXbdu2afbs2fr111+1c+dOeTweXXzxxfJ6vXr++ec1a9Ys/7ZxcXHq27ev1q5dW+5HNbGsNH+//PKLMjIylJCQ4L+tS5cuWrRokcaOHevvQE1PT1d8fLxSU1PtCtlRyh5/GRkZkqQePXqoU6dO+u2337R+/XpJB0Z1JCYmKjk5WUlJSbbF7CRlj78dO3aoZcuWevbZZ1VcXKwXX3xR119/vcaNG6cnn3xSktSsWTNt3LjR5qido/RHQ8YYGWOUlpbmv1x6zOXl5Wnfvn3+Hx/dc889uvjii1mnR+XzV6pZs2b69NNP9fjjj6tFixaSSgpI8fHxatmypXJzcxUfH29LvE5mjFFCQoKeeeYZLV26VBMmTNDUqVP10ksv6ZZbbtFZZ52ljz76SJmZmXrvvffsDtcRdu/eraVLl2rp0qXKzMwsN3271+tV/fr1NWXKFH/h48UXX9S1116rSy+9VBs2bLA5evs55vOfAYLE6/X6/37yySfNWWedZebNm1futj179hiPx2Pef/99W2JEcB155JGmb9++Zs2aNRVuS09PN88884wNUSEYfD6f/+///ve/5sorrzQ//fSTMebA69nn85l69eqZV155xZYYERxer9d069bNnHDCCaa4uNgYY0xRUZExxpji4mLToEED8+abb9oZIizgvRmAXRYuXGg6dOhgDj30UNO6dWvTpk0b8/HHHxtjjFm8eLHp2bOnGT16tHn99deNMSXvPTfeeKMZMWKEycnJsTN0Rzg4f23btjWfffaZKSws9G9T9hxvjDG33HKLGTt2rMnKygp3uI5T2fH3ySefGGOM2bBhgzn11FNNQkKCGT9+vDHGmN27d5sHHnjA9OvXz+zYscPO0B2hsvx99NFHxhhjsrOzzZ49e8p9B/R6veb00083t99+u10hO8rSpUvNsccea37++WdjTPnvVmWtW7fONGjQwKxevdo8/PDDJiEhwf85LZYFmr+yrr32WnPOOeeY/Pz8UIcXEQoKCowxJbkrm7+CggKzadMm06dPH/PXX38ZY4wpLCw0mzdvNn379jWffvqpLfE6yaJFi8yAAQNMly5dTPv27c0ZZ5xhtmzZUm6b0u/NeXl55vLLLzcJCQmmYcOGZv78+XaE7ChO+vxH0QNBVfaD95w5c8q94fh8PjN//nzTrVs388cff9gRHoKk9AS/d+9e07VrVzNo0CDz559/+m/PyckxgwcPNu+8845dISIIyn44+vPPP/0fnIwpea2vXLnS9O7d2/z44492hIcgKH0t//HHH6Zly5ZmzJgxJjMz03/7jh07TO/evc306dPtChFBwHszgHDbsWOH6dSpk7n99tvNhg0bzJw5c8y1115rPB6P+cc//mGMMWbJkiXm1FNPNZ07dzYdOnQwI0aMMI0aNTILFiywN3gHqCp/cXFx5umnnzbZ2dnltt+9e7eZNGmSSU9PN4sXL7YpaueoLn+lx9/GjRvNLbfcYlq0aGHS0tLM4Ycfbpo3b06Hlan+9fvkk09WKKqtWrXK3HnnnSYtLc3fiRrL1q5daw499FCTlpZmBg4caH755RdjTOUd97t37zb9+/c3Z5xxhqlfvz4FD1O7/BljzKZNm8wdd9zB+a+MyopGBxc++vfvbx555BFjTEnR48EHHzSdOnUyGzZssCVmp1i2bJlp2rSpmThxolmwYIF59dVXzbHHHuv/QW/ZPJZ+x7rmmmtMWlpauT6xWOW0z38UPRB01VXh77rrLjNw4ECzffv2MEaEUCg9wW/cuNH07NnTdO/e3TzyyCPmo48+MhMnTjSNGzc2q1evtjlKWFXd6/nee+81vXv3Nps3bw5jRKirmn719OOPP5p27dqZgQMHmrffftv88MMP5s477zTNmzc369atC1OUsKqqdua9GUA4rVy50nTt2rXCF9hHHnnEuFwuM3XqVGOMMZs3bzZz5swx9913n3nllVfMihUrbIjWearLn9vtNi+//LIxpuTz+Ndff22uuuoq06FDBwpG+wV6/O3bt89s2rTJvPzyy+bzzz/n885+tTn+tm3bZu69917Ttm1bCkam5HPYuHHjzJlnnmnefPNNc8YZZ5h+/fpV2XG/ZcsWExcXZxo0aMDr19Q+fz/88IO54oorTLt27cjffjUVjXw+n8nPzze33XabOeyww0z37t3N2LFjTbNmzWI+h1lZWeacc84xV155ZbnrL7jgAjNixIhK7/PSSy8Zl8vF+W8/p33+o+iBOlm/fn2tfsUxffp0M3HiRJOSksIvSSPI6tWrzaxZs2rcrri42Fx55ZVmyJAh5pBDDjGDBw/mpB9BAm3nUp999pm5+eabTWpqasx/MIoUy5YtM7fccku5KTEqs3XrVjNq1CjTrVs30759e9O7d2/z+++/hylKWBVoO5fivRlAqMybN8/Uq1fPLFy40Bhjyp2X7r333nK3oaKa8peQkOD/RfOWLVvM66+/btauXWtHqI4UyPG3aNEiu8JzvNocf0VFRWbdunX8CKqMjz76yD/9748//mhOP/30Kjvu9+3bZ2688UazfPlyW2J1otrkb+fOnebDDz+kYLlfIEWj0h+vbtu2zbz33nvmyiuvNI899hg/OjAlx9MNN9xg3n77bWPMgVkR3n//fXPUUUeZ4uJi/3VlVTbde6xy2uc/lzFlVgUCArBgwQKNHDlSL774os4666xKtzHGyOVySZLy8/N12223adasWfrPf/6j3r17hzNc1NGiRYs0atQojR49Wo888oiaNWtWYRuzf1G20oXY9u3bp7y8PCUlJalhw4bhDhl1EGg7l76eJemOO+7Q7NmzNXXqVPXq1Suc4aIOFi1apEGDBqmgoECffvqpxowZU2Gbg9t4/fr18nq9Sk1NVXp6ejjDRR3Vtp15bwYQaqNGjVJOTo4+/vhjNW7cWEVFRYqPj5fX69Xo0aPVpk0bvfTSS3K73f7PkjggkPxNmzZN8fHxFd7HEVj+Xn75ZblcLo6/SgT6+o2Li7M7VMebNWuWnnvuOa1Zs0ZTp07V4MGDVVBQoHXr1qlr167+3KJyleUvPz9f69evV9euXTn/HeTjjz/Wzp07dcUVV2j27Nl66qmntG7dOr344osaPHhwhT4cHGCM0dy5c3XEEUf4L7tcLn388cd64IEHNGfOHHk8HrlcLmVmZtLnVQUnff7jKEetLFy4UEcddZQuuuiiKgsePp/P/6aTm5ur+vXr6/HHH9eMGTPoVIkQa9eu1ciRI3XRRRfplVdeqbQjvLi42P8lYceOHZKk1NRUtWjRgpN/hKhNO0vyt/Pf//53ffLJJxQ8IsDChQs1ePBgXX755Tr33HP19ttvKzc3V2V/71D2i0JWVpYkqX379jrkkEMoeESI2rYz780AwmHcuHHyer2aOHGi9u7dq/j4ePl8Pnk8HrVs2VIZGRmKi4uj46UKgeSvtKOUDr+KAsmfx+Ph+KtCoK9fVM3n80mSjjnmGN1www065JBDNG7cOM2ePVsTJ07Ucccdp+zsbPJYheryd9ttt+n4449XdnY257+DnHrqqbriiiskScOGDdONN96ojh076tprr9Wvv/4ql8ulwsJCrVixwuZIncflclUoeEgl352ys7P9BY+7775bY8aMUVFRkZ3hOpaTPv/xDo+ALVu2TEceeaRuvPFGPfXUUyouLtasWbP00Ucf6ccff/RvV3rgTpgwQY8//rh27dql+vXrV9qhCmeaPXu2jjzySD3++OMqLi7WY489pssvv1z33HOPvvvuO0nyfzibPHmyJk2apDVr1tgZMuqgLu28cuVKSVLjxo1tixuBmT9/vo466ihNmDBBzz//vAYPHqxPP/1UW7Zskcvl8neIl36YmzBhgp588knt2bPHzrBRS3VpZ96bAYTDmDFjdOaZZ2rJkiUaN26c9uzZ4/+eEB8fr0aNGqmoqEhMPFA58mcN+bOG/Fnndrv9+SnbcT98+HC9/vrr+uCDD9SgQQM67atQU/7ef/99NWjQwOYonaumolFp0Q2VK/u6TE1NVWJior/g8dRTT+npp59mhFYVnPT+QUkZNTLGqKioSHfeeaeSk5N1yimnSJLOOOMMbdiwQdu2bdPu3bt11VVX6b777lPTpk0llZwkpkyZouuvv97O8FEHCxYsUF5eniTpxBNPVGFhodq3b6/33ntP3333nS666CJdc801kqSkpCT99NNPSk5OtjNk1EFd2plRPJFhz549Ouqoo3TttdfqoYceklTyi4s33nhDDz74oP79739X+ILFOTvy0M4AnMTr9crj8UiS/xd9N910k5KSkvTGG2+oe/fuGjt2rHbt2qVvvvlGv/zyCx0GZZA/a8ifNeTPmrL5K6v0Bygul0vHHHOMnnjiCTVo0ECzZ89Wz549bYjUmchf8JUWjUpzJ0lTpkzR8OHDlZycrOnTp1M02q+q469UaUf9rbfeqilTpujnn3/W4YcfHsYIna3sqBinvX+wpgdqVHoC+P3333XXXXdJKpnvvUOHDnrkkUeUnp6uP//8U6effrpuueUWPfLII/777ty5018EgbOVPdH/+9//1qeffqpzzjlHr776qt544w01b95c27Zt0x133KHNmzfrrbfe8rftnj17lJaWZmf4CBDtHP1K23jBggXq16+fpJIPIj6fT/fee68+/vhjfffdd2ratGmFUQCcsyMH7QzAbjt37tTOnTuVnZ3tnw7C5/P5f81X+rcxRqtWrdJrr72mtWvXqlGjRho/frx69OhhZ/i2I3/WkD9ryJ81NeXvYF6vV4899pgefvhh/fTTT+rbt28Yo3Ue8hdc1XXal+2QHjt2rH766aeYLxrV9vh79913df755ys5OVmzZs1S//79wxmu4xQUFMjr9SohIcF/3B1cOHfM+0dIlkdH1FiwYIEZM2aMycrKMsYY88cff5ihQ4eaE044waxdu7bcts8//7xp0qSJ2bhxoykqKjLGGOPz+cIdMupgwYIFZuzYsSYnJ8cYY8zcuXNN/fr1Tb9+/cwZZ5xRbttly5YZl8tlvv76a/91tHNkoJ2j38FtXKq07bZv325SUlLMAw88UO52r9dbbjs4G+0MwG6LFi0y/fr1M127djUtW7Y011xzTaXbcb6pHPmzhvxZQ/6sCTR/B/v444/NkiVLQhyd85E/63bs2GGW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\n" + "image/png": "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ERERERERERER6QU2PYiIiIiIiIiIiIiISC+w6UFERERERERERERERHqBTQ8iIiIiIiIiIiIiItILbHoQEREREREREREREZFeYNODiIiIiIiIiIiIiIj0ApseRERERERERERERESkF9j0ICIiIiIiIiIiIiIivcCmBxERERERERERERER6QU2PYiIiIiIiIiIiIiISC+w6UFERERERERERERERHqBTQ8iIiIiIiIiIiIiItILbHoQEREREREREREREZFeYNODiIiIiIiIiIiIiIj0ApseRERERERERERERESkF9j0ICIiIiIiIiIiIiIivcCmBxERERERERERERER6QU2PYiIiIiIiIiIiIiISC+w6UFERERERERERERERHqBTQ8iIiIiIiIiIiIiItILbHoQEREREREREREREZFeYNODiIiIiIiIiIiIiIj0ApseRERERERERERERESkF9j0ICIiIiIiIiIiIiIivWAgdwFPE0IAAFJTU2WuhMorNzcXmZmZSE1NhaGhodzlUBXhONcOHGf9xzGuHTjONUfBvm/BvjDR88h93MTPF2mYnzTMTxrmJw3zk4b5ScP8pGF+0sidX3mOmXSu6ZGWlgYAaNCggcyVEBERERFVr7S0NNSpU0fuMqgG4HETEREREdVGZTlmUggdO51Mo9EgJiYGlpaWUCgUcpdD5ZCamooGDRogOjoaVlZWcpdDVYTjXDtwnPUfx7h24DjXHEIIpKWlwdnZGUol70BLzyf3cRM/X6RhftIwP2mYnzTMTxrmJw3zk4b5SSN3fuU5ZtK5Kz2USiVcXFzkLoMksLKy4gdHLcBxrh04zvqPY1w7cJxrBl7hQeWhK8dN/HyRhvlJw/ykYX7SMD9pmJ80zE8a5ieNnPmV9ZiJp5EREREREREREREREZFeYNODiIiIiIiIiIiIiIj0ApseVGmMjY2xYMECGBsby10KVSGOc+3AcdZ/HOPageNMRFWFny/SMD9pmJ80zE8a5icN85OG+UnD/KSpSfnp3IPMiYiIiIiIiIiIiIiIKoJXehARERERERERERERkV5g04OIiIiIiIiIiIiIiPQCmx5ERERERERERERERKQX2PQgIiIiIiIiIiIiIiK9wKYHERERERERERERERHpBTY9iIiIiIiISFZCCLlLoFokNjYWFy9elLsMvaHRaOQugWqR2NhY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f39s3rwZ33zzDU6cOIFXXnkFx48fx4QJEwAABgYG0Gg00Gg0+PrrrxEYGKh/Z9lXANc/aZhfUWx6EBERERER6Qnxz92LO3ToALVajdDQUO17lpaWePfdd+Hp6Yk9e/YgLS0NAGBvb4+FCxeiffv2stSsS5ifNMxPGuYnDfOThvlJx0nniuP6Jw3zK45NDyIiIiIiIj2hUCgAAAMHDkRoaCgWL16M9PR0APkHxNbW1vj0009x5swZnDp1Svtz+vLQSqmYnzTMTxrmJw3zk4b5ScNJZ2m4/knD/Ipj04OIiIiIiEjPNG3aFP/973+xadMmzJ49GwkJCdoDYkNDQ7Rr1w516tSRuUrdxfykYX7SMD9pmJ80zK9iOOlcObj+ScP8njCQuwAiIiIiIiKqfH369MG2bdvw+uuvIzY2FsOHD0e7du2wYcMGPHz4EA0aNJC7RJ3G/KRhftIwP2mYnzTMr+IKJp0HDBgAU1NTfPbZZ7C1tQVQ+yadK4rrnzTML59CFFx/RURERERERHrn8uXLmD59OiIiImBgYACVSoUtW7bA09NT7tJqBOYnDfOThvlJw/ykYX4Vt3fvXrz++uvw9fUtMum8fv16nD9/Hi4uLnKXqPO4/klT2/Nj04OIiIiIiEjPpaamIjExEWlpaXByctKedUplw/ykYX7SMD9pmJ80zK/iavukc2Xg+idNbc6PTQ8iIiIiIiIiIiKiSlabJ52J5MSmBxERERERERERERER6QWl3AUQERERERERERERERFVBjY9iIiIiIiIiIiIiIhIL7DpQUREREREREREREREeoFNDyIiIiIiIiIiIiIi0gtsehARERERERERERERkV5g04OIiIiIiIiIiIiIiPQCmx5ERERERERERERERKQX2PQgIiIiIiIiIiIiIiK9wKYHERERERERERERERHpBTY9iIiIiIiIiIiIiIhIL7DpQUREREREREREREREeuH/AWY9YYYEpFd5AAAAAElFTkSuQmCC" }, "metadata": {}, "output_type": "display_data" @@ -212,7 +232,11 @@ "plt.show()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:55:10.720449900Z", + "start_time": "2023-09-26T08:55:05.774912600Z" + } } }, { @@ -261,7 +285,11 @@ "cursor = connection.cursor()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:55:15.168755900Z", + "start_time": "2023-09-26T08:55:15.131758Z" + } } }, { @@ -284,7 +312,11 @@ "connection.commit()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:55:17.530081800Z", + "start_time": "2023-09-26T08:55:17.516083700Z" + } } }, { @@ -307,7 +339,11 @@ "connection.commit()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:55:20.000617300Z", + "start_time": "2023-09-26T08:55:19.246090300Z" + } } }, { @@ -328,7 +364,11 @@ "mmsi, trajectory, sog = cursor.fetchone()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:55:21.021821200Z", + "start_time": "2023-09-26T08:55:20.928825600Z" + } } }, { @@ -338,7 +378,7 @@ { "data": { "text/plain": "
", - "image/png": "iVBORw0KGgoAAAANSUhEUgAABj0AAAO5CAYAAABCHuf5AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAADtyElEQVR4nOzdeXxU5b3H8e+ZJZN9T8hCwr4JGFBAxA1UULCi4t6WutRaq1Vvaa2lO/f2lmp7ba2i1qUurfuGG1WwQkX2LSyyQyAhCQSy75lk5v4RMiQkhIRMciYzn/frxYvMmXPO/OY5J5PkfM/zPIbb7XYLAAAAAAAAAACgl7OYXQAAAAAAAAAAAIA3EHoAAAAAAAAAAAC/QOgBAAAAAAAAAAD8AqEHAAAAAAAAAADwC4QeAAAAAAAAAADALxB6AAAAAAAAAAAAv0DoAQAAAAAAAAAA/AKhBwAAAAAAAAAA8AuEHgAAAAAAAAAAwC8QegAAAADdxDAM/fCHPzztei+99JIMw9CBAwe6vygAAAAA8GOEHgAAAEAnbd26VTfccIP69eun4OBgpaamaurUqXriiSfMLk2SVFhYqD/+8Y+6+OKLlZCQoOjoaE2cOFFvvvlmq3WXLVsmwzDa/Ld69WrPegcOHDjleoZh6Hvf+55n3YqKCv3mN7/RlVdeqdjYWBmGoZdeeqnNWp977jldcskl6tOnjxwOhwYMGKA77rjjlAHQkSNH9P3vf1+pqakKDg5W//799d3vfrfVerm5ubrpppsUHR2tyMhIXXPNNdq/f3+b+3zhhRc0YsQIBQcHa8iQIac8jp3ZJwAAAABz2MwuAAAAAOhNVq5cqSlTpig9PV3f+973lJSUpJycHK1evVqPP/647r///k7vc/bs2brlllvkcDi8UuOqVav0i1/8QjNmzNAvf/lL2Ww2vfvuu7rlllu0fft2zZs3r9U2DzzwgMaPH99i2eDBgz1fJyQk6B//+Eer7T799FO9+uqrmjZtmmfZsWPH9N///d9KT09XRkaGli1bdspaN23apAEDBmjmzJmKiYlRVlaWnnvuOX388cfavHmzUlJSPOvm5OToggsukCTdc889Sk1NVV5entauXdtinxUVFZoyZYpKS0v185//XHa7XX/+8591ySWXKDMzU3FxcZ51//a3v+mee+7R9ddfrzlz5mj58uV64IEHVFVVpYcffviM9gkAAADAPIbb7XabXQQAAADQW1x11VVat26ddu/erejo6BbPFRQUKDEx0fPYMAzdd999evLJJ3u0xqysLFksFvXr18+zzO126/LLL9eKFStUWFiosLAwSY09PaZMmaK3335bN9xwQ6df6/LLL9e6det05MgRBQcHS5Jqa2tVXFyspKQkrV+/XuPHj9eLL76o22+/vUP73LBhg8aNG6f58+frZz/7mWf5jBkztHPnTq1bt67dkOHRRx/Vww8/rLVr13qCnJ07d2rUqFH66U9/qt///veSpOrqaqWlpWnixIn6+OOPPdt/+9vf1sKFC5WTk6OYmJhO7RMAAACAuRjeCgAAAOiEffv2aeTIka0CD0ktAo/mFi5cqFGjRsnhcGjkyJH69NNPWzzf1pwe/fv31ze+8Q0tXrxYY8aMUXBwsM466yy99957p61xwIABLQIPqTGAufbaa1VbW3vKIZnKy8tVX19/2v03yc/P19KlSzVr1ixP4CFJDodDSUlJHd7Pyfr37y9JKikp8SzbuXOn/vWvf+mhhx5SXFycampq5HQ629z+nXfe0fjx41v0XBk+fLguu+wyvfXWW55lS5cuVWFhoe69994W2993332qrKzUJ5980ul9AgAAADAXoQcAAADQCf369dOGDRu0bdu2Dq3/1Vdf6d5779Utt9yiRx99VDU1Nbr++utVWFh42m337Nmjm2++WdOnT9f8+fNls9l04403asmSJWdU++HDhyVJ8fHxrZ674447FBkZqeDgYE2ZMkXr168/7f7eeOMNuVwufetb3zqjeporLCxUQUGB1q9frzvuuEOSdNlll3me//zzzyVJffr00WWXXaaQkBCFhIRo+vTpLcIil8ulLVu2aNy4ca1eY8KECdq3b5/Ky8slNQ6tJanVuueee64sFovn+c7sEwAAAIC5mNMDAAAA6ISf/OQnmj59usaMGaMJEybooosu0mWXXaYpU6bIbre3Wn/Hjh3avn27Bg0aJEmaMmWKMjIy9Prrr+uHP/xhu6+1e/duvfvuu5o1a5Yk6bvf/a6GDx+uhx9+WFOnTu1U3UVFRXr++ed10UUXKTk52bM8KChI119/vWbMmKH4+Hht375df/rTn3TRRRdp5cqVGjt27Cn3+eqrryo5OVmXXnppp2ppS2pqqmprayVJcXFx+utf/9riPe7Zs0eSdPfdd2v8+PF68803lZ2drXnz5unyyy/Xli1bFBoaqqKiItXW1rZ4j02aluXl5WnYsGHKz8+X1Wpt1UMnKChIcXFxysvLk6RO7RMAAACAuQg9AAAAgE6YOnWqVq1apfnz5+uzzz7TqlWr9OijjyohIUHPP/+8Zs6c2WL9yy+/3BN4SNLZZ5+tyMjIUw4x1VxKSoquu+46z+PIyEh95zvf0SOPPKLDhw93eAippt4YJSUleuKJJ1o8N2nSJE2aNMnzeObMmbrhhht09tlna+7cua2G4mqye/dubdiwQT/60Y9ksXS9A/m//vUv1dTUaMeOHfrnP/+pysrKFs9XVFRIkpKSkvTJJ594XrNv37669dZb9dprr+muu+5SdXW1JLU5KXzTEFxN61RXVysoKKjNeoKDg1us19F9AgAAADAXw1sBAAAAnTR+/Hi99957Ki4u1tq1azV37lyVl5frhhtu0Pbt21usm56e3mr7mJgYFRcXn/Z1Bg8eLMMwWiwbOnSoJLUY0ul07r//fn366ad6/vnnlZGR0aHXveaaa7R06VI1NDS0uc6rr74qSV4Z2kpq7AEzffp0zZkzR2+//bbmzZvXYgL4kJAQSdJNN93UImS58cYbZbPZtHLlyhbrNfUaaa6mpqbFOiEhIaqrq2uznpqamhbrdXSfAAAAAMxF6AEAAACcoaCgII0fP16///3v9fTTT8vpdOrtt99usY7Vam1zW7fb3RMlat68eXrqqaf0hz/8QbNnz+7wdmlpaaqrq2vV46LJa6+9pmHDhuncc8/1VqkegwYN0tixYz3BitTY60VqnNOjOavVqri4OE+IFBsbK4fDofz8/Fb7bVrWtK/k5GQ1NDSooKCgxXp1dXUqLCz0rNeZfQIAAAAwF6EHAAAA4AVNk1y3dWH8TO3du7dVOLJ7925JUv/+/U+7/YIFC/Tb3/5W//Vf/6WHH364U6+9f/9+BQcHKzw8vNVza9as0d69e73Wy6Mt1dXVKi0t9TxuCldyc3NbrFdXV6djx44pISFBkmSxWDR69Og2J2Jfs2aNBg4cqIiICEnSmDFjJKnVuuvXr5fL5fI835l9AgAAADAXoQcAAADQCUuXLm2zl8aiRYskyauTWefl5en999/3PC4rK9Mrr7yiMWPGnHY+jzfffFMPPPCAvvWtb+mxxx475XpHjx5ttWzz5s368MMPNW3atDbn63jttdckSd/85jc7+lbaVF9f3+YwX2vXrtXWrVs9QZIkTZ48WYmJiXr11Vc9Q0pJ0ksvvaSGhoYWk57fcMMNWrduXYuQYteuXfriiy904403epZdeumlio2N1dNPP93i9Z9++mmFhobqqquu6vQ+AQAAAJjLcPdUv3oAAADAD4waNUpVVVW67rrrNHz4cNXV1WnlypV68803lZaWpk2bNik6OlqSZBiG7rvvvhZzU0iNvTQmT56sl156SVLjhfs77rhDWVlZnh4c/fv3l8PhUEFBge655x716dNHf//73/X1119r0aJFuuKKK05Z49q1a3XRRRcpKipKjzzyiOx2e4vnJ02apIEDB0pqvPAfEhKiSZMmKTExUdu3b9ezzz4ru92uVatWacSIES22bWhoUGpqqgYMGKBVq1adsoYnn3xSJSUlysvL09NPP61Zs2Zp7NixkhrnGImKilJJSYn69u2rm2++WSNHjlRYWJi2bt2qF198UcHBwVq9erWGDBni2ecrr7yi2267TePHj9fs2bOVnZ2txx9/XBMnTtTSpUs9Q4mVl5dr7NixKi8v109+8hPZ7XY99thjamhoUGZmpqdXiCQ99dRTuu+++3TDDTfoiiuu0PLly/XKK6/of//3f/Xzn//cs15n9gkAAADAPDazCwAAAAB6kz/96U96++23tWjRIj377LOqq6tTenq67r33Xv3yl7/0BB7eMGTIED3xxBN66KGHtGvXLg0YMEBvvvlmu4GHJG3fvl11dXU6evSo7rzzzlbPv/jii57Q49prr9Wrr76qxx57TGVlZUpISNCsWbP0m9/8RoMHD2617eeff64jR47oF7/4Rbs1/OlPf9LBgwc9j9977z299957kqRvf/vbioqKUmhoqO666y4tXbpU77zzjqqrq5WSkqJbb71Vv/zlL1sN4fWd73xHQUFB+sMf/qCHHnpI0dHR+v73v6/f//73LeZOiYiI0LJly/SjH/1Iv/vd7+RyuTR58mT9+c9/bhVO3HvvvbLb7fq///s/ffjhh0pLS9Of//xnPfjggy3W68w+AQAAAJiHnh4AAACAD+rfv79GjRqljz/+2OxSAAAAAKDXYE4PAAAAAAAAAADgFwg9AAAAAAAAAACAXyD0AAAAAAAAAAAAfoE5PQAAAAAAAAAAgF+gpwcAAAAAAAAAAPALhB4AAAAAAAAAAMAvEHoAAAAAAAAAAAC/QOgBAAAAAAAAAAD8AqEHAAAAAAAAAADwC4QeAAAAAAAAAADALxB6AAAAAAAAAAAAv0DoAQAAAAAAAAAA/AKhBwAAAAAAAAAA8AuEHgAAAAAAAAAAwC8QegAAAAAAAAAAAL9A6AEAAAAAAAAAAPwCoQcAAAAAAAAAAPALhB4AAAAAAAAAAMAvEHoAAAAAAAAAAAC/QOgBAAAAAAAAAAD8AqEHAAAAAAAAAADwC4QeAAAAAAAAAADALxB6AAAAAAAAAAAAv0DoAQAAAAAAAAAA/AKhBwAAAAAAAAAA8AuEHgAAAAAAAAAAwC8QegAAAAAAAAAAAL9A6AEAAAAAAAAAAPwCoQcAAAAAAAAAAPALhB4AAAAAAAAAAMAvEHoAAAAAAAAAAAC/QOgBAAAAAAAAAAD8AqEHAAAAAAAAAADwC4QeAAAAAAAAAADALxB6AAAAAAAAAAAAv0DoAQAAAAAAAAAA/AKhBwAAAAAAAAAA8AuEHgAAAAAAAAAAwC8QegAAAAAAAAAAAL9A6AEAAAAAAAAAAPwCoQcAAAAAAAAAAPALhB4AAAAAAAAAAMAvEHoAAAAAAAAAAAC/QOgBAAAAAAAAAAD8AqEHAAAAAAAAAADwC4QeAAAAAAAAAADALxB6AAAAAAAAAAAAv0DoAQAAAAAAAAAA/AKhBwAAAAAAAAAA8AuEHgAAAAAAAAAAwC8QegAAAAAAAAAAAL9A6AEAAAAAAAAAAPwCoQcAAAAAAAAAAPALhB4AAAAAAAAAAMAvEHoAAAAAAAAAAAC/QOgBAAAAAAAAAAD8AqEHAAAAAAAAAADwC4QeAAAAAAAAAADALxB6AAAAAAAAAAAAv0DoAQAAAAAAAAAA/AKhBwAAAAAAAAAA8AuEHgAAAAAAAAAAwC8QegAAAAAAAAAAAL9A6AEAAAAAAAAAAPwCoQcAAAAAAAAAAPALhB4AAAAAAAAAAMAvEHoAAAAAAAAAAAC/QOgBAAAAAAAAAAD8AqEHAAAAAAAAAADwC4QeAAAAAAAAAADALxB6AAAAAAAAAAAAv0DoAQAAAAAAAAAA/AKhBwAAAAAAAAAA8AuEHgAAAAAAAAAAwC8QegAAAAAAAAAAAL9A6AEAAAAAAAAAAPwCoQcAAAAAAAAAAPALhB4AAAAAAAAAAMAvEHoAAAAAAAAA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54AUPVCm1qxZo7lz5+rmm2+2OxSUkezsbPl8PsXHx/t3+Oz8w8+KFSv03//+Vw8//DBtG8Y8Ho/cbrck+U86+D6HF47LAOy0cuVK9ezZU23atNHKlStVv359NW/eXB999JGcTicdL6ewYsUK9e/fXx07dtS6detUu3ZtuVwuDR482O7QQsKmTZvUu3dvXXnllRo3bpz/nCcf29/JrVy5Uueff766du2q1atXKzExUbVr19Ynn3yi2NhYzhlPYeXKlRo0aJCaNWumJUuWqG3btho5cqT+9re/2R1ayFi+fLnOO+883XHHHRQ8Smnt2rV68MEHdeTIEcXGxmrmzJns94rp999/1zXXXKOLLrpId9xxh2JiYtjvlYAd54Bs2Sgzy5Yt0+mnn66MjIwCzzMMLHysWrVKAwcO1LnnnqszzzxTr7zyinbt2uWv1CI8LF++XF27di3UpnyXw8uaNWt02WWXqVevXhowYIC++uorHT58WA6Hg7YOExyXAdgpIyNDY8aM0eWXX67vvvtO69at0913360VK1bozDPPVF5env+iF4WtX79eAwYM0LXXXqsvvvhC8+fP15EjR7R79267Qwt6+ce56dOn67zzztOzzz4rl8ulV199VY8++qiefPJJSaLj7yQOHjyoq666Stdee60+++wzLV68WLfffrtmzZqlQYMG6cCBA1wDnsTmzZt1wQUX6KqrrtKXX36pNWvWqGnTppo1a5bdoYWMVatWqXv37rr55pv19NNPy+fzaenSpfryyy+1YsUKu8MLCatXr1b37t0VHx+v5ORkrV69WldddZX/da4JTmz79u0aPny4Nm3apC+//FJTpkxRTk4O18rFZNc5IEd1lInly5f7D0h33XVXgdeogoaHzZs369xzz1WzZs102223qVmzZnrjjTd04403auPGjVy0honly5fr7LPP1tixY/Xoo48WeO3YkT0IbRs2bFC3bt1UqVIl9evXTzk5ORo/frweeOAB7dy5k5O5MMBxGYDdcnJylJGRoYEDByoqKko1a9bUsGHD9M477+jw4cPq1auXJPmnysFfcnJy9Morr6hfv3564IEH5HA4lJSUpI4dO2rlypUaP368nn32WbvDDFr5x7kdO3aoRYsWkqSzzjpL06dP1+eff66XX35Zbdq00R9//CGJNSqKsmPHDhljdOONN0qSKleurF69eqlly5ZauXKlhgwZIonCUVE8Ho/efvttde7cWRMnTlRMTIzq1KmjG264QXPnztXWrVvtDjHo+Xw+PfTQQ8rIyNADDzwgSRowYIDGjBmjIUOG6IorrtCIESNsjjK4paena+zYsbryyiv1f//3f3r88cd1/fXXq2bNmv73cE1QNGOMPv/8c9WpU0dffvmlWrRooffee69A4YPjxsnZdQ7IEQkBt2XLFnXv3l0jR47Uv/71L3k8Hr344osaP368br/9dv3+++/Kzc21O0xY9L///U9dunTRa6+9ppEjR2r69OkaN26cMjMzNWbMGG3ZsoWL1hC3Y8cOnX322RoxYoT+9a9/KTc3139yNGLECM2aNUspKSmcHIWBd955Rz179tRbb72lu+++W999952uuuoqLVy4UJMmTdKePXto5xDGcRlAMEhMTFReXp6+++47/3Nut1tnnHGGXn/9de3Zs0f33XefJDpejudyuXT55Zfr1ltvldvtlsPh0GOPPaYZM2YoMzNTmzZt0tSpUzV8+HC7Qw1qPp9PK1as0Pvvv68qVaroiy++0HfffacFCxYoMTFRl156qSQ67k/kyJEjWrlypf9xRkaG4uLi9Pzzz2vXrl165plnbIwuuFWuXFn9+/dXQkKCf/uqXbu2nE4n52DF4HQ69eKLL6pz587q0qWLzj33XEVHR+vll1/W2rVrdeedd2rJkiUaO3as3aEGrfT0dB05csS/DpTD4dAff/yhWbNmqVu3burevbt+/vlnSdzUeDyHw6ELL7xQ119/vc444wxNnTpVbdu21YwZM/TKK68oKyuLvq9TsOsckKM5Au6bb75R9erVVbFiRe3Zs0eDBw/WjBkztGjRIn311VcaNGiQPv74Y3m9XrtDhQVpaWlat26d0tLS/M9deeWV/hONf/7zn0pNTeWiNYQtX75czZo104EDB7R9+3ZdeOGF+vLLL3XkyBFt3rxZt99+u1555ZVCU+Ug9GRlZWn37t3KycnxPzdx4kRdfvnlWr16td566y0uyEIYx2UAwcDhcGjo0KH69ddf9fXXXxd4/uyzz9aAAQO0aNEi5eXl2RhlcIqKilKnTp3UsWNHSUenunrppZf02Wef6d///rc+/vhj3XHHHVq0aJE2bNhgb7BBbOTIkTp48KCef/55NWzYUImJiYqLi1NSUpKee+457d69W4sXL7Y7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FSdqY47LAMrLhg0bTMuWLQtdvD7++OPG4XCYKVOmGGOM2blzp1mwYIF54IEHzOuvv27Wr19vQ7TB52T5czqd5rXXXjPGHD0XnzVrlhkzZoxp1KgRBaM/FXf7S0lJMX/88Yd57bXXzJdffsl5zp9Ksv3t2bPHTJo0ydSvX5+CkTl6DjZ27Fhz6aWXmunTp5tLLrnEJCcnn7DTfteuXSYqKspUrFiR7++fSprDH374wVx//fWmQYMG5PBPpyoa+Xw+k52dbe666y5z2mmnmdatW5vBgwebmjVrRnwO09LSzLBhw8wNN9xQ4PkrrrjC9OrVq8ifefXVV43D4WAf+KdgOwek6IFS27ZtW4nu5pg9e7YZP368SUhI4G7SELFp0yYzb968U74vLy/P3HDDDaZbt26mSZMmpmvXruz0Q0hx2znfF198Ye644w5TqVKliD8xCiVr1641d955Z4GpMYqye/du079/f9OqVSvTsGFD0759e7N48eJyihJWFLeN83FcBlAWFi1aZKKjo83y5cuNMabAPmnSpEkFXkNhp8pfTEyM/27mXbt2mWnTppktW7bYEWpQKs72t2LFCrvCC3ol2f48Ho/ZunUrN0AdY+bMmf6pf3/88Udz8cUXn7DTPiUlxdx2221m3bp1tsQarEqSw/3795tPPvmEouWfilM0yr95dc+ePebDDz80N9xwg3nyySe58cAc3Z5uvfVWM2PGDGPMXzMifPTRR+acc84xeXl5/ueOVdR075Eq2M4BHcYcsyoQUExLly5Vv3799Morr2jo0KFFvscYI4fDIUnKzs7WXXfdpXnz5untt99W+/btyzNclMKKFSvUv39/DRw4UI8//rhq1qxZ6D3mz4XZ8hdjS0lJUVZWluLj45WYmFjeIaMUitvO+d9lSZowYYLmz5+vKVOmqF27duUZLkppxYoVOvPMM5WTk6PPP/9cgwYNKvSe49t527Zt8nq9qlSpkqpVq1ae4aIUStrGHJcBlKX+/fsrIyNDn376qapWrSqPxyO32y2v16uBAweqXr16evXVV+V0Ov3nkfhLcfI3depUud3uQsdvFC9/r732mhwOB9tfEYr7/Y2KirI71KA3b948vfDCC9q8ebOmTJmirl27KicnR1u3blXLli39ucWJFZXD7Oxsbdu2TS1btmQfeJxPP/1U+/fv1/XXX6/58+frmWee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6DLDxAJ+5M8a8mcN+bOmJPk73gUXXBDxI2QkchgIK1euVL9+/XTJJZfooosu0k033SRJJ10U2uVy6bTTTtPChQsjepRMabe/jh076v3334/47W/dunU677zz1LhxY910001q2rSpnn32WQ0fPlwZGRmS/lpLJmiOIeVdZUHoWL58ualQoYIZP358geezsrJO+DO33XabufTSSwvdfYrgdKI2zsnJMT6fzzz77LPG6XSaUaNGmR9++MHs2bPH3HfffaZRo0Zm586dNkWNkqKdI8PJ2vl4s2fPNgkJCeaTTz4pp+gQCLQxADv5fD5z3333meHDh/uf27Ztm+nRo4dZsGCB+fbbb22MLviRP2vInzXkzxryZx05tG79+vWmevXq5r777jM//fSTmTJlimndurXZvn27/z3Bcod9sCnt9kc+j8rLyzO33nqrufHGG/3PpaenmyFDhhiHw2EGDhzof/5ko47KGwuZo0h79uxRv3791L17d02ePFler1f/+Mc/tGHDBm3atEk33nij+vXrp9atW0uSNm/erDfffFNvvfWW5s+fX+DuUwSnE7Xx+vXrtWXLFt14443q27evPvnkE40dO1azZ89WlSpVlJmZqU8++cQ/ygfBjXaODMXZZ/fv31+tWrWSJPXp00fdu3fXM888owsuuEAOh4O7/4McbQzAbg6HQ5s2bdKePXv8z7377rv67bffdPXVVyslJUUNGjTQd999p7i4uALziIP8WUX+rCF/1pA/68ihNcYYTZs2Tb1799YjjzwiSapXr57ef/997d692z+KgZwVrbTbH/k8yuVyaePGjapdu7akoyM6KlSooHPOOUdJSUn69NNPddNNN2nKlCknHXVU3ih64IS6deumHTt26NNPP9XUqVPl8XjUsWNHNWrUSC+88IJWrVqlSZMmKT09Xffcc4+WL1+uuXPnRvSCSKHmZG38/PPPa8WKFXr11Vf1888/a9euXcrNzVXz5s2VlJRkd+goAdo5MhR3n92gQQNJ0pgxY9SuXbugOinBydHGAOySv0DlkCFDNHHiRA0aNEhJSUmaPn26PvzwQ7Vt21Yul0vnnnuu7rjjDk2dOpWOgmOQP2vInzXkzxryZx05tI6iUemx/Vnj9Xrl8/nUrFkz7dixQytXrlS7du20detW/fOf/9TkyZPVsmVLvfvuuzpw4ICqV69ud8h+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\n" + "image/png": "iVBORw0KGgoAAAANSUhEUgAABj0AAAO5CAYAAABCHuf5AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3hUZf7+8XtmMum9AQkldKQIqKBiA0UUXBt2Xde2xVUXd92frm7Vbbb9WlZddW3YexcsuKIgHST0DiGQBNJ7m8zM749khoTUySQ5U96v6+Ji5syZcz55zswkc+7zPI/J6XQ6BQAAAAAAAAAA4OfMRhcAAAAAAAAAAADQEwg9AAAAAAAAAABAQCD0AAAAAAAAAAAAAYHQAwAAAAAAAAAABARCDwAAAAAAAAAAEBAIPQAAAAAAAAAAQEAg9AAAAAAAAAAAAAGB0AMAAAAAAAAAAAQEQg8AAAAAAAAAABAQCD0AAACAXmIymXTbbbd1ut78+fNlMpmUlZXV+0UBAAAAQAAj9AAAAAA8tGnTJl166aUaMmSIwsPDlZ6errPPPltPPPGE0aVJkoqKivTwww/r9NNPV0pKiuLj43XSSSfp7bffbrXut99+K5PJ1Oa/lStXutfLyspqdz2TyaSf/exn7nUrKyv1l7/8Reeee64SExNlMpk0f/78Nmt97rnndMYZZ6hfv34KCwvT0KFDdcMNN7QbAB0+fFi/+MUvlJ6ervDwcGVkZOimm25qtV5OTo4uv/xyxcfHKzY2VhdeeKH27t3b5jZfeOEFHXPMMQoPD9fIkSPbPY6ebBMAAACAMUKMLgAAAADwJ8uXL9eMGTM0ePBg/exnP1P//v114MABrVy5Uo8//rh+9atfebzNa6+9VldeeaXCwsJ6pMYVK1boD3/4g+bMmaM//vGPCgkJ0fvvv68rr7xSW7du1X333dfqOfPmzdOUKVNaLBsxYoT7dkpKil599dVWz/viiy/0+uuva9asWe5lhYWF+utf/6rBgwdr4sSJ+vbbb9utdf369Ro6dKguuOACJSQkaN++fXruuef02WefacOGDUpLS3Ove+DAAZ1yyimSpJtvvlnp6enKzc3V6tWrW2yzsrJSM2bMUFlZmX7/+9/LarXq0Ucf1RlnnKHMzEwlJSW513322Wd1880365JLLtEdd9yhpUuXat68eaqurtbvfve7bm0TAAAAgHFMTqfTaXQRAAAAgL8477zztGbNGu3cuVPx8fEtHsvPz1dqaqr7vslk0q233qonn3yyT2vct2+fzGazhgwZ4l7mdDo1c+ZMLVu2TEVFRYqKipLU2NNjxowZevfdd3XppZd6vK+ZM2dqzZo1Onz4sMLDwyVJdXV1KikpUf/+/bV27VpNmTJFL730kq6//voubXPdunU64YQTdP/99+vuu+92L58zZ462b9+uNWvWdBgyPPTQQ/rd736n1atXu4Oc7du3a/z48brrrrv0z3/+U5JUU1OjQYMG6aSTTtJnn33mfv6Pf/xjffTRRzpw4IASEhI82iYAAAAAYzG8FQAAAOCBPXv2aNy4ca0CD0ktAo/mPvroI40fP15hYWEaN26cvvjiixaPtzWnR0ZGhn70ox/pq6++0qRJkxQeHq6xY8fqgw8+6LTGoUOHtgg8pMYA5qKLLlJdXV27QzJVVFSooaGh0+275OXlafHixZo7d6478JCksLAw9e/fv8vbOVpGRoYkqbS01L1s+/bt+vzzz3XnnXcqKSlJtbW1stlsbT7/vffe05QpU1r0XBkzZozOOussvfPOO+5lixcvVlFRkW655ZYWz7/11ltVVVWlBQsWeLxNAAAAAMYi9AAAAAA8MGTIEK1bt06bN2/u0vrff/+9brnlFl155ZV66KGHVFtbq0suuURFRUWdPnfXrl264oorNHv2bN1///0KCQnRZZddpkWLFnWr9kOHDkmSkpOTWz12ww03KDY2VuHh4ZoxY4bWrl3b6fbeeustORwOXXPNNd2qp7mioiLl5+dr7dq1uuGGGyRJZ511lvvxr7/+WpLUr18/nXXWWYqIiFBERIRmz57dIixyOBzauHGjTjjhhFb7mDp1qvbs2aOKigpJjUNrSWq17vHHHy+z2ex+3JNtAgAAADAWc3oAAAAAHvh//+//afbs2Zo0aZKmTp2q0047TWeddZZmzJghq9Xaav1t27Zp69atGj58uCRpxowZmjhxot58803ddtttHe5r586dev/99zV37lxJ0k033aQxY8bod7/7nc4++2yP6i4uLtbzzz+v0047TQMGDHAvDw0N1SWXXKI5c+YoOTlZW7du1b/+9S+ddtppWr58uSZPntzuNl9//XUNGDBAZ555pke1tCU9PV11dXWSpKSkJP373/9u8TPu2rVLkvTzn/9cU6ZM0dtvv63s7Gzdd999mjlzpjZu3KjIyEgVFxerrq6uxc/o4lqWm5ur0aNHKy8vTxaLpVUPndDQUCUlJSk3N1eSPNomAAAAAGMRegAAAAAeOPvss7VixQrdf//9+vLLL7VixQo99NBDSklJ0fPPP68LLrigxfozZ850Bx6SdOyxxyo2NrbdIaaaS0tL08UXX+y+Hxsbq5/85Cd68MEHdejQoS4PIeXqjVFaWqonnniixWPTpk3TtGnT3PcvuOACXXrppTr22GN1zz33tBqKy2Xnzp1at26dfvOb38hs9r4D+eeff67a2lpt27ZNr732mqqqqlo8XllZKUnq37+/FixY4N7nwIEDddVVV+mNN97QT3/6U9XU1EhSm5PCu4bgcq1TU1Oj0NDQNusJDw9vsV5XtwkAAADAWAxvBQAAAHhoypQp+uCDD1RSUqLVq1frnnvuUUVFhS699FJt3bq1xbqDBw9u9fyEhASVlJR0up8RI0bIZDK1WDZq1ChJajGkU2d+9atf6YsvvtDzzz+viRMndmm/F154oRYvXiy73d7mOq+//rok9cjQVlJjD5jZs2frjjvu0Lvvvqv77ruvxQTwERERkqTLL7+8Rchy2WWXKSQkRMuXL2+xnqvXSHO1tbUt1omIiFB9fX2b9dTW1rZYr6vbBAAAAGAsQg8AAACgm0JDQzVlyhT985//1NNPPy2bzaZ33323xToWi6XN5zqdzr4oUffdd5/+85//6IEHHtC1117b5ecNGjRI9fX1rXpcuLzxxhsaPXq0jj/++J4q1W348OGaPHmyO1iRGnu9SI1zejRnsViUlJTkDpESExMVFhamvLy8Vtt1LXNta8CAAbLb7crPz2+xXn19vYqKitzrebJNAAAAAMYi9AAAAAB6gGuS67ZOjHfX7t27W4UjO3fulCRlZGR0+vynnnpK9957r37961/rd7/7nUf73rt3r8LDwxUdHd3qsVWrVmn37t091sujLTU1NSorK3Pfd4UrOTk5Ldarr69XYWGhUlJSJElms1kTJkxocyL2VatWadiwYYqJiZEkTZo0SZJarbt27Vo5HA73455sEwAAAICxCD0AAAAADyxevLjNXhoLFy6UpB6dzDo3N1cffvih+355ebleeeUVTZo0qdP5PN5++23NmzdP11xzjR555JF21ysoKGi1bMOGDfrkk080a9asNufreOONNyRJV199dVd/lDY1NDS0OczX6tWrtWnTJneQJEnTp09XamqqXn/9dfeQUpI0f/582e32FpOeX3rppVqzZk2LkGLHjh365ptvdNlll7mXnXnmmUpMTNTTTz/dYv9PP/20IiMjdd5553m8TQAAAADGMjn7ql89AAAAEADGjx+v6upqXXzxxRozZozq6+u1fPlyvf322xo0aJDWr1+v+Ph4SZLJZNKtt97aYm4KqbGXxvTp0zV//nxJjSfub7jhBu3bt8/dgyMjI0NhYWHKz8/XzTffrH79+unFF1/Uli1btHDhQp1zzjnt1rh69WqddtppiouL04MPPiir1dri8WnTpmnYsGGSGk/8R0REaNq0aUpNTdXWrVv13//+V1arVStWrNAxxxzT4rl2u13p6ekaOnSoVqxY0W4NTz75pEpLS5Wbm6unn35ac+fO1eTJkyU1zjESFxen0tJSDRw4UFdccYXGjRunqKgobdq0SS+99JLCw8O1cuVKjRw50r3NV155Rdddd52mTJmia6+9VtnZ2Xr88cd10kknafHixe6hxCoqKjR58mRVVFTo//2//yer1apHHnlEdrtdmZmZ7l4hkvSf//xHt956qy699FKdc845Wrp0qV555RX94x//0O9//3v3ep5sEwAAAIBxQowuAAAAAPAn//rXv/Tuu+9q4cKF+u9//6v6+noNHjxYt9xyi/74xz+6A4+eMHLkSD3xxBO68847tWPHDg0dOlRvv/12h4GHJG3dulX19fUqKCjQjTfe2Orxl156yR16XHTRRXr99df1yCOPqLy8XCkpKZo7d67+8pe/aMSIEa2e+/XXX+vw4cP6wx/+0GEN//rXv7R//373/Q8++EAffPCBJOnHP/6x4uLiFBkZqZ/+9KdavHix3nvvPdXU1CgtLU1XXXWV/vjHP7YawusnP/mJQkND9cADD+jOO+9UfHy8fvGLX+if//xni7lTYmJi9O233+o3v/mN/v73v8vhcGj69Ol69NFHW4UTt9xyi6xWq/7v//5Pn3zyiQYNGqRHH31Ut99+e4v1PNkmAAAAAOPQ0wMAAADwQRkZGRo/frw+++wzo0sBAAAAAL/BnB4AAAAAAAAAACAgEHoAAAAAAAAAAICAQOgBAAAAAAAAAAACAnN6AAAAAAAAAACAgEBPDwAAAAAAAAAAEBAIPQAAAAAAAAAAQEAg9AAAAAAAAAAAAAGB0AMAAAAAAAAAAAQEQg8AAAAAAAAAABAQCD0AAAAAAAAAAEBAIPQAAAAAAAAAAAABgdADAAAAAAAAAAAEBEIPAAAAAAAAAAAQEAg9AAAAAAAAAABAQCD0AAAAAAAAAAAAAYHQAwAAAAAAAAAABARCDwAAAAAAAAAAEBAIPQAAAAAAAAAAQEAg9AAAAAAAAAAAAAGB0AMAAAAAAAAAAAQEQg8AAAAAAAAAABAQCD0AAAAAAAAAAEBAIPQAAAAAAAAAAAABgdADAAAAAAAAAAAEBEIPAAAAAAAAAAAQEAg9AAAAAAAAAABAQCD0AAAAAAAAAAAAAYHQAwAAAAAAAAAABARCDwAAAAAAAAAAEBAIPQAAAAAAAAAAQEAg9AAAAAAAAAAAAAGB0AMAAAAAAAAAAAQEQg8AAAAAAAAAABAQCD0AAAAAAAAAAEBAIPQAAAAAAAAAAAABgdADAAAAAAAAAAAEBEIPAAAAAAAAAAAQEAg9AAAAAAAAAABAQCD0AAAAAAAAAAAAAYHQAwAAAAAAAAAABARCDwAAAAAAAAAAEBAIPQAAAAAAAAAAQEAg9AAAAAAAAAAAAAGB0AMAAAAAAAAAAAQEQg8AAAAAAAAAABAQCD0AAAAAAAAAAEBAIPQAAAAAAAAAAAABgdADAAAAAAAAAAAEBEIPAAAAAAAAAAAQEAg9AAAAAAAAAABAQCD0AAAAAAAAAAAAAYHQAwAAAAAAAAAABARCDwAAAAAAAAAAEBAIPQAAAAAAAAAAQEAg9AAAAAAAAAAAAAGB0AMAAAAAAAAAAAQEQg8AAAAAAAAAABAQCD0AAAAAAAAAAEBAIPQAAAAAAAAAAAABgdADAAAAAAAAAAAEBEIPAAAAAAAAAAAQEAg9AAAAAAAAAABAQCD0AAAAAAAAAAAAAYHQAwAAAAAAAAAABARCDwAAAAAAAAAAEBAIPQAAAAAAAAAAQEAg9AAAAAAAAAAAAAGB0AMAAAAAAAAAAAQEQg8AAAAAAAAAABAQCD0AAAAAAAAAAEBAIPQAAAAAAAAAAAABgdADAAAAAAAAAAAEBEIPAAAAAAAAAAAQEAg9AAAAAAAAAABAQCD0AAAAAAAAAAAAAYHQAwAAAAAAAAAABARCDwAAAAAAAAAAEBAIPQAAAAAAAAAAQEAg9AAAAAAAAAAA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pS9vP0XJ0WG0n5doP+8Y3X6e7LdXQg9X4LF//35988039NgAAMAPmUwmjUiN1ojUaF01tXGM+byyGq3eV9wUhJRox+EK7S2o0t6CKr215oAkqX9suKYMTdTUjARNGZqoUakxLa54BIBglp+fr8rKSj3wwAP6+9//rgcffFBffPGF5s6dq8WLF+uMM85o83m+OCwwQ+d5h/bzjq+0X26VdLjiyKmVwsp6LVy40H1/waaeP+2SEe1ssY/uONJ+R+qrr632ervBxFdeg/6K9uvc7nKprKbxPfr6p99oZNyR/li0n3doP+8Y1X6eDAnc4799XYHHrl27tHjxYiUlJfX0LgAAgEEGxEXowknpunBSuiSptLpea7NKtCarsSfIpoNlOlReq0835OrTDbmSpLgIq04elqQzRqfo9FEpSo+PMPJHAABDOZqGuLnwwgv1m9/8RpI0adIkLV++XM8880y7oYcvDQvM0Hneof2842vtt+NQhR7cuKLFsjlz5rhv377Cs94cu/42S798fb2+3l7gvj/yTy23cfVpYzRn2pBu1Xt0+zWvLzIySnPmnNqt7QYTX3sN+hvar+tW7SvWE1vWSpKmnjhVJw9Lov28RPt5x+j282RIYI9Dj8rKSu3evdt9f9++fcrMzFRiYqIGDBigSy+9VD/88IM+++wz2e12HTrU2I0zMTFRoaGhnu4OAAD4sPjIUM0c208zx/aTJNXU27X+QIk7CFm3v0RlNTZ9seWQvtjS+DfB8JQonT4qRWeMStGJQ5MUEcr4tACCR3JyskJCQjR27NgWy4855hh9//337T7PF4cFZug879B+3vGV9guxtj6t4k1dVqtVFou5w22ZzWavf/a22s/uZLhFT/jKa9Bf0X6ds1hCWtxu3l60n3doP+8Y1X6e7NPj0GPt2rWaMWOG+77raqPrrrtO9957rz755BNJjVcrNbd48WJNnz7d090BAAA/EhFq0bThyZo2PFlS48Sem3LKtHRXob7bWaD12SXaU1ClPQVVemlZlkJDzDpxaKJOH5miM0anaGRqNPOBAAhooaGhmjJlinbs2NFi+c6dOzVkSPeu3AYQWMwG/S1kdzCVOeBLnDrynnTy9gQ84nHoMX36dDk7eKd19BgAAAguIRazJg9O0OTBCZp31kiV1di0fHehluwq0JKdhcoprdHSXYVauqtQ/1i4Tf1j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" }, "metadata": {}, "output_type": "display_data" @@ -352,7 +392,11 @@ "plt.show()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:55:24.320559400Z", + "start_time": "2023-09-26T08:55:22.912704800Z" + } } }, { @@ -374,7 +418,11 @@ "pymeos_finalize()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-09-26T08:55:25.827246600Z", + "start_time": "2023-09-26T08:55:25.810248300Z" + } } } ], From d0a00dc65fbca5887fdefe56142bc5d02dbc6eff Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Mon, 25 Sep 2023 13:41:10 +0200 Subject: [PATCH 041/101] Improve tests --- pymeos/tests/main/tgeogpoint_test.py | 271 ++++++++++++++++++++++++++- pymeos/tests/main/tgeompoint_test.py | 54 +++++- 2 files changed, 321 insertions(+), 4 deletions(-) diff --git a/pymeos/tests/main/tgeogpoint_test.py b/pymeos/tests/main/tgeogpoint_test.py index 1fa135e1..1e98c00d 100644 --- a/pymeos/tests/main/tgeogpoint_test.py +++ b/pymeos/tests/main/tgeogpoint_test.py @@ -4,7 +4,7 @@ import pytest import numpy as np -from shapely import Point +from shapely import Point, LineString import shapely.geometry from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, \ @@ -1494,6 +1494,275 @@ def test_at_minus(self, temporal, restrictor): assert TGeogPoint.merge(temporal.at(restrictor), temporal.minus(restrictor)) == temporal +class TestTGeogPointEverSpatialOperations(TestTGeogPoint): + tpi = TGeogPointInst('Point(1 1)@2019-09-01') + tpds = TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') + tps = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') + tpss = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, True), + (tpds, True), + (tps, True), + (tpss, True), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_temporal_ever_contained_withindist_intersects(self, temporal, expected): + assert temporal.is_ever_within_distance(Point(1,1), 1) == expected + assert temporal.is_ever_within_distance(TGeogPointInst('Point(1 1)@2019-09-01'), 1) == expected + assert temporal.ever_intersects(Point(1,1)) == expected + assert temporal.ever_intersects(TGeogPointInst('Point(1 1)@2019-09-01')) == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, True), + (tpds, True), + (tps, True), + (tpss, True), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_temporal_ever_disjoint(self, temporal, expected): + assert temporal.is_ever_disjoint(Point(3,3)) == expected + assert temporal.is_ever_disjoint(TGeogPointInst('Point(3 3)@2019-09-01')) == expected + + +class TestTGeogPointTemporalSpatialOperations(TestTGeogPoint): + tpi = TGeogPointInst('Point(1 1)@2019-09-01') + tpds = TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') + tps = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') + tpss = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, TBoolInst('True@2019-09-01')), + (tpds, TBoolSeq('{True@2019-09-01, False@2019-09-02}')), + (tps, TBoolSeqSet('{[True@2019-09-01], (False@2019-09-01, False@2019-09-02]}')), + (tpss, TBoolSeqSet('{[True@2019-09-01], (False@2019-09-01, False@2019-09-02],' + '[True@2019-09-03, True@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_temporal_intersects_disjoint(self, temporal, expected): + assert temporal.intersects(Point(1,1)) == expected + assert temporal.disjoint(Point(1,1)) == ~expected + + # Verify that these results are correct wrt lifting the 9DEM definition of touches + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, TBoolInst('False@2019-09-01')), + (tpds, TBoolSeq('{False@2019-09-01, False@2019-09-02}')), + (tps, TBoolSeqSet('[False@2019-09-01, False@2019-09-02]')), + (tpss, TBoolSeqSet('{[False@2019-09-01, False@2019-09-02],' + '[False@2019-09-03, False@2019-09-05]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_temporal_touches(self, temporal, expected): + assert temporal.touches(Point(1,1)) == expected + + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tpi, Point(1,1), TBoolInst('True@2019-09-01')), + (tpds, Point(1,1), TBoolSeq('{True@2019-09-01, True@2019-09-02}')), + (tps, Point(1,1), TBoolSeqSet('{[True@2019-09-01, True@2019-09-02]}')), + (tpss, Point(1,1), TBoolSeqSet('{[True@2019-09-01, True@2019-09-02],' + '[True@2019-09-03, True@2019-09-05]}')), + + (tpi, TGeogPointInst('Point(1 1)@2019-09-01'), TBoolInst('True@2019-09-01')), + (tpds, TGeogPointSeq('{Point(1 1)@2019-09-01, Point(1 1)@2019-09-02}'), + TBoolSeq('{True@2019-09-01, False@2019-09-02}')), + (tps, TGeogPointSeq('[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02]'), + TBoolSeqSet('{[True@2019-09-01, True@2019-09-02]}')), + (tpss, + TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02],' + '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}'), + TBoolSeqSet('{[True@2019-09-01, True@2019-09-02],' + '[True@2019-09-03, True@2019-09-05]}')), + ], + ids=['Instant Geo', 'Discrete Sequence Geo', 'Sequence Geo', 'SequenceSet Geo', + 'Instant TPoint', 'Discrete Sequence TPoint', 'Sequence TPoint', 'SequenceSet TPoint'] + ) + def test_temporal_withindist(self, temporal, argument, expected): + assert temporal.within_distance(argument, 2) == expected + + +class TestTGeogPointDistanceOperations(TestTGeogPoint): + tpi = TGeogPointInst('Point(1 1)@2019-09-01') + tpds = TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') + tps = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') + tpss = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tpi, Point(1,1), TFloatInst('0@2019-09-01')), + (tpds, Point(1,1), TFloatSeq('{0@2019-09-01, 156876.149@2019-09-02}')), + (tps, Point(1,1), TFloatSeq('[0@2019-09-01, 156876.149@2019-09-02]')), + (tpss, Point(1,1), TFloatSeqSet('{[0@2019-09-01, 156876.149@2019-09-02],' + '[0@2019-09-03, 0@2019-09-05]}')), + + (tpi, STBox('GEODSTBOX X((1,1),(1,1))'), TFloatInst('0@2019-09-01')), + (tpds, STBox('GEODSTBOX X((1,1),(1,1))'), TFloatSeq('{0@2019-09-01, 156876.149@2019-09-02}')), + (tps, STBox('GEODSTBOX X((1,1),(1,1))'), TFloatSeq('[0@2019-09-01, 156876.149@2019-09-02]')), + (tpss, STBox('GEODSTBOX X((1,1),(1,1))'), TFloatSeqSet('{[0@2019-09-01, 156876.149@2019-09-02],' + '[0@2019-09-03, 0@2019-09-05]}')), + + (tpi, TGeogPointInst('Point(1 1)@2019-09-01'), TFloatInst('0@2019-09-01')), + (tpds, TGeogPointSeq('{Point(1 1)@2019-09-01, Point(1 1)@2019-09-02}'), + TFloatSeq('{0@2019-09-01, 156876.149@2019-09-02}')), + (tps, TGeogPointSeq('[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02]'), + TFloatSeq('[0@2019-09-01, 156876.149@2019-09-02]')), + (tpss, + TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02],' + '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}'), + TFloatSeqSet('{[0@2019-09-01, 156876.149@2019-09-02],' + '[0@2019-09-03, 0@2019-09-05]}')), + ], + ids=['Instant Geo', 'Discrete Sequence Geo', 'Sequence Geo', 'SequenceSet Geo', + 'Instant STBox', 'Discrete Sequence STBox', 'Sequence STBox', 'SequenceSet STBox', + 'Instant TPoint', 'Discrete Sequence TPoint', 'Sequence TPoint', 'SequenceSet TPoint'] + ) + def test_distance(self, temporal, argument, expected): + assert temporal.distance(argument).round(3) == expected + assert round(temporal.nearest_approach_distance(argument), 3) == 0.0 + + @pytest.mark.parametrize( + 'temporal, argument', + [ + (tpi, Point(1,1)), + (tpds, Point(1,1)), + (tps, Point(1,1)), + (tpss, Point(1,1)), + + (tpi, TGeogPointInst('Point(1 1)@2019-09-01')), + (tpds, TGeogPointSeq('{Point(1 1)@2019-09-01, Point(1 1)@2019-09-02}')), + (tps, TGeogPointSeq('[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02]')), + (tpss, + TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02],' + '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}')), + ], + ids=['Instant Geo', 'Discrete Sequence Geo', 'Sequence Geo', 'SequenceSet Geo', + 'Instant TPoint', 'Discrete Sequence TPoint', 'Sequence TPoint', 'SequenceSet TPoint'] + ) + def test_nearest_approach_instant(self, temporal, argument): + assert temporal.nearest_approach_instant(argument) == TGeogPointInst('Point(1 1)@2019-09-01') + + @pytest.mark.parametrize( + 'temporal, argument', + [ + (tpi, Point(1,1)), + (tpds, Point(1,1)), + (tps, Point(1,1)), + (tpss, Point(1,1)), + + (tpi, TGeogPointInst('Point(1 1)@2019-09-01')), + (tpds, TGeogPointSeq('{Point(1 1)@2019-09-01, Point(1 1)@2019-09-02}')), + (tps, TGeogPointSeq('[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02]')), + (tpss, + TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(1 1)@2019-09-02],' + '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}')), + ], + ids=['Instant Geo', 'Discrete Sequence Geo', 'Sequence Geo', 'SequenceSet Geo', + 'Instant TPoint', 'Discrete Sequence TPoint', 'Sequence TPoint', 'SequenceSet TPoint'] + ) + def test_shortest_line(self, temporal, argument): + assert temporal.shortest_line(argument) == LineString([(1,1), (1,1)]) + + +class TestTGeogPointSimilarityFunctions(TestTGeogPoint): + tfi = TGeogPointInst('Point(1 1)@2019-09-01') + tfds = TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') + tfs = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') + tfss = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],' + '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tfi, TGeogPointInst('Point(3 3)@2019-09-02'), 313705.45), + (tfds, TGeogPointInst('Point(3 3)@2019-09-03'), 313705.45), + (tfs, TGeogPointInst('Point(3 3)@2019-09-03'), 313705.45), + (tfss, TGeogPointInst('Point(3 3)@2019-09-08'), 313705.45), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] + ) + def test_frechet_distance(self, temporal, argument, expected): + assert round(temporal.frechet_distance(argument), 2) == expected + + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tfi, TGeogPointInst('Point(3 3)@2019-09-02'), 313705.45), + (tfds, TGeogPointInst('Point(3 3)@2019-09-03'), 470534.77), + (tfs, TGeogPointInst('Point(3 3)@2019-09-03'), 470534.77), + (tfss, TGeogPointInst('Point(3 3)@2019-09-08'), 1097945.67), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] + ) + def test_dyntimewarp_distance(self, temporal, argument, expected): + assert round(temporal.dyntimewarp_distance(argument), 2) == expected + + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tfi, TGeogPointInst('Point(3 3)@2019-09-02'), 313705.45), + (tfds, TGeogPointInst('Point(3 3)@2019-09-03'), 313705.45), + (tfs, TGeogPointInst('Point(3 3)@2019-09-03'), 313705.45), + (tfss, TGeogPointInst('Point(3 3)@2019-09-08'), 313705.45), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] + ) + def test_hausdorff_distance(self, temporal, argument, expected): + assert round(temporal.hausdorff_distance(argument), 2) == expected + + +class TestTGeogPointSplitOperations(TestTGeogPoint): + tpi = TGeogPointInst('Point(1 1)@2019-09-01') + tpds = TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') + tps = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') + tpss = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, [TGeogPointInst('Point(1 1)@2019-09-01')]), + (tpds, [TGeogPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}')]), + (tps, [TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]')]), + (tpss, [TGeogPointSeq('[Point(1 1)@2019-09-01,Point(2 2)@2019-09-02]'), + TGeogPointSeq('[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]')]), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_time_split(self, temporal, expected): + assert temporal.time_split(timedelta(days=2), '2019-09-01') == expected + + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tpi, [TGeogPointInst('Point(1 1)@2019-09-01')]), + (tpds, [TGeogPointSeq('{Point(1 1)@2019-09-01}'), + TGeogPointSeq('{Point(2 2)@2019-09-02}')]), + (tps, [TGeogPointSeq('[Point(1 1)@2019-09-01, Point(1.5 1.5)@2019-09-01 12:00:00+00)'), + TGeogPointSeq('[Point(1.5 1.5)@2019-09-01 12:00:00+00, Point(2 2)@2019-09-02]')]), + (tpss, [TGeogPointSeq('[Point(1 1)@2019-09-01,Point(2 2)@2019-09-02]'), + TGeogPointSeq('[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]')]), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_time_split_n(self, temporal, expected): + fragments = temporal.time_split_n(2) + rounded = [frag.round(1) for frag in fragments] + assert rounded == expected + + class TestTGeogPointComparisons(TestTGeogPoint): tp = TGeogPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') other = TGeogPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index 8a06f36e..21b5543c 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -2126,6 +2126,53 @@ def test_shortest_line(self, temporal, argument): assert temporal.shortest_line(argument) == LineString([(1,1), (1,1)]) +class TestTGeomPointSimilarityFunctions(TestTGeomPoint): + tfi = TGeomPointInst('Point(1 1)@2019-09-01') + tfds = TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') + tfs = TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') + tfss = TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],' + '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') + + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tfi, TGeomPointInst('Point(3 3)@2019-09-02'), 2.83), + (tfds, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), + (tfs, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), + (tfss, TGeomPointInst('Point(3 3)@2019-09-08'), 2.83), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] + ) + def test_frechet_distance(self, temporal, argument, expected): + assert round(temporal.frechet_distance(argument), 2) == expected + + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tfi, TGeomPointInst('Point(3 3)@2019-09-02'), 2.83), + (tfds, TGeomPointInst('Point(3 3)@2019-09-03'), 4.24), + (tfs, TGeomPointInst('Point(3 3)@2019-09-03'), 4.24), + (tfss, TGeomPointInst('Point(3 3)@2019-09-08'), 9.9), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] + ) + def test_dyntimewarp_distance(self, temporal, argument, expected): + assert round(temporal.dyntimewarp_distance(argument), 2) == expected + + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tfi, TGeomPointInst('Point(3 3)@2019-09-02'), 2.83), + (tfds, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), + (tfs, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), + (tfss, TGeomPointInst('Point(3 3)@2019-09-08'), 2.83), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] + ) + def test_hausdorff_distance(self, temporal, argument, expected): + assert round(temporal.hausdorff_distance(argument), 2) == expected + + class TestTGeomPointSplitOperations(TestTGeomPoint): tpi = TGeomPointInst('Point(1 1)@2019-09-01') tpds = TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') @@ -2187,9 +2234,10 @@ def test_space_split(self, temporal, expected): TGeomPointSeq('{Point(2 2)@2019-09-02}')]), (tps, [TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02)'), TGeomPointSeq('[Point(2 2)@2019-09-02]')]), - (tpss, [TGeomPointSeqSet('{[POINT(1 1)@2019-09-01, POINT(2 2)@2019-09-02),' - '[POINT(1 1)@2019-09-03, POINT(1 1)@2019-09-05]}'), - TGeomPointSeqSet('{[POINT(2 2)@2019-09-02]}')]), + (tpss, [TGeomPointSeq('{[POINT(1 1)@2019-09-01, POINT(2 2)@2019-09-02)}'), + TGeomPointSeq('{[POINT(2 2)@2019-09-02]}'), + TGeomPointSeq('{[POINT(1 1)@2019-09-03, POINT(1 1)@2019-09-05)}'), + TGeomPointSeq('{[POINT(1 1)@2019-09-05]}')]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) From 18ce66fab6927e83fd84ecf8605a67d8c9fc5696 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Wed, 27 Sep 2023 14:16:28 +0200 Subject: [PATCH 042/101] Add collection classes --- pymeos/pymeos/collections/number/floatset.py | 186 +++++++++++++++++++ pymeos/pymeos/collections/number/intset.py | 185 ++++++++++++++++++ pymeos/pymeos/collections/text/textset.py | 185 ++++++++++++++++++ 3 files changed, 556 insertions(+) create mode 100644 pymeos/pymeos/collections/number/floatset.py create mode 100644 pymeos/pymeos/collections/number/intset.py create mode 100644 pymeos/pymeos/collections/text/textset.py diff --git a/pymeos/pymeos/collections/number/floatset.py b/pymeos/pymeos/collections/number/floatset.py new file mode 100644 index 00000000..76c7ba9a --- /dev/null +++ b/pymeos/pymeos/collections/number/floatset.py @@ -0,0 +1,186 @@ +from __future__ import annotations + +from typing import Optional, overload, Union + +from pymeos_cffi import * + +from .. import Span, SpanSet +from ..base import Set + + +class FloatSet(Set[float]): + """ + Class for representing a set of text values. + + ``FloatSet`` objects can be created with a single argument of type float as + in MobilityDB. + + >>> FloatSet(string='{1.5, 2.5, 3.5, 4.5}') + + Another possibility is to create a ``FloatSet`` object from a list of floats. + + >>> FloatSet(elements=[1.5, 2.5, 3.5, 4.5]) + + + """ + + __slots__ = ['_inner'] + + _parse_function = floatset_in + _parse_value_function = lambda x: x + _make_function = floatset_make + + # ------------------------- Constructors ---------------------------------- + + # ------------------------- Output ---------------------------------------- + + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`float` instance + + MEOS Functions: + floatset_out + """ + return floatset_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + + def to_spanset(self) -> SpanSet: + raise NotImplementedError() + + def to_span(self) -> Span: + raise NotImplementedError() + + # ------------------------- Accessors ------------------------------------- + + def start_element(self): + """ + Returns the first element in ``self``. + + Returns: + A :class:`float` instance + + MEOS Functions: + floatset_start_value + """ + return floatset_start_value(self._inner) + + def end_element(self): + """ + Returns the last element in ``self``. + + Returns: + A :class:`float` instance + + MEOS Functions: + floatset_end_value + """ + return floatset_end_value(self._inner) + + def element_n(self, n: float): + """ + Returns the ``n``-th element in ``self``. + + Args: + n: The 0-based index of the element to return. + + Returns: + A :class:`float` instance + + MEOS Functions: + floatset_value_n + """ + super().element_n(n) + return floatset_value_n(self._inner, n) + + def elements(self): + """ + Returns the elements in ``self``. + + Returns: + A list of :class:`float` instances + + MEOS Functions: + floatset_values + """ + elems = floatset_values(self._inner) + return [elems[i] for i in range(self.num_elements())] + + # ------------------------- Set Operations -------------------------------- + + @overload + def intersection(self, other: float) -> Optional[float]: + ... + + @overload + def intersection(self, other: FloatSet) -> Optional[FloatSet]: + ... + + def intersection(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: A :class:`FloatSet` or :class:`float` instance + + Returns: + An object of the same type as ``other`` or ``None`` if the + intersection is empty. + + MEOS Functions: + intersection_floatset_text, intersection_set_set + """ + if isinstance(other, float): + return intersection_floatset_text(self._inner, other) + elif isinstance(other, FloatSet): + result = super().intersection(other) + return FloatSet(elements=result) if result is not None else None + else: + return super().intersection(other) + + def minus(self, other: Union[FloatSet, float]) -> Optional[FloatSet]: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: A :class:`FloatSet` or :class:`float` instance + + Returns: + A :class:`FloatSet` instance. + + MEOS Functions: + minus_floatset_text, minus_set_set + """ + if isinstance(other, float): + result = minus_floatset_text(self._inner, other) + return FloatSet(elements=result) if result is not None else None + elif isinstance(other, FloatSet): + result = super().minus(other) + return FloatSet(elements=result) if result is not None else None + else: + return super().minus(other) + + def union(self, other: Union[FloatSet, float]) -> FloatSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: A :class:`FloatSet` or :class:`float` instance + + Returns: + A :class:`FloatSet` instance. + + MEOS Functions: + union_floatset_text, union_set_set + """ + if isinstance(other, float): + result = union_floatset_text(self._inner, other) + return FloatSet(elements=result) if result is not None else None + elif isinstance(other, FloatSet): + result = super().union(other) + return FloatSet(elements=result) if result is not None else None + else: + return super().union(other) diff --git a/pymeos/pymeos/collections/number/intset.py b/pymeos/pymeos/collections/number/intset.py new file mode 100644 index 00000000..ebfa6a98 --- /dev/null +++ b/pymeos/pymeos/collections/number/intset.py @@ -0,0 +1,185 @@ +from __future__ import annotations + +from typing import Optional, overload, Union + +from pymeos_cffi import * + +from .. import Span, SpanSet +from ..base import Set + + +class IntSet(Set[int]): + """ + Class for representing a set of text values. + + ``IntSet`` objects can be created with a single argument of type integer as + in MobilityDB. + + >>> IntSet(string='{1, 2, 3, 4}') + + Another possibility is to create a ``IntSet`` object from a list of integers. + + >>> IntSet(elements=[1, 2, 3, 4]) + + + """ + + __slots__ = ['_inner'] + + _parse_function = intset_in + _parse_value_function = lambda x: x + _make_function = intset_make + + # ------------------------- Constructors ---------------------------------- + + # ------------------------- Output ---------------------------------------- + + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`int` instance + + MEOS Functions: + intset_out + """ + return intset_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + + def to_spanset(self) -> SpanSet: + raise NotImplementedError() + + def to_span(self) -> Span: + raise NotImplementedError() + + # ------------------------- Accessors ------------------------------------- + + def start_element(self): + """ + Returns the first element in ``self``. + + Returns: + A :class:`int` instance + + MEOS Functions: + intset_start_value + """ + return intset_start_value(self._inner) + + def end_element(self): + """ + Returns the last element in ``self``. + + Returns: + A :class:`int` instance + + MEOS Functions: + intset_end_value + """ + return intset_end_value(self._inner) + + def element_n(self, n: int): + """ + Returns the ``n``-th element in ``self``. + + Args: + n: The 0-based index of the element to return. + + Returns: + A :class:`int` instance + + MEOS Functions: + intset_value_n + """ + super().element_n(n) + return intset_value_n(self._inner, n) + + def elements(self): + """ + Returns the elements in ``self``. + + Returns: + A list of :class:`int` instances + + MEOS Functions: + intset_values + """ + elems = intset_values(self._inner) + return [elems[i] for i in range(self.num_elements())] + + # ------------------------- Set Operations -------------------------------- + + @overload + def intersection(self, other: int) -> Optional[int]: + ... + + @overload + def intersection(self, other: IntSet) -> Optional[IntSet]: + ... + + def intersection(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: A :class:`IntSet` or :class:`int` instance + + Returns: + An object of the same type as ``other`` or ``None`` if the intersection is empty. + + MEOS Functions: + intersection_intset_text, intersection_set_set + """ + if isinstance(other, int): + return intersection_intset_text(self._inner, other) + elif isinstance(other, IntSet): + result = super().intersection(other) + return IntSet(elements=result) if result is not None else None + else: + return super().intersection(other) + + def minus(self, other: Union[IntSet, int]) -> Optional[IntSet]: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: A :class:`IntSet` or :class:`int` instance + + Returns: + A :class:`IntSet` instance. + + MEOS Functions: + minus_intset_text, minus_set_set + """ + if isinstance(other, int): + result = minus_intset_text(self._inner, other) + return IntSet(elements=result) if result is not None else None + elif isinstance(other, IntSet): + result = super().minus(other) + return IntSet(elements=result) if result is not None else None + else: + return super().minus(other) + + def union(self, other: Union[IntSet, int]) -> IntSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: A :class:`IntSet` or :class:`int` instance + + Returns: + A :class:`IntSet` instance. + + MEOS Functions: + union_intset_text, union_set_set + """ + if isinstance(other, int): + result = union_intset_text(self._inner, other) + return IntSet(elements=result) if result is not None else None + elif isinstance(other, IntSet): + result = super().union(other) + return IntSet(elements=result) if result is not None else None + else: + return super().union(other) diff --git a/pymeos/pymeos/collections/text/textset.py b/pymeos/pymeos/collections/text/textset.py new file mode 100644 index 00000000..232948d0 --- /dev/null +++ b/pymeos/pymeos/collections/text/textset.py @@ -0,0 +1,185 @@ +from __future__ import annotations + +from typing import Optional, overload, Union + +from pymeos_cffi import * + +from .. import Span, SpanSet +from ..base import Set + + +class TextSet(Set[str]): + """ + Class for representing a set of text values. + + ``TextSet`` objects can be created with a single argument of type string as + in MobilityDB. + + >>> TextSet(string='{a, b, c, def}') + + Another possibility is to create a ``TextSet`` object from a list of strings. + + >>> TextSet(elements=['a', 'b', 'c', 'def']) + + + """ + + __slots__ = ['_inner'] + + _parse_function = textset_in + _parse_value_function = lambda x: x + _make_function = textset_make + + # ------------------------- Constructors ---------------------------------- + + # ------------------------- Output ---------------------------------------- + + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + textset_out + """ + return textset_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + + def to_spanset(self) -> SpanSet: + raise NotImplementedError() + + def to_span(self) -> Span: + raise NotImplementedError() + + # ------------------------- Accessors ------------------------------------- + + def start_element(self): + """ + Returns the first element in ``self``. + + Returns: + A :class:`str` instance + + MEOS Functions: + textset_start_value + """ + return textset_start_value(self._inner) + + def end_element(self): + """ + Returns the last element in ``self``. + + Returns: + A :class:`str` instance + + MEOS Functions: + textset_end_value + """ + return textset_end_value(self._inner) + + def element_n(self, n: int): + """ + Returns the ``n``-th element in ``self``. + + Args: + n: The 0-based index of the element to return. + + Returns: + A :class:`str` instance + + MEOS Functions: + textset_value_n + """ + super().element_n(n) + return textset_value_n(self._inner, n) + + def elements(self): + """ + Returns the elements in ``self``. + + Returns: + A list of :class:`str` instances + + MEOS Functions: + textset_values + """ + elems = textset_values(self._inner) + return [elems[i] for i in range(self.num_elements())] + + # ------------------------- Set Operations -------------------------------- + + @overload + def intersection(self, other: str) -> Optional[str]: + ... + + @overload + def intersection(self, other: TextSet) -> Optional[TextSet]: + ... + + def intersection(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: A :class:`TextSet` or :class:`str` instance + + Returns: + An object of the same type as ``other`` or ``None`` if the intersection is empty. + + MEOS Functions: + intersection_textset_text, intersection_set_set + """ + if isinstance(other, str): + return intersection_textset_text(self._inner, other) + elif isinstance(other, TextSet): + result = super().intersection(other) + return TextSet(elements=result) if result is not None else None + else: + return super().intersection(other) + + def minus(self, other: Union[TextSet, str]) -> Optional[TextSet]: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: A :class:`TextSet` or :class:`str` instance + + Returns: + A :class:`TextSet` instance. + + MEOS Functions: + minus_textset_text, minus_set_set + """ + if isinstance(other, str): + result = minus_textset_text(self._inner, other) + return TextSet(elements=result) if result is not None else None + elif isinstance(other, TextSet): + result = super().minus(other) + return TextSet(elements=result) if result is not None else None + else: + return super().minus(other) + + def union(self, other: Union[TextSet, str]) -> TextSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: A :class:`TextSet` or :class:`str` instance + + Returns: + A :class:`TextSet` instance. + + MEOS Functions: + union_textset_text, union_set_set + """ + if isinstance(other, str): + result = union_textset_text(self._inner, other) + return TextSet(elements=result) if result is not None else None + elif isinstance(other, TextSet): + result = super().union(other) + return TextSet(elements=result) if result is not None else None + else: + return super().union(other) From 58a7be57cd5c9370f55cb9e2dbd726ca0b7f9b7a Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Wed, 27 Sep 2023 14:19:37 +0200 Subject: [PATCH 043/101] Improve tests --- pymeos/tests/time/timestampset_test.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pymeos/tests/time/timestampset_test.py b/pymeos/tests/time/timestampset_test.py index cacd4bfe..443efd04 100644 --- a/pymeos/tests/time/timestampset_test.py +++ b/pymeos/tests/time/timestampset_test.py @@ -330,9 +330,3 @@ def test_shift_scale(self): datetime(2020, 1, 5, 1, tzinfo=timezone.utc), datetime(2020, 1, 5, 3, tzinfo=timezone.utc)]) - -class TestTimestampSetMiscFunctions(TestTimestampSet): - timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-02 00:00:00+0, 2020-01-04 00:00:00+0}') - - def test_hash(self): - hash(self.timestampset) From bf9720597d48f89728344efe2c47f28c490d5e8a Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Wed, 27 Sep 2023 17:18:52 +0200 Subject: [PATCH 044/101] Merge with spans branch --- pymeos/tests/collections/time/periodset_test.py | 2 +- pymeos/tests/main/tbool_test.py | 2 +- .../pymeos_cffi/builder/build_pymeos_functions.py | 10 ++++++---- pymeos_cffi/pymeos_cffi/functions.py | 14 ++++++++------ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/pymeos/tests/collections/time/periodset_test.py b/pymeos/tests/collections/time/periodset_test.py index f960c5dd..c4d09008 100644 --- a/pymeos/tests/collections/time/periodset_test.py +++ b/pymeos/tests/collections/time/periodset_test.py @@ -120,7 +120,7 @@ def test_hash(self): assert hash(self.periodset) == 552347465 -class TestPeriodSetTransformationFunctions(TestPeriodSet): +class TestPeriodSetTransformations(TestPeriodSet): @pytest.mark.parametrize( 'delta,result', diff --git a/pymeos/tests/main/tbool_test.py b/pymeos/tests/main/tbool_test.py index 90bcbba6..d1418127 100644 --- a/pymeos/tests/main/tbool_test.py +++ b/pymeos/tests/main/tbool_test.py @@ -730,7 +730,7 @@ def test_sequenceset_sequence_functions(self): TBoolSeq('[True@2019-09-06]')] -class TestTBoolTransformationSet(TestTBool): +class TestTBoolTransformations(TestTBool): tbi = TBoolInst('True@2019-09-01') tbds = TBoolSeq('{True@2019-09-01, False@2019-09-02}') tbs = TBoolSeq('[True@2019-09-01, False@2019-09-02]') diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index 15f07c14..19744cd7 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -114,8 +114,8 @@ def __init__(self, ctype: str, ptype: str, conversion: Optional[str]) -> None: ('tpoint_space_time_split', 'count'), ('tbox_as_hexwkb', 'size'), ('stbox_as_hexwkb', 'size'), - ('tbox_tile_list', 'rows'), - ('tbox_tile_list', 'columns'), + ('tintbox_tile_list', 'count'), + ('tfloatbox_tile_list', 'count'), ('stbox_tile_list', 'cellcount'), } @@ -166,8 +166,10 @@ def __init__(self, ctype: str, ptype: str, conversion: Optional[str]) -> None: ('period_tcount_transfn', 'state'), ('periodset_tcount_transfn', 'state'), ('stbox_tile_list', 'duration'), - ('tbox_tile_list', 'xorigin'), - ('tbox_tile_list', 'torigin'), + ('tintbox_tile_list', 'xorigin'), + ('tintbox_tile_list', 'torigin'), + ('tfloatbox_tile_list', 'xorigin'), + ('tfloatbox_tile_list', 'torigin'), } diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index c9bf02a6..9a7396c7 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -7586,22 +7586,24 @@ def stbox_tile_list(bounds: 'const STBox *', xsize: float, ysize: float, zsize: return result if result != _ffi.NULL else None, count[0] -def tintbox_tile_list(box: 'const TBox *', xsize: int, duration: 'const Interval *', xorigin: int, torigin: int) -> "Tuple['TBox *', 'int']": +def tintbox_tile_list(box: 'const TBox *', xsize: int, duration: 'const Interval *', xorigin: 'Optional[int]', torigin: "Optional[int]") -> "Tuple['TBox *', 'int']": box_converted = _ffi.cast('const TBox *', box) duration_converted = _ffi.cast('const Interval *', duration) - torigin_converted = _ffi.cast('TimestampTz', torigin) + xorigin_converted = xorigin if xorigin is not None else _ffi.NULL + torigin_converted = _ffi.cast('TimestampTz', torigin) if torigin is not None else _ffi.NULL count = _ffi.new('int *') - result = _lib.tintbox_tile_list(box_converted, xsize, duration_converted, xorigin, torigin_converted, count) + result = _lib.tintbox_tile_list(box_converted, xsize, duration_converted, xorigin_converted, torigin_converted, count) _check_error() return result if result != _ffi.NULL else None, count[0] -def tfloatbox_tile_list(box: 'const TBox *', xsize: float, duration: 'const Interval *', xorigin: float, torigin: int) -> "Tuple['TBox *', 'int']": +def tfloatbox_tile_list(box: 'const TBox *', xsize: float, duration: 'const Interval *', xorigin: 'Optional[float]', torigin: "Optional[int]") -> "Tuple['TBox *', 'int']": box_converted = _ffi.cast('const TBox *', box) duration_converted = _ffi.cast('const Interval *', duration) - torigin_converted = _ffi.cast('TimestampTz', torigin) + xorigin_converted = xorigin if xorigin is not None else _ffi.NULL + torigin_converted = _ffi.cast('TimestampTz', torigin) if torigin is not None else _ffi.NULL count = _ffi.new('int *') - result = _lib.tfloatbox_tile_list(box_converted, xsize, duration_converted, xorigin, torigin_converted, count) + result = _lib.tfloatbox_tile_list(box_converted, xsize, duration_converted, xorigin_converted, torigin_converted, count) _check_error() return result if result != _ffi.NULL else None, count[0] From 7334851cec927421ea672c4a1b3a1e482a25f675 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Wed, 27 Sep 2023 17:55:45 +0200 Subject: [PATCH 045/101] Improve tests --- pymeos/pymeos/__init__.py | 4 +++- pymeos/pymeos/collections/__init__.py | 3 +++ pymeos/pymeos/collections/base/__init__.py | 2 +- pymeos/pymeos/collections/number/__init__.py | 4 ++++ pymeos/pymeos/collections/number/floatset.py | 3 +-- pymeos/pymeos/collections/number/intset.py | 3 +-- pymeos/pymeos/collections/text/__init__.py | 3 +++ pymeos/pymeos/collections/text/textset.py | 3 +-- 8 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pymeos/pymeos/__init__.py b/pymeos/pymeos/__init__.py index 0fd9c980..9f9b523b 100644 --- a/pymeos/pymeos/__init__.py +++ b/pymeos/pymeos/__init__.py @@ -3,7 +3,7 @@ from .main import * from .meos_init import * from .temporal import * -from .collections import Period, PeriodSet, TimestampSet, Time +from .collections import IntSet, FloatSet, TextSet, Period, PeriodSet, TimestampSet, Time __version__ = '1.1.3a1' __all__ = [ @@ -21,6 +21,8 @@ 'TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', 'TGeogPointSeqSet', # temporal 'Temporal', 'TInstant', 'TSequence', 'TSequenceSet', + # collections + 'IntSet', 'FloatSet', 'TextSet', # time 'Time', 'Period', 'TimestampSet', 'PeriodSet', # extras diff --git a/pymeos/pymeos/collections/__init__.py b/pymeos/pymeos/collections/__init__.py index 45d99bcf..99a57c2e 100644 --- a/pymeos/pymeos/collections/__init__.py +++ b/pymeos/pymeos/collections/__init__.py @@ -1,7 +1,10 @@ from .base import * +from .number import * +from .text import * from .time import * __all__ = [ 'Set', 'Span', 'SpanSet', + 'IntSet', 'FloatSet', 'TextSet', 'Time', 'TimestampSet', 'Period', 'PeriodSet', 'datetime', 'timedelta' ] diff --git a/pymeos/pymeos/collections/base/__init__.py b/pymeos/pymeos/collections/base/__init__.py index 29db124f..07785cc5 100644 --- a/pymeos/pymeos/collections/base/__init__.py +++ b/pymeos/pymeos/collections/base/__init__.py @@ -2,4 +2,4 @@ from .span import Span from .spanset import SpanSet -__all__ = ["Set", "Span", "SpanSet"] +__all__ = ['Set', 'Span', 'SpanSet'] diff --git a/pymeos/pymeos/collections/number/__init__.py b/pymeos/pymeos/collections/number/__init__.py index e69de29b..3e104e08 100644 --- a/pymeos/pymeos/collections/number/__init__.py +++ b/pymeos/pymeos/collections/number/__init__.py @@ -0,0 +1,4 @@ +from .intset import IntSet +from .floatset import FloatSet + +__all__ = ['IntSet', 'FloatSet'] diff --git a/pymeos/pymeos/collections/number/floatset.py b/pymeos/pymeos/collections/number/floatset.py index 76c7ba9a..93e14b62 100644 --- a/pymeos/pymeos/collections/number/floatset.py +++ b/pymeos/pymeos/collections/number/floatset.py @@ -4,8 +4,7 @@ from pymeos_cffi import * -from .. import Span, SpanSet -from ..base import Set +from .. import Set, Span, SpanSet class FloatSet(Set[float]): diff --git a/pymeos/pymeos/collections/number/intset.py b/pymeos/pymeos/collections/number/intset.py index ebfa6a98..76d5ba31 100644 --- a/pymeos/pymeos/collections/number/intset.py +++ b/pymeos/pymeos/collections/number/intset.py @@ -4,8 +4,7 @@ from pymeos_cffi import * -from .. import Span, SpanSet -from ..base import Set +from .. import Set, Span, SpanSet class IntSet(Set[int]): diff --git a/pymeos/pymeos/collections/text/__init__.py b/pymeos/pymeos/collections/text/__init__.py index e69de29b..fd4d26a5 100644 --- a/pymeos/pymeos/collections/text/__init__.py +++ b/pymeos/pymeos/collections/text/__init__.py @@ -0,0 +1,3 @@ +from .textset import TextSet + +__all__ = ['TextSet'] diff --git a/pymeos/pymeos/collections/text/textset.py b/pymeos/pymeos/collections/text/textset.py index 232948d0..892404a6 100644 --- a/pymeos/pymeos/collections/text/textset.py +++ b/pymeos/pymeos/collections/text/textset.py @@ -4,8 +4,7 @@ from pymeos_cffi import * -from .. import Span, SpanSet -from ..base import Set +from .. import Set, Span, SpanSet class TextSet(Set[str]): From 9ac7045fbcef8f41153b7b804a05c51d1c80a3b9 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Wed, 27 Sep 2023 18:53:02 +0200 Subject: [PATCH 046/101] Add all sets and spans (missing spansets, only dummy to avoid error). Add test for textset. --- pymeos/pymeos/__init__.py | 5 +- pymeos/pymeos/boxes/tbox.py | 7 +- pymeos/pymeos/collections/__init__.py | 6 +- pymeos/pymeos/collections/base/collection.py | 14 + pymeos/pymeos/collections/base/set.py | 137 +++--- pymeos/pymeos/collections/base/span.py | 58 +-- pymeos/pymeos/collections/geo/__init__.py | 3 + pymeos/pymeos/collections/geo/geoset.py | 287 +++++++++++ pymeos/pymeos/collections/number/floatset.py | 306 ++++++++++++ pymeos/pymeos/collections/number/floatspan.py | 462 ++++++++++++++++++ .../pymeos/collections/number/floatspanset.py | 5 + pymeos/pymeos/collections/number/intset.py | 306 ++++++++++++ pymeos/pymeos/collections/number/intspan.py | 462 ++++++++++++++++++ .../pymeos/collections/number/intspanset.py | 5 + pymeos/pymeos/collections/text/__init__.py | 3 + pymeos/pymeos/collections/text/textset.py | 70 ++- pymeos/pymeos/collections/time/period.py | 34 +- .../collections/time/time_collection.py | 5 +- .../pymeos/collections/time/timestampset.py | 28 +- pymeos/pymeos/main/tbool.py | 31 +- pymeos/pymeos/main/tfloat.py | 60 +-- pymeos/pymeos/main/tint.py | 34 +- pymeos/pymeos/main/tpoint.py | 102 ++-- pymeos/pymeos/main/ttext.py | 36 +- pymeos/tests/collections/text/__init__.py | 0 pymeos/tests/collections/text/textset_test.py | 273 +++++++++++ pymeos_cffi/docker/MobilityDB.Dockerfile | 10 + pymeos_examples/PyMEOS Examples/AIS.ipynb | 52 +- 28 files changed, 2461 insertions(+), 340 deletions(-) create mode 100644 pymeos/pymeos/collections/geo/geoset.py create mode 100644 pymeos/pymeos/collections/number/floatset.py create mode 100644 pymeos/pymeos/collections/number/floatspan.py create mode 100644 pymeos/pymeos/collections/number/floatspanset.py create mode 100644 pymeos/pymeos/collections/number/intset.py create mode 100644 pymeos/pymeos/collections/number/intspan.py create mode 100644 pymeos/pymeos/collections/number/intspanset.py create mode 100644 pymeos/tests/collections/text/__init__.py create mode 100644 pymeos/tests/collections/text/textset_test.py create mode 100644 pymeos_cffi/docker/MobilityDB.Dockerfile diff --git a/pymeos/pymeos/__init__.py b/pymeos/pymeos/__init__.py index 0fd9c980..0d0f9ec5 100644 --- a/pymeos/pymeos/__init__.py +++ b/pymeos/pymeos/__init__.py @@ -3,7 +3,7 @@ from .main import * from .meos_init import * from .temporal import * -from .collections import Period, PeriodSet, TimestampSet, Time +from .collections import Period, PeriodSet, TimestampSet, Time, TextSet __version__ = '1.1.3a1' __all__ = [ @@ -21,8 +21,9 @@ 'TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', 'TGeogPointSeqSet', # temporal 'Temporal', 'TInstant', 'TSequence', 'TSequenceSet', - # time + # Collections 'Time', 'Period', 'TimestampSet', 'PeriodSet', + 'TextSet', # extras 'TInterpolation', # aggregators diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index 4a6cffa0..adefb937 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -292,17 +292,18 @@ def as_hexwkb(self) -> str: return tbox_as_hexwkb(self._inner, -1)[0] # ------------------------- Conversions ---------------------------------- - def to_floatrange(self) -> floatrange: + def to_span(self) -> Span: """ Returns the numeric span of ``self``. Returns: - A new :class:`~spans.floatrange` instance + A new :class:`FloatSpan` instance MEOS Functions: tbox_to_floatspan """ - return floatspan_to_floatrange(tbox_to_floatspan(self._inner)) + from ..collections import FloatSpan + return FloatSpan(_inner=tbox_to_floatspan(self._inner)) def to_period(self) -> Period: """ diff --git a/pymeos/pymeos/collections/__init__.py b/pymeos/pymeos/collections/__init__.py index 45d99bcf..b4be70f5 100644 --- a/pymeos/pymeos/collections/__init__.py +++ b/pymeos/pymeos/collections/__init__.py @@ -1,7 +1,11 @@ from .base import * from .time import * +from .text import * +from .geo import * __all__ = [ 'Set', 'Span', 'SpanSet', - 'Time', 'TimestampSet', 'Period', 'PeriodSet', 'datetime', 'timedelta' + 'Time', 'TimestampSet', 'Period', 'PeriodSet', 'datetime', 'timedelta', + 'TextSet', + 'GeometrySet', 'GeographySet' ] diff --git a/pymeos/pymeos/collections/base/collection.py b/pymeos/pymeos/collections/base/collection.py index e078fa8c..74c25957 100644 --- a/pymeos/pymeos/collections/base/collection.py +++ b/pymeos/pymeos/collections/base/collection.py @@ -57,3 +57,17 @@ def is_over_or_right(self, other) -> bool: @abstractmethod def is_right(self, other) -> bool: raise NotImplementedError() + + # ------------------------- Database Operations --------------------------- + + # ------------------------- Database Operations --------------------------- + @classmethod + def read_from_cursor(cls, value, _=None): + """ + Reads a :class:`STBox` from a database cursor. Used when automatically + loading objects from the database. + Users should use the class constructor instead. + """ + if not value: + return None + return cls(string=value) diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py index edfdf347..94fd405e 100644 --- a/pymeos/pymeos/collections/base/set.py +++ b/pymeos/pymeos/collections/base/set.py @@ -46,7 +46,7 @@ def __copy__(self: Self) -> Self: Return a copy of ``self``. Returns: - A new :class:`Period` instance + A new :class:`Span` instance MEOS Functions: set_copy @@ -137,7 +137,7 @@ def to_spanset(self) -> SpanSet: Returns a SpanSet that contains a Span for each element in ``self``. Returns: - A new :class:`PeriodSet` instance + A new :class:`SpanSet` instance MEOS Functions: set_to_spanset @@ -147,10 +147,10 @@ def to_spanset(self) -> SpanSet: @abstractmethod def to_span(self) -> Span: """ - Returns a period that encompasses ``self``. + Returns a span that encompasses ``self``. Returns: - A new :class:`Period` instance + A new :class:`Span` instance MEOS Functions: set_span @@ -236,7 +236,7 @@ def is_adjacent(self, other) -> bool: Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them contains it. Args: - other: temporal object to compare with + other: object to compare with Returns: True if adjacent, False otherwise @@ -258,7 +258,7 @@ def is_contained_in(self, container) -> bool: Returns whether ``self`` is contained in ``container``. Args: - container: temporal object to compare with + container: object to compare with Returns: True if contained, False otherwise @@ -277,12 +277,13 @@ def is_contained_in(self, container) -> bool: else: raise TypeError(f'Operation not supported with type {container.__class__}') + @abstractmethod def contains(self, content) -> bool: """ Returns whether ``self`` contains ``content``. Args: - content: temporal object to compare with + content: object to compare with Returns: True if contains, False otherwise @@ -300,7 +301,7 @@ def __contains__(self, item): Returns whether ``self`` contains ``content``. Args: - item: temporal object to compare with + item: object to compare with Returns: True if contains, False otherwise @@ -315,7 +316,7 @@ def overlaps(self, other) -> bool: Returns whether ``self`` overlaps ``other``. That is, both share at least an instant Args: - other: temporal object to compare with + other: object to compare with Returns: True if overlaps, False otherwise @@ -380,7 +381,7 @@ def is_over_or_left(self, other) -> bool: at the same value). Args: - other: temporal object to compare with + other: object to compare with Returns: True if before, False otherwise @@ -405,7 +406,7 @@ def is_over_or_right(self, other) -> bool: starts (or at the same value). Args: - other: temporal object to compare with + other: object to compare with Returns: True if overlapping or to the right, False otherwise @@ -430,7 +431,7 @@ def is_right(self, other) -> bool: is to the right ``other``. Args: - other: temporal object to compare with + other: object to compare with Returns: True if right, False otherwise @@ -478,10 +479,10 @@ def distance(self, other) -> float: @abstractmethod def intersection(self, other): """ - Returns the temporal intersection of ``self`` and ``other``. + Returns the intersection of ``self`` and ``other``. Args: - other: temporal object to intersect with + other: object to intersect with Returns: A :class:`Collection` instance. The actual class depends on ``other``. @@ -489,109 +490,95 @@ def intersection(self, other): MEOS Functions: intersection_set_set, intersection_spanset_span, intersection_spanset_spanset """ - from .span import Span - from .spanset import SpanSet - if isinstance(other, Set): - return intersection_set_set(self._inner, other._inner) - elif isinstance(other, Span): - return intersection_spanset_span(set_to_spanset(self._inner), other._inner) - elif isinstance(other, SpanSet): - return intersection_spanset_spanset(set_to_spanset(self._inner), other._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') + raise TypeError(f'Operation not supported with type {other.__class__}') def __mul__(self, other): """ - Returns the temporal intersection of ``self`` and ``other``. + Returns the intersection of ``self`` and ``other``. Args: - other: temporal object to intersect with + other: object to intersect with Returns: A :class:`Collection` instance. The actual class depends on ``other``. - - MEOS Functions: - intersection_set_set, intersection_spanset_span, intersection_spanset_spanset """ return self.intersection(other) @abstractmethod def minus(self, other): """ - Returns the temporal difference of ``self`` and ``other``. + Returns the difference of ``self`` and ``other``. Args: - other: temporal object to diff with + other: object to diff with Returns: A :class:`Collection` instance. The actual class depends on ``other``. - - MEOS Functions: - minus_set_set, minus_spanset_span, minus_spanset_spanset """ - from .span import Span - from .spanset import SpanSet - if isinstance(other, Set): - return minus_set_set(self._inner, other._inner) - elif isinstance(other, Span): - return minus_spanset_span(set_to_spanset(self._inner), other._inner) - elif isinstance(other, SpanSet): - return minus_spanset_spanset(set_to_spanset(self._inner), other._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') + raise TypeError(f'Operation not supported with type {other.__class__}') def __sub__(self, other): """ - Returns the temporal difference of ``self`` and ``other``. + Returns the difference of ``self`` and ``other``. Args: - other: temporal object to diff with + other: object to diff with Returns: A :class:`Collection` instance. The actual class depends on ``other``. - - MEOS Functions: - minus_set_set, minus_spanset_span, minus_spanset_spanset """ return self.minus(other) + @abstractmethod + def subtract_from(self, other): + """ + Returns the difference of ``other`` and ``self``. + + Args: + other: object to subtract ``self`` from + + Returns: + A :class:`Collection` instance or an element instance. The actual class depends on ``other``. + + See Also: + :meth:`minus` + """ + raise NotImplementedError() + + def __rsub__(self, other): + """ + Returns the difference of ``other`` and ``self``. + + Args: + other: object to subtract ``self`` from + + Returns: + A :class:`Collection` instance or an element instance. The actual class depends on ``other``. + """ + return self.subtract_from(other) + @abstractmethod def union(self, other): """ - Returns the temporal union of ``self`` and ``other``. + Returns the union of ``self`` and ``other``. Args: - other: temporal object to merge with + other: object to merge with Returns: A :class:`Collection` instance. The actual class depends on ``other``. - - MEOS Functions: - union_set_set, union_spanset_span, union_spanset_spanset """ - from .span import Span - from .spanset import SpanSet - if isinstance(other, Set): - return union_set_set(self._inner, other._inner) - elif isinstance(other, Span): - return union_spanset_span(set_to_spanset(self._inner), other._inner) - elif isinstance(other, SpanSet): - return union_spanset_spanset(set_to_spanset(self._inner), other._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') + raise TypeError(f'Operation not supported with type {other.__class__}') def __add__(self, other): """ - Returns the temporal union of ``self`` and ``other``. + Returns the union of ``self`` and ``other``. Args: - other: temporal object to merge with + other: object to merge with Returns: A :class:`Collection` instance. The actual class depends on ``other``. - - MEOS Functions: - union_set_set, union_spanset_span, union_spanset_spanset """ return self.union(other) @@ -601,7 +588,7 @@ def __eq__(self, other): Returns whether ``self`` and ``other`` are equal. Args: - other: temporal object to compare with + other: object to compare with Returns: True if equal, False otherwise @@ -618,7 +605,7 @@ def __ne__(self, other): Returns whether ``self`` and ``other`` are not equal. Args: - other: temporal object to compare with + other: object to compare with Returns: True if not equal, False otherwise @@ -635,7 +622,7 @@ def __lt__(self, other): Return whether ``self`` is less than ``other``. Args: - other: temporal object to compare with + other: object to compare with Returns: True if less than, False otherwise @@ -652,7 +639,7 @@ def __le__(self, other): Return whether ``self`` is less than or equal to ``other``. Args: - other: temporal object to compare with + other: object to compare with Returns: True if less than or equal, False otherwise @@ -669,7 +656,7 @@ def __gt__(self, other): Return whether ``self`` is greater than ``other``. Args: - other: temporal object to compare with + other: object to compare with Returns: True if greater than, False otherwise @@ -686,7 +673,7 @@ def __ge__(self, other): Return whether ``self`` is greater than or equal to ``other``. Args: - other: temporal object to compare with + other: object to compare with Returns: True if greater than or equal, False otherwise diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index 0151b529..4d62eb22 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -153,29 +153,19 @@ def to_spanset(self) -> SpanSet: return span_to_spanset(self._inner) # ------------------------- Accessors ------------------------------------- + @abstractmethod def lower(self) -> T: """ Returns the lower bound of a period - - Returns: - The lower bound of the period as a :class:`datetime.datetime` - - MEOS Functions: - period_lower """ - return period_lower(self._inner) + return NotImplementedError() + @abstractmethod def upper(self) -> T: """ Returns the upper bound of a period - - Returns: - The upper bound of the period as a :class:`datetime.datetime` - - MEOS Functions: - period_upper """ - return period_upper(self._inner) + return NotImplementedError() def lower_inc(self) -> bool: """ @@ -502,16 +492,7 @@ def intersection(self, other): MEOS Functions: intersection_span_span, intersection_spanset_span, intersection_period_timestamp """ - from .set import Set - from .spanset import SpanSet - if isinstance(other, Set): - return intersection_spanset_span(set_to_spanset(other._inner), self._inner) - elif isinstance(other, Span): - return intersection_span_span(self._inner, other._inner) - elif isinstance(other, SpanSet): - return intersection_spanset_span(other._inner, self._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') + raise TypeError(f'Operation not supported with type {other.__class__}') def __mul__(self, other): """ @@ -531,40 +512,25 @@ def __mul__(self, other): @abstractmethod def minus(self, other): """ - Returns the temporal difference of ``self`` and ``other``. + Returns the difference of ``self`` and ``other``. Args: - other: temporal object to diff with + other: object to diff with Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - minus_period_timestamp, minus_span_spanset, minus_span_span + A :class:`SpanSet` instance. """ - from .set import Set - from .spanset import SpanSet - if isinstance(other, Set): - return minus_span_spanset(self._inner, set_to_spanset(other._inner)) - elif isinstance(other, Span): - return minus_span_span(self._inner, other._inner) - elif isinstance(other, SpanSet): - return minus_span_spanset(self._inner, other._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') + raise TypeError(f'Operation not supported with type {other.__class__}') def __sub__(self, other): """ - Returns the temporal difference of ``self`` and ``other``. + Returns the difference of ``self`` and ``other``. Args: - other: temporal object to diff with + other: object to diff with Returns: - A :class:`PeriodSet` instance. - - MEOS Functions: - minus_period_timestamp, minus_span_spanset, minus_span_span + A :class:`SpanSet` instance. """ return self.minus(other) diff --git a/pymeos/pymeos/collections/geo/__init__.py b/pymeos/pymeos/collections/geo/__init__.py index e69de29b..e24207f2 100644 --- a/pymeos/pymeos/collections/geo/__init__.py +++ b/pymeos/pymeos/collections/geo/__init__.py @@ -0,0 +1,3 @@ +from .geoset import GeometrySet, GeographySet + +__all__ = ['GeometrySet', 'GeographySet'] diff --git a/pymeos/pymeos/collections/geo/geoset.py b/pymeos/pymeos/collections/geo/geoset.py new file mode 100644 index 00000000..46a5995a --- /dev/null +++ b/pymeos/pymeos/collections/geo/geoset.py @@ -0,0 +1,287 @@ +from __future__ import annotations + +from abc import ABC +from typing import List, overload, Optional, Union + +import shapely as shp +from pymeos_cffi import geoset_start_value, gserialized_to_shapely_geometry, geoset_end_value, geoset_value_n, \ + geoset_values, intersection_geoset_geo, minus_geoset_geo, union_geoset_geo, geoset_as_ewkt, geoset_as_text, \ + geoset_out, geoset_make, geoset_srid, geoset_round, minus_geo_geoset, geomset_in, geogset_in, pgis_geometry_in, \ + geometry_to_gserialized, pgis_geography_in, geography_to_gserialized, intersection_set_set, minus_set_set, \ + union_set_set + +from ..base import Set + + +class GeoSet(Set[shp.Geometry], ABC): + __slots__ = ['_inner'] + + _make_function = geoset_make + + # ------------------------- Constructors ---------------------------------- + + # ------------------------- Output ---------------------------------------- + + def __str__(self, max_decimals: int = 15): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + geoset_out + """ + return geoset_out(self._inner, max_decimals) + + def as_ewkt(self, max_decimals: int = 15) -> str: + """ + Returns the EWKT representation of ``self``. + + Args: + max_decimals: The number of decimal places to use for the coordinates. + + Returns: + A :class:`str` instance. + + MEOS Functions: + geoset_as_ewkt + """ + return geoset_as_ewkt(self._inner, max_decimals) + + def as_wkt(self, max_decimals: int = 15): + """ + Returns the WKT representation of ``self``. + + Args: + max_decimals: The number of decimal places to use for the coordinates. + + Returns: + A :class:`str` instance. + + MEOS Functions: + geoset_as_text + """ + return geoset_as_text(self._inner, max_decimals) + + def as_text(self, max_decimals: int = 15): + """ + Returns the WKT representation of ``self``. + + Args: + max_decimals: The number of decimal places to use for the coordinates. + + Returns: + A :class:`str` instance. + + MEOS Functions: + geoset_as_text + """ + return geoset_as_text(self._inner, max_decimals) + + # ------------------------- Conversions ----------------------------------- + + def to_spanset(self): + raise NotImplementedError() + + def to_span(self): + raise NotImplementedError() + + # ------------------------- Accessors ------------------------------------- + + def start_element(self) -> shp.Geometry: + """ + Returns the first element in ``self``. + + Returns: + A :class:`Geometry` instance + + MEOS Functions: + geoset_start_value + """ + return gserialized_to_shapely_geometry(geoset_start_value(self._inner)) + + def end_element(self) -> shp.Geometry: + """ + Returns the last element in ``self``. + + Returns: + A :class:`Geometry` instance + + MEOS Functions: + geoset_end_value + """ + return gserialized_to_shapely_geometry(geoset_end_value(self._inner)) + + def element_n(self, n: int) -> shp.Geometry: + """ + Returns the ``n``-th element in ``self``. + + Args: + n: The 0-based index of the element to return. + + Returns: + A :class:`Geometry` instance + + MEOS Functions: + geoset_value_n + """ + super().element_n(n) + return gserialized_to_shapely_geometry(geoset_value_n(self._inner, n + 1)[0]) + + def elements(self) -> List[shp.Geometry]: + """ + Returns a list of all elements in ``self``. + + Returns: + A list of :class:`Geometry` instances + + MEOS Functions: + geoset_values + """ + elems = geoset_values(self._inner) + return [gserialized_to_shapely_geometry(elems[i]) for i in range(self.num_elements())] + + def srid(self) -> int: + """ + Returns the SRID of ``self``. + + Returns: + An integer + + MEOS Functions: + geoset_srid + """ + return geoset_srid(self._inner) + + # ------------------------- Topological Operations -------------------------------- + + def contains(self, content: Union[GeoSet, str]) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + """ + return super().contains(content) + + # ------------------------- Set Operations -------------------------------- + + @overload + def intersection(self, other: str) -> Optional[str]: + ... + + @overload + def intersection(self, other: GeoSet) -> Optional[GeoSet]: + ... + + def intersection(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: A :class:`GeoSet` or :class:`Geometry` instance + + Returns: + An object of the same type as ``other`` or ``None`` if the intersection is empty. + + MEOS Functions: + intersection_geoset_geo, intersection_set_set + """ + if isinstance(other, shp.Geometry): + return gserialized_to_shapely_geometry( + intersection_geoset_geo(self._inner, geometry_to_gserialized(other))[0]) + elif isinstance(other, GeoSet): + result = intersection_set_set(self._inner, other._inner) + return GeoSet(_inner=result) if result is not None else None + else: + return super().intersection(other) + + def minus(self, other: Union[GeoSet, shp.Geometry]) -> Optional[GeoSet]: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: A :class:`GeoSet` or :class:`Geometry` instance + + Returns: + A :class:`GeoSet` instance or ``None`` if the difference is empty. + + MEOS Functions: + minus_geoset_geo, minus_set_set + + See Also: + :meth:`subtract_from` + """ + if isinstance(other, shp.Geometry): + result = minus_geoset_geo(self._inner, geometry_to_gserialized(other)) + return GeoSet(_inner=result) if result is not None else None + elif isinstance(other, GeoSet): + result = minus_set_set(self._inner, other._inner) + return GeoSet(_inner=result) if result is not None else None + else: + return super().minus(other) + + def subtract_from(self, other: shp.Geometry) -> Optional[shp.Geometry]: + """ + Returns the difference of ``other`` and ``self``. + + Args: + other: A :class:`Geometry` instance + + Returns: + A :class:`Geometry` instance. + + MEOS Functions: + minus_geo_geoset + + See Also: + :meth:`minus` + """ + result = minus_geo_geoset(geometry_to_gserialized(other), self._inner) + return gserialized_to_shapely_geometry(result[0]) if result is not None else None + + def union(self, other: Union[GeoSet, shp.Geometry]) -> GeoSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: A :class:`GeoSet` or :class:`Geometry` instance + + Returns: + A :class:`GeoSet` instance. + + MEOS Functions: + union_geoset_geo, union_set_set + """ + if isinstance(other, shp.Geometry): + result = union_geoset_geo(self._inner, geometry_to_gserialized(other)) + return GeoSet(_inner=result) if result is not None else None + elif isinstance(other, GeoSet): + result = union_set_set(self._inner, other._inner) + return GeoSet(_inner=result) if result is not None else None + else: + return super().union(other) + + # ------------------------- Transformations ------------------------------------ + + def round(self, max_decimals): + return self.__class__(_inner=geoset_round(self._inner, max_decimals)) + + +class GeometrySet(GeoSet): + _mobilitydb_name = 'geomset' + + _parse_function = geomset_in + _parse_value_function = lambda x: pgis_geometry_in(x, -1) if isinstance(x, str) else geometry_to_gserialized(x) + + +class GeographySet(GeoSet): + _mobilitydb_name = 'geogset' + + + _parse_function = geogset_in + _parse_value_function = lambda x: pgis_geography_in(x, -1) if isinstance(x, str) else geography_to_gserialized(x) diff --git a/pymeos/pymeos/collections/number/floatset.py b/pymeos/pymeos/collections/number/floatset.py new file mode 100644 index 00000000..78cad050 --- /dev/null +++ b/pymeos/pymeos/collections/number/floatset.py @@ -0,0 +1,306 @@ +from __future__ import annotations + +from typing import List, Union, overload, Optional + +from pymeos_cffi import floatset_in, floatset_make, floatset_out, set_to_spanset, floatset_start_value, floatset_end_value, \ + floatset_value_n, floatset_values, contains_floatset_float, intersection_floatset_float, intersection_set_set, minus_floatset_float, \ + minus_set_set, union_set_set, union_floatset_float, floatset_shift_scale, minus_float_floatset, distance_floatset_float + +from .. import Span, SpanSet +from ..base import Set +from .floatspan import FloatSpan +from .floatspanset import FloatSpanSet + + +class FloatSet(Set[float]): + """ + Class for representing a set of text values. + + ``TextSet`` objects can be created with a single argument of type string as in MobilityDB. + + >>> FloatSet(string='{1, 3, 56}') + + Another possibility is to create a ``TextSet`` object from a list of strings or floats. + + >>> FloatSet(elements=[1, '2', 3, '56']) + + + """ + + __slots__ = ['_inner'] + + _mobilitydb_name = 'floatset' + + _parse_function = floatset_in + _parse_value_function = float + _make_function = floatset_make + + # ------------------------- Constructors ---------------------------------- + + # ------------------------- Output ---------------------------------------- + + def __str__(self, max_decimals: int = 15): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + floatset_out + """ + return floatset_out(self._inner, max_decimals) + + # ------------------------- Conversions ----------------------------------- + + def to_spanset(self) -> FloatSpanSet: + """ + Returns a SpanSet that contains a Span for each element in ``self``. + + Returns: + A new :class:`FloatSpanSet` instance + + MEOS Functions: + set_to_spanset + """ + + return FloatSpanSet(_inner=super().to_spanset()) + + def to_span(self) -> FloatSpan: + """ + Returns a span that encompasses ``self``. + + Returns: + A new :class:`FloatSpan` instance + + MEOS Functions: + set_span + """ + return FloatSpan(_inner=super().to_span()) + + # ------------------------- Accessors ------------------------------------- + + def start_element(self) -> float: + """ + Returns the first element in ``self``. + + Returns: + A :class:`float` instance + + MEOS Functions: + floatset_start_value + """ + return floatset_start_value(self._inner) + + def end_element(self) -> float: + """ + Returns the last element in ``self``. + + Returns: + A :class:`float` instance + + MEOS Functions: + floatset_end_value + """ + return floatset_end_value(self._inner) + + def element_n(self, n: int) -> float: + """ + Returns the ``n``-th element in ``self``. + + Args: + n: The 0-based index of the element to return. + + Returns: + A :class:`float` instance + + MEOS Functions: + floatset_value_n + """ + super().element_n(n) + return floatset_value_n(self._inner, n + 1) + + def elements(self) -> List[float]: + """ + Returns the elements in ``self``. + + Returns: + A list of :class:`float` instances + + MEOS Functions: + floattset_values + """ + elems = floatset_values(self._inner) + return [elems[i] for i in range(self.num_elements())] + + # ------------------------- Transformations ------------------------------------ + + def shift(self, delta: float) -> FloatSet: + """ + Returns a new ``FloatSet`` instance with all elements shifted by ``delta``. + + Args: + delta: The value to shift by. + + Returns: + A new :class:`FloatSet` instance + + MEOS Functions: + floatset_shift_scale + """ + return self.shift_scale(delta, None) + + def scale(self, new_width: float) -> FloatSet: + """ + Returns a new ``FloatSet`` instance with all elements scaled to so that the encompassing + span has width ``new_width``. + + Args: + new_width: The new width. + + Returns: + A new :class:`FloatSet` instance + + MEOS Functions: + floatset_shift_scale + """ + return self.shift_scale(None, new_width) + + def shift_scale(self, delta: Optional[float], new_width: Optional[float]) -> FloatSet: + """ + Returns a new ``FloatSet`` instance with all elements shifted by ``delta`` and scaled to so that the + encompassing span has width ``new_width``. + + Args: + delta: The value to shift by. + new_width: The new width. + + Returns: + A new :class:`FloatSet` instance + + MEOS Functions: + floatset_shift_scale + """ + return FloatSet( + _inner=floatset_shift_scale(self._inner, delta, new_width, delta is not None, new_width is not None)) + + # ------------------------- Topological Operations -------------------------------- + + def contains(self, content: Union[FloatSet, float]) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_set_set, contains_floatset_float + """ + if isinstance(content, float): + return contains_floatset_float(self._inner, content) + else: + return super().contains(content) + + # ------------------------- Set Operations -------------------------------- + + @overload + def intersection(self, other: float) -> Optional[float]: + ... + + @overload + def intersection(self, other: FloatSet) -> Optional[FloatSet]: + ... + + def intersection(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: A :class:`FloatSet` or :class:`float` instance + + Returns: + An object of the same type as ``other`` or ``None`` if the intersection is empty. + + MEOS Functions: + intersection_set_set, intersection_floatset_float + """ + if isinstance(other, float): + return intersection_floatset_float(self._inner, other) + elif isinstance(other, FloatSet): + result = intersection_set_set(self._inner, other._inner) + return FloatSet(_inner=result) if result is not None else None + else: + return super().intersection(other) + + def minus(self, other: Union[FloatSet, float]) -> Optional[FloatSet]: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: A :class:`FloatSet` or :class:`float` instance + + Returns: + A :class:`FloatSet` instance or ``None`` if the difference is empty. + + MEOS Functions: + minus_set_set, minus_floatset_float + """ + if isinstance(other, float): + result = minus_floatset_float(self._inner, other) + return FloatSet(_inner=result) if result is not None else None + elif isinstance(other, FloatSet): + result = minus_set_set(self._inner, other._inner) + return FloatSet(_inner=result) if result is not None else None + else: + return super().minus(other) + + def subtract_from(self, other: float) -> Optional[float]: + """ + Returns the difference of ``other`` and ``self``. + + Args: + other: A :class:`float` instance + + Returns: + A :class:`float` instance or ``None`` if the difference is empty. + + MEOS Functions: + minus_float_floatset + + See Also: + :meth:`minus` + """ + return minus_float_floatset(other, self._inner) + + def union(self, other: Union[FloatSet, float]) -> FloatSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: A :class:`FloatSet` or :class:`float` instance + + Returns: + A :class:`FloatSet` instance. + + MEOS Functions: + union_set_set, union_floatset_float + """ + if isinstance(other, float): + result = union_floatset_float(self._inner, other) + return FloatSet(_inner=result) if result is not None else None + elif isinstance(other, FloatSet): + result = union_set_set(self._inner, other._inner) + return FloatSet(_inner=result) if result is not None else None + else: + return super().union(other) + + # ------------------------- Distance Operations --------------------------- + + def distance(self, other: Union[float, FloatSet, FloatSpan, FloatSpanSet]) -> float: + if isinstance(other, float): + return distance_floatset_float(self._inner, other) + else: + return super().distance(other) diff --git a/pymeos/pymeos/collections/number/floatspan.py b/pymeos/pymeos/collections/number/floatspan.py new file mode 100644 index 00000000..e896b138 --- /dev/null +++ b/pymeos/pymeos/collections/number/floatspan.py @@ -0,0 +1,462 @@ +from __future__ import annotations + +from typing import List, Union, overload, Optional, TYPE_CHECKING + +from pymeos_cffi import floatset_in, floatset_make, floatset_out, set_to_spanset, floatset_start_value, floatset_end_value, \ + floatset_value_n, floatset_values, contains_floatset_float, intersection_floatset_float, intersection_set_set, minus_floatset_float, \ + minus_set_set, union_set_set, union_floatset_float, floatset_shift_scale, minus_float_floatset, distance_floatset_float, \ + floatspan_in, floatspan_lower, floatspan_upper, numspan_shift_scale, contains_floatspan_float, adjacent_floatspan_float, \ + adjacent_span_span, float_to_floatspan, overlaps_span_span, span_eq, left_floatspan_float, overleft_floatspan_float, \ + right_floatspan_float, overright_floatspan_float, intersection_span_span, intersection_spanset_spanset, \ + intersection_spanset_span, minus_floatspan_float, minus_span_span, minus_spanset_span, union_floatspan_float, \ + union_span_span, union_spanset_span, floatspan_out + +from .. import Span, SpanSet +from ..base import Set + +if TYPE_CHECKING: + from ...boxes import TBox + from .floatset import FloatSet + from .floatspanset import FloatSpanSet + + +class FloatSpan(Span[float]): + """ + Class for representing sets of contiguous float values between a lower and + an upper bound. The bounds may be inclusive or not. + + ``FloatSpan`` objects can be created with a single argument of type string + as in MobilityDB. + + >>> FloatSpan('(2.5, 5.21]') + + Another possibility is to provide the ``lower`` and ``upper`` named parameters (of type str or float), and + optionally indicate whether the bounds are inclusive or exclusive (by default, the lower bound is inclusive and the + upper is exclusive): + + >>> FloatSpan(lower=2.0, upper=5.8) + >>> FloatSpan(lower=2.0, upper=5.8, lower_inc=False, upper_inc=True) + >>> FloatSpan(lower='2.0', upper='5.8', upper_inc=True) + """ + + __slots__ = ['_inner'] + + _mobilitydb_name = 'floatspan' + + _parse_function = floatspan_in + _parse_value_function = float + _make_function = floatspan_in + + # ------------------------- Output ---------------------------------------- + def __str__(self, max_decimals: int = 15) -> str: + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + floatspan_out + """ + return floatspan_out(self._inner, max_decimals) + + # ------------------------- Conversions ----------------------------------- + + def to_spanset(self) -> FloatSpanSet: + """ + Returns a spanset containing ``self``. + + Returns: + A new :class:`FloatSpanSet` instance + + MEOS Functions: + span_to_spanset + """ + from .floatspanset import FloatSpanSet + return FloatSpanSet(_inner=super().to_spanset()) + + # ------------------------- Accessors ------------------------------------- + def lower(self) -> float: + """ + Returns the lower bound of ``self``. + + Returns: + The lower bound of the span as a :class:`float` + + MEOS Functions: + period_lower + """ + + return floatspan_lower(self._inner) + + def upper(self) -> float: + """ + Returns the upper bound of ``self``. + + Returns: + The upper bound of the span as a :class:`float` + + MEOS Functions: + period_upper + """ + return floatspan_upper(self._inner) + + def width(self) -> float: + """ + Returns the width of ``self``. + + Returns: + Returns a `float` representing the width of the span + + MEOS Functions: + span_width + """ + return self.width() + + # ------------------------- Transformations ------------------------------- + def shift(self, delta: float) -> FloatSpan: + """ + Return a new ``FloatSpan`` with the lower and upper bounds shifted by ``delta``. + + Args: + delta: The value to shift by + + Returns: + A new ``FloatSpan`` instance + + MEOS Functions: + floatspan_shift_scale + """ + return self.shift_scale(delta, None) + + def scale(self, new_width: float) -> FloatSpan: + """ + Return a new ``FloatSpan`` with the lower and upper bounds scaled so that the width is ``new_width``. + + Args: + new_width: The new width + + Returns: + A new ``FloatSpan`` instance + + MEOS Functions: + floatspan_shift_scale + """ + return self.shift_scale(None, new_width) + + def shift_scale(self, delta: Optional[float], new_width: Optional[float]) -> FloatSpan: + """ + Return a new ``FloatSpan`` with the lower and upper bounds shifted by ``delta`` and scaled so that the width is ``new_width``. + + Args: + delta: The value to shift by + new_width: The new width + + Returns: + A new ``FloatSpan`` instance + + MEOS Functions: + floatspan_shift_scale + """ + return FloatSpan(numspan_shift_scale(self._inner, delta, new_width, delta is not None, new_width is not None)) + + # ------------------------- Topological Operations -------------------------------- + + def is_adjacent(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them + contains it. + + Args: + other: object to compare with + + Returns: + True if adjacent, False otherwise + + MEOS Functions: + adjacent_span_span, adjacent_span_spanset, adjacent_floatset_float + """ + if isinstance(other, float): + return adjacent_floatspan_float(self._inner, other) + elif isinstance(other, TBox): + return self.is_adjacent(other.to_span()) + else: + return super().is_adjacent(other) + + def is_contained_in(self, container: Union[TBox, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is contained in ``container``. + + Args: + container: object to compare with + + Returns: + True if contained, False otherwise + + MEOS Functions: + contained_span_span, contained_span_spanset, contained_floatset_float + """ + if isinstance(container, TBox): + return self.is_contained_in(container.to_span()) + else: + return super().is_contained_in(container) + + def contains(self, content: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_set_set, contains_floatset_float + """ + if isinstance(content, float): + return contains_floatspan_float(self._inner, content) + elif isinstance(content, TBox): + return self.contains(content.to_span()) + else: + return super().contains(content) + + def overlaps(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` overlaps ``other``. That is, both share at least an instant + + Args: + other: object to compare with + + Returns: + True if overlaps, False otherwise + + MEOS Functions: + overlaps_span_span, overlaps_span_spanset, overlaps_period_timestampset, + overlaps_period_temporal + """ + if isinstance(other, float): + return overlaps_span_span(self._inner, float_to_floatspan(other)) + elif isinstance(other, TBox): + return self.overlaps(other.to_span()) + else: + return super().overlaps(other) + + def is_same(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` and the bounding period of ``other`` is the same. + + Args: + other: object to compare with + + Returns: + True if equal, False otherwise + + MEOS Functions: + same_period_temporal + """ + if isinstance(other, float): + return span_eq(self._inner, float_to_floatspan(other)) + elif isinstance(other, TBox): + return self.is_same(other.to_span()) + else: + return super().is_same(other) + + # ------------------------- Position Operations --------------------------- + def is_left(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + + Args: + other: object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + left_span_span, left_span_spanset, left_floatspan_float + """ + if isinstance(other, float): + return left_floatspan_float(self._inner, other) + elif isinstance(other, TBox): + return self.is_left(other.to_span()) + else: + return super().is_left(other) + + def is_over_or_left(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or + at the same time). + + Args: + other: object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overleft_span_span, overleft_span_spanset, overleft_floatspan_float + """ + if isinstance(other, float): + return overleft_floatspan_float(self._inner, other) + elif isinstance(other, TBox): + return self.is_over_or_left(other.to_span()) + else: + return super().is_over_or_left(other) + + def is_right(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. + + Args: + other: object to compare with + + Returns: + True if after, False otherwise + + MEOS Functions: + right_span_span, right_span_spanset, right_floatspan_float + """ + if isinstance(other, float): + return right_floatspan_float(other, self._inner) + elif isinstance(other, TBox): + return self.is_right(other.to_span()) + else: + return super().is_right(other) + + def is_over_or_right(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts + (or at the same time). + + Args: + other: object to compare with + + Returns: + True if overlapping or after, False otherwise + + MEOS Functions: + overright_span_span, overright_span_spanset, overright_floatspan_float + """ + if isinstance(other, float): + return overright_floatspan_float(other, self._inner) + elif isinstance(other, TBox): + return self.is_over_or_right(other.to_span()) + else: + return super().is_over_or_right(other) + + # ------------------------- Distance Operations --------------------------- + def distance(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> float: + """ + Returns the distance between ``self`` and ``other``. + + Args: + other: object to compare with + + Returns: + A :class:`datetime.timedelta` instance + + MEOS Functions: + distance_span_span, distance_span_spanset, distance_floatset_float, + """ + if isinstance(other, float): + return distance_floatset_float(self._inner, other) + elif isinstance(other, TBox): + return self.distance(other.to_span()) + else: + return super().distance(other) + + # ------------------------- Set Operations -------------------------------- + @overload + def intersection(self, other: float) -> Optional[float]: + ... + + @overload + def intersection(self, other: FloatSpan) -> Optional[FloatSpan]: + ... + + @overload + def intersection(self, other: Union[FloatSet, FloatSpanSet]) -> Optional[FloatSpanSet]: + ... + + def intersection(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: object to intersect with + + Returns: + A :class:`Collection[float]` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_span_span, intersection_spanset_span, intersection_floatset_float + """ + from .floatset import FloatSet + from .floatspanset import FloatSpanSet + if isinstance(other, float): + return intersection_floatset_float(self._inner, other) + elif isinstance(other, FloatSet): + return self.intersection(other.to_span()) + elif isinstance(other, FloatSpan): + result = intersection_span_span(self._inner, other._inner) + return FloatSpan(_inner=result) if result is not None else None + elif isinstance(other, FloatSpanSet): + result = intersection_spanset_span(other._inner, self._inner) + return FloatSpanSet(_inner=result) if result is not None else None + else: + super().intersection(other) + + def minus(self, other: Union[float, FloatSet, FloatSpan, FloatSpanSet]) -> FloatSpanSet: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: object to diff with + + Returns: + A :class:`FloatSpanSet` instance. + + MEOS Functions: + minus_span_span, minus_spanset_span, minus_floatset_float + """ + from .floatset import FloatSet + from .floatspanset import FloatSpanSet + if isinstance(other, float): + result = minus_floatspan_float(self._inner, other) + elif isinstance(other, FloatSet): + return self.minus(other.to_spanset()) + elif isinstance(other, FloatSpan): + result = minus_span_span(self._inner, other._inner) + elif isinstance(other, FloatSpanSet): + result = minus_spanset_span(other._inner, self._inner) + else: + super().minus(other) + return FloatSpanSet(_inner=result) if result is not None else None + + def union(self, other: Union[float, FloatSet, FloatSpan, FloatSpanSet]) -> FloatSpanSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: object to merge with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + union_spanset_span, union_span_span, union_floatset_float + """ + from .floatset import FloatSet + from .floatspanset import FloatSpanSet + if isinstance(other, float): + result = union_floatspan_float(self._inner, other) + elif isinstance(other, FloatSet): + return self.union(other.to_spanset()) + elif isinstance(other, FloatSpan): + result = union_span_span(self._inner, other._inner) + elif isinstance(other, FloatSpanSet): + result = union_spanset_span(other._inner, self._inner) + else: + super().union(other) + return FloatSpanSet(_inner=result) if result is not None else None diff --git a/pymeos/pymeos/collections/number/floatspanset.py b/pymeos/pymeos/collections/number/floatspanset.py new file mode 100644 index 00000000..f9290d04 --- /dev/null +++ b/pymeos/pymeos/collections/number/floatspanset.py @@ -0,0 +1,5 @@ +from pymeos.collections import SpanSet + + +class FloatSpanSet(SpanSet[float]): + pass diff --git a/pymeos/pymeos/collections/number/intset.py b/pymeos/pymeos/collections/number/intset.py new file mode 100644 index 00000000..7202b6fb --- /dev/null +++ b/pymeos/pymeos/collections/number/intset.py @@ -0,0 +1,306 @@ +from __future__ import annotations + +from typing import List, Union, overload, Optional + +from pymeos_cffi import intset_in, intset_make, intset_out, set_to_spanset, intset_start_value, intset_end_value, \ + intset_value_n, intset_values, contains_intset_int, intersection_intset_int, intersection_set_set, minus_intset_int, \ + minus_set_set, union_set_set, union_intset_int, intset_shift_scale, minus_int_intset, distance_intset_int + +from .. import Span, SpanSet +from ..base import Set +from .intspan import IntSpan +from .intspanset import IntSpanSet + + +class IntSet(Set[int]): + """ + Class for representing a set of text values. + + ``TextSet`` objects can be created with a single argument of type string as in MobilityDB. + + >>> IntSet(string='{1, 3, 56}') + + Another possibility is to create a ``TextSet`` object from a list of strings or integers. + + >>> IntSet(elements=[1, '2', 3, '56']) + + + """ + + __slots__ = ['_inner'] + + _mobilitydb_name = 'intset' + + _parse_function = intset_in + _parse_value_function = int + _make_function = intset_make + + # ------------------------- Constructors ---------------------------------- + + # ------------------------- Output ---------------------------------------- + + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + intset_out + """ + return intset_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + + def to_spanset(self) -> IntSpanSet: + """ + Returns a SpanSet that contains a Span for each element in ``self``. + + Returns: + A new :class:`IntSpanSet` instance + + MEOS Functions: + set_to_spanset + """ + + return IntSpanSet(_inner=super().to_spanset()) + + def to_span(self) -> IntSpan: + """ + Returns a span that encompasses ``self``. + + Returns: + A new :class:`IntSpan` instance + + MEOS Functions: + set_span + """ + return IntSpan(_inner=super().to_span()) + + # ------------------------- Accessors ------------------------------------- + + def start_element(self) -> int: + """ + Returns the first element in ``self``. + + Returns: + A :class:`int` instance + + MEOS Functions: + intset_start_value + """ + return intset_start_value(self._inner) + + def end_element(self) -> int: + """ + Returns the last element in ``self``. + + Returns: + A :class:`int` instance + + MEOS Functions: + intset_end_value + """ + return intset_end_value(self._inner) + + def element_n(self, n: int) -> int: + """ + Returns the ``n``-th element in ``self``. + + Args: + n: The 0-based index of the element to return. + + Returns: + A :class:`int` instance + + MEOS Functions: + intset_value_n + """ + super().element_n(n) + return intset_value_n(self._inner, n + 1) + + def elements(self) -> List[int]: + """ + Returns the elements in ``self``. + + Returns: + A list of :class:`int` instances + + MEOS Functions: + inttset_values + """ + elems = intset_values(self._inner) + return [elems[i] for i in range(self.num_elements())] + + # ------------------------- Transformations ------------------------------------ + + def shift(self, delta: int) -> IntSet: + """ + Returns a new ``IntSet`` instance with all elements shifted by ``delta``. + + Args: + delta: The value to shift by. + + Returns: + A new :class:`IntSet` instance + + MEOS Functions: + intset_shift_scale + """ + return self.shift_scale(delta, None) + + def scale(self, new_width: int) -> IntSet: + """ + Returns a new ``IntSet`` instance with all elements scaled to so that the encompassing + span has width ``new_width``. + + Args: + new_width: The new width. + + Returns: + A new :class:`IntSet` instance + + MEOS Functions: + intset_shift_scale + """ + return self.shift_scale(None, new_width) + + def shift_scale(self, delta: Optional[int], new_width: Optional[int]) -> IntSet: + """ + Returns a new ``IntSet`` instance with all elements shifted by ``delta`` and scaled to so that the + encompassing span has width ``new_width``. + + Args: + delta: The value to shift by. + new_width: The new width. + + Returns: + A new :class:`IntSet` instance + + MEOS Functions: + intset_shift_scale + """ + return IntSet( + _inner=intset_shift_scale(self._inner, delta, new_width, delta is not None, new_width is not None)) + + # ------------------------- Topological Operations -------------------------------- + + def contains(self, content: Union[IntSet, int]) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_set_set, contains_intset_int + """ + if isinstance(content, int): + return contains_intset_int(self._inner, content) + else: + return super().contains(content) + + # ------------------------- Set Operations -------------------------------- + + @overload + def intersection(self, other: int) -> Optional[int]: + ... + + @overload + def intersection(self, other: IntSet) -> Optional[IntSet]: + ... + + def intersection(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: A :class:`IntSet` or :class:`int` instance + + Returns: + An object of the same type as ``other`` or ``None`` if the intersection is empty. + + MEOS Functions: + intersection_set_set, intersection_intset_int + """ + if isinstance(other, int): + return intersection_intset_int(self._inner, other) + elif isinstance(other, IntSet): + result = intersection_set_set(self._inner, other._inner) + return IntSet(_inner=result) if result is not None else None + else: + return super().intersection(other) + + def minus(self, other: Union[IntSet, int]) -> Optional[IntSet]: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: A :class:`IntSet` or :class:`int` instance + + Returns: + A :class:`IntSet` instance or ``None`` if the difference is empty. + + MEOS Functions: + minus_set_set, minus_intset_int + """ + if isinstance(other, int): + result = minus_intset_int(self._inner, other) + return IntSet(_inner=result) if result is not None else None + elif isinstance(other, IntSet): + result = minus_set_set(self._inner, other._inner) + return IntSet(_inner=result) if result is not None else None + else: + return super().minus(other) + + def subtract_from(self, other: int) -> Optional[int]: + """ + Returns the difference of ``other`` and ``self``. + + Args: + other: A :class:`int` instance + + Returns: + A :class:`int` instance or ``None`` if the difference is empty. + + MEOS Functions: + minus_int_intset + + See Also: + :meth:`minus` + """ + return minus_int_intset(other, self._inner) + + def union(self, other: Union[IntSet, int]) -> IntSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: A :class:`IntSet` or :class:`int` instance + + Returns: + A :class:`IntSet` instance. + + MEOS Functions: + union_set_set, union_intset_int + """ + if isinstance(other, int): + result = union_intset_int(self._inner, other) + return IntSet(_inner=result) if result is not None else None + elif isinstance(other, IntSet): + result = union_set_set(self._inner, other._inner) + return IntSet(_inner=result) if result is not None else None + else: + return super().union(other) + + # ------------------------- Distance Operations --------------------------- + + def distance(self, other: Union[int, IntSet, IntSpan, IntSpanSet]) -> float: + if isinstance(other, int): + return distance_intset_int(self._inner, other) + else: + return super().distance(other) diff --git a/pymeos/pymeos/collections/number/intspan.py b/pymeos/pymeos/collections/number/intspan.py new file mode 100644 index 00000000..c15230f9 --- /dev/null +++ b/pymeos/pymeos/collections/number/intspan.py @@ -0,0 +1,462 @@ +from __future__ import annotations + +from typing import List, Union, overload, Optional, TYPE_CHECKING + +from pymeos_cffi import intset_in, intset_make, intset_out, set_to_spanset, intset_start_value, intset_end_value, \ + intset_value_n, intset_values, contains_intset_int, intersection_intset_int, intersection_set_set, minus_intset_int, \ + minus_set_set, union_set_set, union_intset_int, intset_shift_scale, minus_int_intset, distance_intset_int, \ + intspan_in, intspan_lower, intspan_upper, numspan_shift_scale, contains_intspan_int, adjacent_intspan_int, \ + adjacent_span_span, int_to_intspan, overlaps_span_span, span_eq, left_intspan_int, overleft_intspan_int, \ + right_intspan_int, overright_intspan_int, intersection_span_span, intersection_spanset_spanset, \ + intersection_spanset_span, minus_intspan_int, minus_span_span, minus_spanset_span, union_intspan_int, \ + union_span_span, union_spanset_span, intspan_out + +from .. import Span, SpanSet +from ..base import Set + +if TYPE_CHECKING: + from ...boxes import TBox + from .intset import IntSet + from .intspanset import IntSpanSet + + +class IntSpan(Span[int]): + """ + Class for representing sets of contiguous integer values between a lower and + an upper bound. The bounds may be inclusive or not. + + ``IntSpan`` objects can be created with a single argument of type string + as in MobilityDB. + + >>> IntSpan('(2, 5]') + + Another possibility is to provide the ``lower`` and ``upper`` named parameters (of type str or int), and + optionally indicate whether the bounds are inclusive or exclusive (by default, the lower bound is inclusive and the + upper is exclusive): + + >>> IntSpan(lower=2, upper=5) + >>> IntSpan(lower=2, upper=5, lower_inc=False, upper_inc=True) + >>> IntSpan(lower='2', upper='5', upper_inc=True) + """ + + __slots__ = ['_inner'] + + _mobilitydb_name = 'intspan' + + _parse_function = intspan_in + _parse_value_function = int + _make_function = intspan_in + + # ------------------------- Output ---------------------------------------- + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + intspan_out + """ + return intspan_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + + def to_spanset(self) -> IntSpanSet: + """ + Returns a spanset containing ``self``. + + Returns: + A new :class:`IntSpanSet` instance + + MEOS Functions: + span_to_spanset + """ + from .intspanset import IntSpanSet + return IntSpanSet(_inner=super().to_spanset()) + + # ------------------------- Accessors ------------------------------------- + def lower(self) -> int: + """ + Returns the lower bound of ``self``. + + Returns: + The lower bound of the span as a :class:`int` + + MEOS Functions: + period_lower + """ + + return intspan_lower(self._inner) + + def upper(self) -> int: + """ + Returns the upper bound of ``self``. + + Returns: + The upper bound of the span as a :class:`int` + + MEOS Functions: + period_upper + """ + return intspan_upper(self._inner) + + def width(self) -> float: + """ + Returns the width of ``self``. + + Returns: + Returns a `float` representing the width of the span + + MEOS Functions: + span_width + """ + return self.width() + + # ------------------------- Transformations ------------------------------- + def shift(self, delta: int) -> IntSpan: + """ + Return a new ``IntSpan`` with the lower and upper bounds shifted by ``delta``. + + Args: + delta: The value to shift by + + Returns: + A new ``IntSpan`` instance + + MEOS Functions: + intspan_shift_scale + """ + return self.shift_scale(delta, None) + + def scale(self, new_width: int) -> IntSpan: + """ + Return a new ``IntSpan`` with the lower and upper bounds scaled so that the width is ``new_width``. + + Args: + new_width: The new width + + Returns: + A new ``IntSpan`` instance + + MEOS Functions: + intspan_shift_scale + """ + return self.shift_scale(None, new_width) + + def shift_scale(self, delta: Optional[int], new_width: Optional[int]) -> IntSpan: + """ + Return a new ``IntSpan`` with the lower and upper bounds shifted by ``delta`` and scaled so that the width is ``new_width``. + + Args: + delta: The value to shift by + new_width: The new width + + Returns: + A new ``IntSpan`` instance + + MEOS Functions: + intspan_shift_scale + """ + return IntSpan(numspan_shift_scale(self._inner, delta, new_width, delta is not None, new_width is not None)) + + # ------------------------- Topological Operations -------------------------------- + + def is_adjacent(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them + contains it. + + Args: + other: object to compare with + + Returns: + True if adjacent, False otherwise + + MEOS Functions: + adjacent_span_span, adjacent_span_spanset, adjacent_intset_int + """ + if isinstance(other, int): + return adjacent_intspan_int(self._inner, other) + elif isinstance(other, TBox): + return self.is_adjacent(other.to_span()) + else: + return super().is_adjacent(other) + + def is_contained_in(self, container: Union[TBox, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is contained in ``container``. + + Args: + container: object to compare with + + Returns: + True if contained, False otherwise + + MEOS Functions: + contained_span_span, contained_span_spanset, contained_intset_int + """ + if isinstance(container, TBox): + return self.is_contained_in(container.to_span()) + else: + return super().is_contained_in(container) + + def contains(self, content: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_set_set, contains_intset_int + """ + if isinstance(content, int): + return contains_intspan_int(self._inner, content) + elif isinstance(content, TBox): + return self.contains(content.to_span()) + else: + return super().contains(content) + + def overlaps(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` overlaps ``other``. That is, both share at least an instant + + Args: + other: object to compare with + + Returns: + True if overlaps, False otherwise + + MEOS Functions: + overlaps_span_span, overlaps_span_spanset, overlaps_period_timestampset, + overlaps_period_temporal + """ + if isinstance(other, int): + return overlaps_span_span(self._inner, int_to_intspan(other)) + elif isinstance(other, TBox): + return self.overlaps(other.to_span()) + else: + return super().overlaps(other) + + def is_same(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` and the bounding period of ``other`` is the same. + + Args: + other: object to compare with + + Returns: + True if equal, False otherwise + + MEOS Functions: + same_period_temporal + """ + if isinstance(other, int): + return span_eq(self._inner, int_to_intspan(other)) + elif isinstance(other, TBox): + return self.is_same(other.to_span()) + else: + return super().is_same(other) + + # ------------------------- Position Operations --------------------------- + def is_left(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + + Args: + other: object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + left_span_span, left_span_spanset, left_intspan_int + """ + if isinstance(other, int): + return left_intspan_int(self._inner, other) + elif isinstance(other, TBox): + return self.is_left(other.to_span()) + else: + return super().is_left(other) + + def is_over_or_left(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or + at the same time). + + Args: + other: object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overleft_span_span, overleft_span_spanset, overleft_intspan_int + """ + if isinstance(other, int): + return overleft_intspan_int(self._inner, other) + elif isinstance(other, TBox): + return self.is_over_or_left(other.to_span()) + else: + return super().is_over_or_left(other) + + def is_right(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. + + Args: + other: object to compare with + + Returns: + True if after, False otherwise + + MEOS Functions: + right_span_span, right_span_spanset, right_intspan_int + """ + if isinstance(other, int): + return right_intspan_int(other, self._inner) + elif isinstance(other, TBox): + return self.is_right(other.to_span()) + else: + return super().is_right(other) + + def is_over_or_right(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts + (or at the same time). + + Args: + other: object to compare with + + Returns: + True if overlapping or after, False otherwise + + MEOS Functions: + overright_span_span, overright_span_spanset, overright_intspan_int + """ + if isinstance(other, int): + return overright_intspan_int(other, self._inner) + elif isinstance(other, TBox): + return self.is_over_or_right(other.to_span()) + else: + return super().is_over_or_right(other) + + # ------------------------- Distance Operations --------------------------- + def distance(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> float: + """ + Returns the distance between ``self`` and ``other``. + + Args: + other: object to compare with + + Returns: + A :class:`datetime.timedelta` instance + + MEOS Functions: + distance_span_span, distance_span_spanset, distance_intset_int, + """ + if isinstance(other, int): + return distance_intset_int(self._inner, other) + elif isinstance(other, TBox): + return self.distance(other.to_span()) + else: + return super().distance(other) + + # ------------------------- Set Operations -------------------------------- + @overload + def intersection(self, other: int) -> Optional[int]: + ... + + @overload + def intersection(self, other: IntSpan) -> Optional[IntSpan]: + ... + + @overload + def intersection(self, other: Union[IntSet, IntSpanSet]) -> Optional[IntSpanSet]: + ... + + def intersection(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: object to intersect with + + Returns: + A :class:`Collection[int]` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_span_span, intersection_spanset_span, intersection_intset_int + """ + from .intset import IntSet + from .intspanset import IntSpanSet + if isinstance(other, int): + return intersection_intset_int(self._inner, other) + elif isinstance(other, IntSet): + return self.intersection(other.to_span()) + elif isinstance(other, IntSpan): + result = intersection_span_span(self._inner, other._inner) + return IntSpan(_inner=result) if result is not None else None + elif isinstance(other, IntSpanSet): + result = intersection_spanset_span(other._inner, self._inner) + return IntSpanSet(_inner=result) if result is not None else None + else: + super().intersection(other) + + def minus(self, other: Union[int, IntSet, IntSpan, IntSpanSet]) -> IntSpanSet: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: object to diff with + + Returns: + A :class:`IntSpanSet` instance. + + MEOS Functions: + minus_span_span, minus_spanset_span, minus_intset_int + """ + from .intset import IntSet + from .intspanset import IntSpanSet + if isinstance(other, int): + result = minus_intspan_int(self._inner, other) + elif isinstance(other, IntSet): + return self.minus(other.to_spanset()) + elif isinstance(other, IntSpan): + result = minus_span_span(self._inner, other._inner) + elif isinstance(other, IntSpanSet): + result = minus_spanset_span(other._inner, self._inner) + else: + super().minus(other) + return IntSpanSet(_inner=result) if result is not None else None + + def union(self, other: Union[int, IntSet, IntSpan, IntSpanSet]) -> IntSpanSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: object to merge with + + Returns: + A :class:`PeriodSet` instance. + + MEOS Functions: + union_spanset_span, union_span_span, union_intset_int + """ + from .intset import IntSet + from .intspanset import IntSpanSet + if isinstance(other, int): + result = union_intspan_int(self._inner, other) + elif isinstance(other, IntSet): + return self.union(other.to_spanset()) + elif isinstance(other, IntSpan): + result = union_span_span(self._inner, other._inner) + elif isinstance(other, IntSpanSet): + result = union_spanset_span(other._inner, self._inner) + else: + super().union(other) + return IntSpanSet(_inner=result) if result is not None else None diff --git a/pymeos/pymeos/collections/number/intspanset.py b/pymeos/pymeos/collections/number/intspanset.py new file mode 100644 index 00000000..cbb63a84 --- /dev/null +++ b/pymeos/pymeos/collections/number/intspanset.py @@ -0,0 +1,5 @@ +from pymeos.collections import SpanSet + + +class IntSpanSet(SpanSet[int]): + pass diff --git a/pymeos/pymeos/collections/text/__init__.py b/pymeos/pymeos/collections/text/__init__.py index e69de29b..43983783 100644 --- a/pymeos/pymeos/collections/text/__init__.py +++ b/pymeos/pymeos/collections/text/__init__.py @@ -0,0 +1,3 @@ +from .textset import TextSet + +__all__ = ['TextSet'] \ No newline at end of file diff --git a/pymeos/pymeos/collections/text/textset.py b/pymeos/pymeos/collections/text/textset.py index 992907a7..a883c9a5 100644 --- a/pymeos/pymeos/collections/text/textset.py +++ b/pymeos/pymeos/collections/text/textset.py @@ -4,7 +4,6 @@ from pymeos_cffi import * -from .. import Span, SpanSet from ..base import Set @@ -25,6 +24,8 @@ class TextSet(Set[str]): __slots__ = ['_inner'] + _mobilitydb_name = 'textset' + _parse_function = textset_in _parse_value_function = lambda x: x _make_function = textset_make @@ -47,10 +48,10 @@ def __str__(self): # ------------------------- Conversions ----------------------------------- - def to_spanset(self) -> SpanSet: + def to_spanset(self): raise NotImplementedError() - def to_span(self) -> Span: + def to_span(self): raise NotImplementedError() # ------------------------- Accessors ------------------------------------- @@ -93,7 +94,7 @@ def element_n(self, n: int): textset_value_n """ super().element_n(n) - return textset_value_n(self._inner, n) + return text2cstring(textset_value_n(self._inner, n + 1)[0]) def elements(self): """ @@ -106,7 +107,27 @@ def elements(self): textset_values """ elems = textset_values(self._inner) - return [elems[i] for i in range(self.num_elements())] + return [text2cstring(elems[i]) for i in range(self.num_elements())] + + # ------------------------- Topological Operations -------------------------------- + + def contains(self, content: Union[TextSet, str]) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_set_set, contains_textset_text + """ + if isinstance(content, str): + return contains_textset_text(self._inner, content) + else: + return super().contains(content) # ------------------------- Set Operations -------------------------------- @@ -132,10 +153,10 @@ def intersection(self, other): intersection_textset_text, intersection_set_set """ if isinstance(other, str): - return intersection_textset_text(self._inner, other) + return text2cstring(intersection_textset_text(self._inner, other)[0]) elif isinstance(other, TextSet): - result = super().intersection(other) - return TextSet(elements=result) if result is not None else None + result = intersection_set_set(self._inner, other._inner) + return TextSet(_inner=result) if result is not None else None else: return super().intersection(other) @@ -147,20 +168,39 @@ def minus(self, other: Union[TextSet, str]) -> Optional[TextSet]: other: A :class:`TextSet` or :class:`str` instance Returns: - A :class:`TextSet` instance. + A :class:`TextSet` instance or ``None`` if the difference is empty. MEOS Functions: minus_textset_text, minus_set_set """ if isinstance(other, str): result = minus_textset_text(self._inner, other) - return TextSet(elements=result) if result is not None else None + return TextSet(_inner=result) if result is not None else None elif isinstance(other, TextSet): - result = super().minus(other) - return TextSet(elements=result) if result is not None else None + result = minus_set_set(self._inner, other._inner) + return TextSet(_inner=result) if result is not None else None else: return super().minus(other) + def subtract_from(self, other: str) -> Optional[str]: + """ + Returns the difference of ``other`` and ``self``. + + Args: + other: A :class:`str` instance + + Returns: + A :class:`str` instance. + + MEOS Functions: + minus_geo_geoset + + See Also: + :meth:`minus` + """ + result = minus_text_textset(other, self._inner) + return text2cstring(result[0]) if result is not None else None + def union(self, other: Union[TextSet, str]) -> TextSet: """ Returns the union of ``self`` and ``other``. @@ -176,9 +216,9 @@ def union(self, other: Union[TextSet, str]) -> TextSet: """ if isinstance(other, str): result = union_textset_text(self._inner, other) - return TextSet(elements=result) if result is not None else None + return TextSet(_inner=result) if result is not None else None elif isinstance(other, TextSet): - result = super().union(other) - return TextSet(elements=result) if result is not None else None + result = union_set_set(self._inner, other._inner) + return TextSet(_inner=result) if result is not None else None else: return super().union(other) diff --git a/pymeos/pymeos/collections/time/period.py b/pymeos/pymeos/collections/time/period.py index 7ca8d7ac..500b208e 100644 --- a/pymeos/pymeos/collections/time/period.py +++ b/pymeos/pymeos/collections/time/period.py @@ -98,7 +98,7 @@ def lower(self) -> datetime: period_lower """ - return timestamptz_to_datetime(super().lower()) + return timestamptz_to_datetime(period_lower(self._inner)) def upper(self) -> datetime: """ @@ -110,7 +110,7 @@ def upper(self) -> datetime: MEOS Functions: period_upper """ - return timestamptz_to_datetime(super().upper()) + return timestamptz_to_datetime(period_upper(self._inner)) def duration(self) -> timedelta: """ @@ -548,16 +548,15 @@ def intersection(self, other: Time) -> Optional[Time]: result = intersection_period_timestamp(self._inner, datetime_to_timestamptz(other)) return timestamptz_to_datetime(result) if result is not None else None elif isinstance(other, TimestampSet): - result = super().intersection(other) - return TimestampSet(_inner=result) if result is not None else None + return self.intersection(other.to_periodset()) elif isinstance(other, Period): - result = super().intersection(other) + result = intersection_span_span(self._inner, other._inner) return Period(_inner=result) if result is not None else None elif isinstance(other, PeriodSet): - result = super().intersection(other) + result = intersection_spanset_span(other._inner, self._inner) return PeriodSet(_inner=result) if result is not None else None else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().intersection(other) def minus(self, other: Time) -> PeriodSet: """ @@ -570,20 +569,20 @@ def minus(self, other: Time) -> PeriodSet: A :class:`PeriodSet` instance. MEOS Functions: - minus_period_timestamp, minus_span_spanset, minus_span_span + minus_period_timestamp, minus_span_spanset, minus_span_span """ from .periodset import PeriodSet from .timestampset import TimestampSet if isinstance(other, datetime): result = minus_period_timestamp(self._inner, datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): - result = super().minus(other) + return self.minus(other.to_periodset()) elif isinstance(other, Period): - result = super().minus(other) + result = minus_span_span(self._inner, other._inner) elif isinstance(other, PeriodSet): - result = super().minus(other) + result = minus_span_spanset(self._inner, other._inner) else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().minus(other) return PeriodSet(_inner=result) if result is not None else None def union(self, other: Time) -> PeriodSet: @@ -617,14 +616,3 @@ def union(self, other: Time) -> PeriodSet: def plot(self, *args, **kwargs): from ...plotters import TimePlotter return TimePlotter.plot_period(self, *args, **kwargs) - - # ------------------------- Database Operations --------------------------- - @staticmethod - def read_from_cursor(value, _=None): - """ - Reads a :class:`Period` from a database cursor. Used when automatically loading objects from the database. - Users should use the class constructor instead. - """ - if not value: - return None - return Period(string=value) diff --git a/pymeos/pymeos/collections/time/time_collection.py b/pymeos/pymeos/collections/time/time_collection.py index c663db1c..ae502887 100644 --- a/pymeos/pymeos/collections/time/time_collection.py +++ b/pymeos/pymeos/collections/time/time_collection.py @@ -1,11 +1,8 @@ -from abc import ABC, abstractmethod +from abc import ABC from datetime import datetime -from typing import TypeVar from ..base.collection import Collection -Self = TypeVar('Self', bound='Set[Any]') - class TimeCollection(Collection[datetime], ABC): diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py index 1049be96..c006d30b 100644 --- a/pymeos/pymeos/collections/time/timestampset.py +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -581,17 +581,15 @@ def intersection(self, other: Union[Time, Temporal]) -> Optional[Time]: from .period import Period from .periodset import PeriodSet if isinstance(other, datetime): - result = intersection_set_set(self._inner, timestamp_to_tstzset(datetime_to_timestamptz(other))) + result = intersection_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) return timestamptz_to_datetime(result) if result is not None else None elif isinstance(other, TimestampSet): - result = super().intersection(other) + result = intersection_set_set(self._inner, other._inner) return TimestampSet(_inner=result) if result is not None else None elif isinstance(other, Period): - result = super().intersection(other) - return PeriodSet(_inner=result) if result is not None else None + return self.to_periodset().intersection(other) elif isinstance(other, PeriodSet): - result = super().intersection(other) - return PeriodSet(_inner=result) if result is not None else None + return self.to_periodset().intersection(other) elif isinstance(other, Temporal): return self.intersection(other.time()) elif isinstance(other, get_args(Box)): @@ -626,20 +624,18 @@ def minus(self, other: Union[Time, Temporal, Box]) -> Optional[Time]: result = minus_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) return TimestampSet(_inner=result) if result is not None else None elif isinstance(other, TimestampSet): - result = super().minus(other) + result = minus_set_set(self._inner, other._inner) return TimestampSet(_inner=result) if result is not None else None elif isinstance(other, Period): - result = super().minus(other) - return PeriodSet(_inner=result) if result is not None else None + return self.to_periodset().minus(other) elif isinstance(other, PeriodSet): - result = super().minus(other) - return PeriodSet(_inner=result) if result is not None else None + return self.to_periodset().minus(other) elif isinstance(other, Temporal): return self.minus(other.time()) elif isinstance(other, get_args(Box)): return self.minus(other.to_period()) else: - raise TypeError(f'Operation not supported with type {other.__class__}') + return super().minus(other) @overload def union(self, other: Union[datetime, TimestampSet]) -> TimestampSet: @@ -667,17 +663,17 @@ def union(self, other: Union[Time, Temporal, Box]) -> Union[PeriodSet, Timestamp if isinstance(other, datetime): return TimestampSet(_inner=union_timestampset_timestamp(self._inner, datetime_to_timestamptz(other))) elif isinstance(other, TimestampSet): - return TimestampSet(_inner=super().union(other)) + return TimestampSet(_inner=union_set_set(self._inner, other._inner)) elif isinstance(other, Period): - return PeriodSet(_inner=super().union(other)) + return self.to_periodset().union(other) elif isinstance(other, PeriodSet): - return PeriodSet(_inner=super().union(other)) + return self.to_periodset().union(other) elif isinstance(other, Temporal): return self.union(other.time()) elif isinstance(other, get_args(Box)): return self.union(other.to_period()) else: - raise TypeError(f'Operation not supported with type {other.__class__}') + return super().union(other) # ------------------------- Comparisons ----------------------------------- diff --git a/pymeos/pymeos/main/tbool.py b/pymeos/pymeos/main/tbool.py index cd415b8c..50153b04 100644 --- a/pymeos/pymeos/main/tbool.py +++ b/pymeos/pymeos/main/tbool.py @@ -51,7 +51,7 @@ def from_base_time(value: bool, base: datetime) -> TBoolInst: @staticmethod @overload def from_base_time(value: bool, base: Union[TimestampSet, Period]) -> \ - TBoolSeq: + TBoolSeq: ... @staticmethod @@ -77,16 +77,16 @@ def from_base_time(value: bool, base: Time) -> TBool: """ if isinstance(base, datetime): return TBoolInst(_inner=tboolinst_make(value, - datetime_to_timestamptz(base))) + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): return TBoolSeq(_inner=tboolseq_from_base_timestampset(value, - base._inner)) + base._inner)) elif isinstance(base, Period): return TBoolSeq(_inner=tboolseq_from_base_period(value, - base._inner)) + base._inner)) elif isinstance(base, PeriodSet): return TBoolSeqSet(_inner=tboolseqset_from_base_periodset(value, - base._inner)) + base._inner)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Output ---------------------------------------- @@ -151,7 +151,7 @@ def value_at_timestamp(self, timestamp) -> bool: tbool_value_at_timestamp """ return tbool_value_at_timestamp(self._inner, - datetime_to_timestamptz(timestamp), True) + datetime_to_timestamptz(timestamp), True) # ------------------------- Ever and Always Comparisons ------------------- def always_eq(self, value: bool) -> bool: @@ -302,7 +302,7 @@ def temporal_and(self, other: Union[bool, TBool]) -> TBool: return self.__class__(_inner=tand_tbool_bool(self._inner, other)) elif isinstance(other, TBool): return self.__class__(_inner=tand_tbool_tbool(self._inner, - other._inner)) + other._inner)) raise TypeError(f'Operation not supported with type {other.__class__}') def __and__(self, other): @@ -337,7 +337,7 @@ def temporal_or(self, other: Union[bool, TBool]) -> TBool: return self.__class__(_inner=tor_tbool_bool(self._inner, other)) elif isinstance(other, TBool): return self.__class__(_inner=tor_tbool_tbool(self._inner, - other._inner)) + other._inner)) raise TypeError(f'Operation not supported with type {other.__class__}') def __or__(self, other): @@ -439,7 +439,6 @@ def read_from_cursor(value, _=None): raise Exception("ERROR: Could not parse temporal boolean value") - class TBoolInst(TInstant[bool, 'TBool', 'TBoolInst', 'TBoolSeq', 'TBoolSeqSet'], TBool): """ Class for representing temporal boolean values at a single instant. @@ -451,11 +450,11 @@ def __init__(self, string: Optional[str] = None, *, value: Optional[Union[str, bool]] = None, timestamp: Optional[Union[str, datetime]] = None, _inner=None): super().__init__(string=string, value=value, timestamp=timestamp, - _inner=_inner) + _inner=_inner) class TBoolSeq(TSequence[bool, 'TBool', 'TBoolInst', 'TBoolSeq', - 'TBoolSeqSet'], TBool): +'TBoolSeqSet'], TBool): """ Class for representing temporal boolean values over a period of time. """ @@ -468,13 +467,13 @@ def __init__(self, string: Optional[str] = None, *, interpolation: TInterpolation = TInterpolation.STEPWISE, normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, - lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, - normalize=normalize, _inner=_inner) + lower_inc=lower_inc, upper_inc=upper_inc, + expandable=expandable, interpolation=interpolation, + normalize=normalize, _inner=_inner) class TBoolSeqSet(TSequenceSet[bool, 'TBool', 'TBoolInst', 'TBoolSeq', - 'TBoolSeqSet'], TBool): +'TBoolSeqSet'], TBool): """ Class for representing temporal boolean values over a period of time with gaps. """ @@ -484,4 +483,4 @@ def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, TBoolSeq]]] = None, normalize: bool = True, _inner=None): super().__init__(string=string, sequence_list=sequence_list, - normalize=normalize, _inner=_inner) + normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index 258ad755..22a73768 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -25,7 +25,7 @@ class TFloat(TNumber[float, 'TFloat', 'TFloatInst', 'TFloatSeq', 'TFloatSeqSet'] # ------------------------- Constructors ---------------------------------- @staticmethod def from_base_temporal(value: float, base: Temporal, - interpolation: TInterpolation = TInterpolation.LINEAR) -> TFloat: + interpolation: TInterpolation = TInterpolation.LINEAR) -> TFloat: """ Returns a new temporal float with the value `value` and the temporal frame of `base`. @@ -52,7 +52,7 @@ def from_base_time(value: float, base: datetime) -> TFloatInst: @staticmethod @overload def from_base_time(value: float, base: Union[TimestampSet, Period]) -> \ - TFloatSeq: + TFloatSeq: ... @staticmethod @@ -62,7 +62,7 @@ def from_base_time(value: float, base: PeriodSet) -> TFloatSeqSet: @staticmethod def from_base_time(value: float, base: Time, - interpolation: TInterpolation = None) -> TFloat: + interpolation: TInterpolation = None) -> TFloat: """ Returns a new temporal float with the value `value` and the temporal frame of `base`. @@ -81,16 +81,16 @@ def from_base_time(value: float, base: Time, """ if isinstance(base, datetime): return TFloatInst(_inner=tfloatinst_make(value, - datetime_to_timestamptz(base))) + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): return TFloatSeq(_inner=tfloatseq_from_base_timestampset(value, - base._inner)) + base._inner)) elif isinstance(base, Period): return TFloatSeq(_inner=tfloatseq_from_base_period(value, - base._inner, interpolation)) + base._inner, interpolation)) elif isinstance(base, PeriodSet): return TFloatSeqSet(_inner=tfloatseqset_from_base_periodset(value, - base._inner, interpolation)) + base._inner, interpolation)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Output ---------------------------------------- @@ -140,7 +140,7 @@ def to_tint(self) -> TInt: from ..factory import _TemporalFactory if self.interpolation() == TInterpolation.LINEAR: raise ValueError("Cannot convert a temporal float with linear " \ - "interpolation to a temporal integer") + "interpolation to a temporal integer") return _TemporalFactory.create_temporal(tfloat_to_tint(self._inner)) def to_floatrange(self) -> floatrange: @@ -603,7 +603,7 @@ def temporal_less(self, other: Union[int, float, Temporal]) -> Temporal: return Temporal._factory(result) def temporal_less_or_equal(self, other: Union[int, float, Temporal]) -> \ - Temporal: + Temporal: """ Returns the temporal less or equal relation between `self` and `other`. @@ -625,7 +625,7 @@ def temporal_less_or_equal(self, other: Union[int, float, Temporal]) -> \ return Temporal._factory(result) def temporal_greater_or_equal(self, other: Union[int, float, Temporal]) \ - -> Temporal: + -> Temporal: """ Returns the temporal greater or equal relation between `self` and `other`. @@ -668,8 +668,8 @@ def temporal_greater(self, other: Union[int, float, Temporal]) -> Temporal: return Temporal._factory(result) # ------------------------- Restrictions ---------------------------------- - def at(self, other: Union[int, float, List[int], List[float], - floatrange, List[floatrange], TBox, Time]) -> TFloat: + def at(self, other: Union[int, float, List[int], List[float], + floatrange, List[floatrange], TBox, Time]) -> TFloat: """ Returns a new temporal float with the values of `self` restricted to the value or time `other`. @@ -690,17 +690,17 @@ def at(self, other: Union[int, float, List[int], List[float], elif isinstance(other, floatrange): result = tnumber_at_span(self._inner, floatrange_to_floatspan(other)) elif isinstance(other, list) and (isinstance(other[0], int) or \ - isinstance(other[0], float)): + isinstance(other[0], float)): result = temporal_at_values(self._inner, floatset_make(other)) # elif isinstance(other, list) and (isinstance(other[0], floatrange) or isinstance(other[0], intrange)): - # results = [tnumber_at_span(self._inner, value) for value in other if other is not None] - # result = temporal_merge_array(results, len(results)) + # results = [tnumber_at_span(self._inner, value) for value in other if other is not None] + # result = temporal_merge_array(results, len(results)) else: return super().at(other) return Temporal._factory(result) def minus(self, other: Union[int, float, List[int], List[float], - floatrange, List[floatrange], TBox, Time]) -> Temporal: + floatrange, List[floatrange], TBox, Time]) -> Temporal: """ Returns a new temporal float with the values of `self` restricted to the complement of the time or value `other`. @@ -720,10 +720,10 @@ def minus(self, other: Union[int, float, List[int], List[float], result = tfloat_minus_value(self._inner, float(other)) elif isinstance(other, floatrange): result = tnumber_minus_span(self._inner, - floatrange_to_floatspan(other)) + floatrange_to_floatspan(other)) elif isinstance(other, list) and isinstance(other[0], float): result = temporal_minus_values(self._inner, - floatset_make(other)) + floatset_make(other)) else: return super().minus(other) return Temporal._factory(result) @@ -742,7 +742,7 @@ def value_at_timestamp(self, timestamp) -> float: tfloat_value_at_timestamp """ return tfloat_value_at_timestamp(self._inner, - datetime_to_timestamptz(timestamp), True) + datetime_to_timestamptz(timestamp), True) def derivative(self) -> TFloat: """ @@ -774,7 +774,7 @@ def to_degrees(self, normalize: bool = True) -> TFloat: """ from ..factory import _TemporalFactory return _TemporalFactory.create_temporal(tfloat_degrees(self._inner, - normalize)) + normalize)) def to_radians(self) -> TFloat: """ @@ -789,7 +789,7 @@ def to_radians(self) -> TFloat: from ..factory import _TemporalFactory return _TemporalFactory.create_temporal(tfloat_radians(self._inner)) - def round(self, maxdd : int = 0) -> TFloat: + def round(self, maxdd: int = 0) -> TFloat: """ Returns `self` rounded to the given number of decimal digits. @@ -804,11 +804,11 @@ def round(self, maxdd : int = 0) -> TFloat: """ from ..factory import _TemporalFactory return _TemporalFactory.create_temporal(tfloat_round(self._inner, - maxdd)) + maxdd)) # ------------------------- Split Operations ------------------------------ def value_split(self, size: float, start: Optional[float] = 0) -> \ - List[Temporal]: + List[Temporal]: """ Splits `self` into fragments with respect to value buckets @@ -825,7 +825,7 @@ def value_split(self, size: float, start: Optional[float] = 0) -> \ tiles, new_count = tfloat_value_split(self._inner, size, start) from ..factory import _TemporalFactory return [_TemporalFactory.create_temporal(tiles[i]) for i in \ - range(new_count)] + range(new_count)] def time_value_split(self, value_start: float, value_size: float, time_start: Union[str, datetime], @@ -852,7 +852,7 @@ def time_value_split(self, value_start: float, value_size: float, if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) tiles, new_count = tfloat_value_time_split(self._inner, value_size, - value_start, dt, st) + value_start, dt, st) return [Temporal._factory(tiles[i]) for i in range(new_count)] # ------------------------- Database Operations --------------------------- @@ -884,7 +884,7 @@ def read_from_cursor(value, _=None): class TFloatInst(TInstant[float, 'TFloat', 'TFloatInst', 'TFloatSeq', - 'TFloatSeqSet'], TFloat): +'TFloatSeqSet'], TFloat): """ Class for representing temporal floats at a single instant. """ @@ -895,11 +895,11 @@ def __init__(self, string: Optional[str] = None, *, value: Optional[Union[str, float]] = None, timestamp: Optional[Union[str, datetime]] = None, _inner=None): super().__init__(string=string, value=value, timestamp=timestamp, - _inner=_inner) + _inner=_inner) class TFloatSeq(TSequence[float, 'TFloat', 'TFloatInst', 'TFloatSeq', - 'TFloatSeqSet'], TFloat): +'TFloatSeqSet'], TFloat): """ Class for representing temporal floats over a period of time. """ @@ -918,7 +918,7 @@ def __init__(self, string: Optional[str] = None, *, class TFloatSeqSet(TSequenceSet[float, 'TFloat', 'TFloatInst', 'TFloatSeq', - 'TFloatSeqSet'], TFloat): +'TFloatSeqSet'], TFloat): """ Class for representing temporal floats over a period of time with gaps. """ @@ -928,4 +928,4 @@ def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, TFloatSeq]]] = None, normalize: bool = True, _inner=None): super().__init__(string=string, sequence_list=sequence_list, - normalize=normalize, _inner=_inner) + normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index 3b82ac7f..2757fa9b 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -76,15 +76,15 @@ def from_base_time(value: int, base: Time) -> TInt: """ if isinstance(base, datetime): return TIntInst(_inner=tintinst_make(value, - datetime_to_timestamptz(base))) + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): return TIntSeq(_inner=tintseq_from_base_timestampset(value, - base._inner)) + base._inner)) elif isinstance(base, Period): return TIntSeq(_inner=tintseq_from_base_period(value, base._inner)) elif isinstance(base, PeriodSet): return TIntSeqSet(_inner=tintseqset_from_base_periodset(value, - base._inner)) + base._inner)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Output ---------------------------------------- @@ -241,7 +241,7 @@ def value_at_timestamp(self, timestamp) -> int: tint_value_at_timestamp """ return tint_value_at_timestamp(self._inner, - datetime_to_timestamptz(timestamp), True) + datetime_to_timestamptz(timestamp), True) # ------------------------- Ever and Always Comparisons ------------------- def always_less(self, value: int) -> bool: @@ -658,8 +658,8 @@ def temporal_greater(self, other: Union[int, Temporal]) -> Temporal: # ------------------------- Restrictions ---------------------------------- def at(self, other: Union[int, List[int], - intrange, floatrange, List[intrange], List[floatrange], TBox, - datetime, TimestampSet, Period, PeriodSet]) -> Temporal: + intrange, floatrange, List[intrange], List[floatrange], TBox, + datetime, TimestampSet, Period, PeriodSet]) -> Temporal: """ Returns a new temporal int with th e values of `self` restricted to the time or value `other`. @@ -683,8 +683,8 @@ def at(self, other: Union[int, List[int], return Temporal._factory(result) def minus(self, other: Union[int, List[int], - intrange, floatrange, List[intrange], List[floatrange], TBox, - datetime, TimestampSet, Period, PeriodSet]) -> Temporal: + intrange, floatrange, List[intrange], List[floatrange], TBox, + datetime, TimestampSet, Period, PeriodSet]) -> Temporal: """ Returns a new temporal int with the values of `self` restricted to the complement of the time or value `other`. @@ -710,7 +710,7 @@ def minus(self, other: Union[int, List[int], # ------------------------- Distance -------------------------------------- def nearest_approach_distance(self, - other: Union[int, float, TNumber, TBox]) -> float: + other: Union[int, float, TNumber, TBox]) -> float: """ Returns the nearest approach distance between `self` and `other`. @@ -776,7 +776,7 @@ def time_value_split(self, value_start: int, value_size: int, if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) tiles, new_count = tint_value_time_split(self._inner, value_size, - value_start, dt, st) + value_start, dt, st) return [Temporal._factory(tiles[i]) for i in range(new_count)] # ------------------------- Database Operations --------------------------- @@ -812,7 +812,7 @@ def __init__(self, string: Optional[str] = None, *, value: Optional[Union[str, int]] = None, timestamp: Optional[Union[str, datetime]] = None, _inner=None): super().__init__(string=string, value=value, timestamp=timestamp, - _inner=_inner) + _inner=_inner) class TIntSeq(TSequence[int, 'TInt', 'TIntInst', 'TIntSeq', 'TIntSeqSet'], TInt): @@ -828,19 +828,19 @@ def __init__(self, string: Optional[str] = None, *, interpolation: TInterpolation = TInterpolation.STEPWISE, normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, - lower_inc=lower_inc, upper_inc=upper_inc, expandable=expandable, - interpolation=interpolation, normalize=normalize, _inner=_inner) + lower_inc=lower_inc, upper_inc=upper_inc, expandable=expandable, + interpolation=interpolation, normalize=normalize, _inner=_inner) class TIntSeqSet(TSequenceSet[int, 'TInt', 'TIntInst', 'TIntSeq', 'TIntSeqSet'], - TInt): + TInt): """ Class for representing temporal integers over a period of time with gaps. """ ComponentClass = TIntSeq def __init__(self, string: Optional[str] = None, *, - sequence_list: Optional[List[Union[str, TIntSeq]]] = None, - normalize: bool = True, _inner=None): + sequence_list: Optional[List[Union[str, TIntSeq]]] = None, + normalize: bool = True, _inner=None): super().__init__(string=string, sequence_list=sequence_list, - normalize=normalize, _inner=_inner) + normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 7e38301b..3ef1c845 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -372,7 +372,7 @@ def time_weighted_centroid(self, precision: int = 15) -> shp.Point: return gserialized_to_shapely_geometry(tpoint_twcentroid(self._inner), precision) # type: ignore # ------------------------- Spatial Reference System ---------------------- - def srid(self) -> int: + def srid(self) -> int: """ Returns the SRID. @@ -393,7 +393,7 @@ def set_srid(self: Self, srid: int) -> Self: return self.__class__(_inner=tpoint_set_srid(self._inner, srid)) # ------------------------- Transformations ------------------------------- - def round(self, maxdd : int = 0) -> TPoint: + def round(self, maxdd: int = 0) -> TPoint: """ Round the coordinate values to a number of decimal places. @@ -1165,11 +1165,11 @@ def to_dataframe(self, precision: int = 15) -> GeoDataFrame: """ sequences = self.sequences() data = { - 'sequence': [i + 1 for i, seq in enumerate(sequences) for _ in - range(seq.num_instants())], + 'sequence': [i + 1 for i, seq in enumerate(sequences) for _ in + range(seq.num_instants())], 'time': [t for seq in sequences for t in seq.timestamps()], - 'geometry': [v for seq in sequences for v in - seq.values(precision=precision)] + 'geometry': [v for seq in sequences for v in + seq.values(precision=precision)] } return GeoDataFrame(data, crs=self.srid()).set_index(keys=['sequence', 'time']) @@ -1192,17 +1192,20 @@ def plot(self, *args, **kwargs): class TGeomPoint(TPoint['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', - 'TGeomPointSeqSet'], ABC): +'TGeomPointSeqSet'], ABC): """ Abstract class for temporal geometric points. """ + + _mobilitydb_name = 'tgeompoint' + BaseClass = shp.Point _parse_function = tgeompoint_in # ------------------------- Output ---------------------------------------- @staticmethod def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], - base: Temporal) -> TGeomPoint: + base: Temporal) -> TGeomPoint: """ Creates a temporal geometric point from a base geometry and the time frame of another temporal object. @@ -1224,19 +1227,19 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], @staticmethod @overload def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], - base: datetime) -> TGeomPointInst: + base: datetime) -> TGeomPointInst: ... @staticmethod @overload def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], - base: Union[TimestampSet, Period]) -> TGeomPointSeq: + base: Union[TimestampSet, Period]) -> TGeomPointSeq: ... @staticmethod @overload def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], - base: PeriodSet) -> TGeomPointSeqSet: + base: PeriodSet) -> TGeomPointSeqSet: ... @staticmethod @@ -1260,16 +1263,16 @@ def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Time, gs = geometry_to_gserialized(value) if isinstance(base, datetime): return TGeomPointInst(_inner=tpointinst_make(gs, - datetime_to_timestamptz(base))) + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): return TGeomPointSeq(_inner=tpointseq_from_base_timestampset(gs, - base._inner)) + base._inner)) elif isinstance(base, Period): return TGeomPointSeq(_inner=tpointseq_from_base_period(gs, - base._inner, interpolation)) + base._inner, interpolation)) elif isinstance(base, PeriodSet): return TGeomPointSeqSet(_inner=tpointseqset_from_base_periodset(gs, - base._inner, interpolation)) + base._inner, interpolation)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Conversions ---------------------------------- @@ -1301,7 +1304,7 @@ def to_shapely_geometry(self, precision: int = 15) -> shpb.BaseGeometry: gserialized_to_shapely_geometry """ return gserialized_to_shapely_geometry(tpoint_trajectory(self._inner), - precision) + precision) def to_dataframe(self) -> GeoDataFrame: """ @@ -1435,7 +1438,7 @@ def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: return Temporal._factory(result) def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) \ - -> Temporal: + -> Temporal: """ Returns the temporal inequality relation between `self` and `other`. @@ -1484,17 +1487,20 @@ def read_from_cursor(value, _=None): class TGeogPoint(TPoint['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', - 'TGeogPointSeqSet'], ABC): +'TGeogPointSeqSet'], ABC): """ Abstract class for representing temporal geographic points. """ + + _mobilitydb_name = 'tgeogpoint' + BaseClass = shp.Point _parse_function = tgeogpoint_in # ------------------------- Output ---------------------------------------- @staticmethod def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], - base: Temporal) -> TGeogPoint: + base: Temporal) -> TGeogPoint: """ Creates a temporal geographic point from a base geometry and the time frame of another temporal object. @@ -1516,19 +1522,19 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], @staticmethod @overload def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], - base: datetime) -> TGeogPointInst: + base: datetime) -> TGeogPointInst: ... @staticmethod @overload def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], - base: Union[TimestampSet, Period]) -> TGeogPointSeq: + base: Union[TimestampSet, Period]) -> TGeogPointSeq: ... @staticmethod @overload def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], - base: PeriodSet) -> TGeogPointSeqSet: + base: PeriodSet) -> TGeogPointSeqSet: ... @staticmethod @@ -1552,16 +1558,16 @@ def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Time, gs = geography_to_gserialized(value) if isinstance(base, datetime): return TGeogPointInst(_inner=tpointinst_make(gs, - datetime_to_timestamptz(base))) + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): return TGeogPointSeq(_inner=tpointseq_from_base_timestampset(gs, - base._inner)) + base._inner)) elif isinstance(base, Period): return TGeogPointSeq(_inner=tpointseq_from_base_period(gs, - base._inner, interpolation)) + base._inner, interpolation)) elif isinstance(base, PeriodSet): return TGeogPointSeqSet(_inner=tpointseqset_from_base_periodset(gs, - base._inner, interpolation)) + base._inner, interpolation)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Conversions ---------------------------------- @@ -1745,7 +1751,7 @@ def read_from_cursor(value, _=None): class TGeomPointInst(TPointInst['TGeomPoint', 'TGeomPointInst', - 'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): +'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): """ Class for representing temporal geometric points at a single instant. """ @@ -1757,13 +1763,13 @@ def __init__(self, string: Optional[str] = None, *, timestamp: Optional[Union[str, datetime]] = None, srid: Optional[int] = 0, _inner=None) -> None: super().__init__(string=string, value=point, timestamp=timestamp, - _inner=_inner) + _inner=_inner) if self._inner is None: self._inner = tgeompoint_in(f"SRID={srid};{point}@{timestamp}") class TGeogPointInst(TPointInst['TGeogPoint', 'TGeogPointInst', - 'TGeogPointSeq', 'TGeogPointSeqSet'], TGeogPoint): +'TGeogPointSeq', 'TGeogPointSeqSet'], TGeogPoint): """ Class for representing temporal geographic points at a single instant. """ @@ -1776,7 +1782,7 @@ def __init__(self, string: Optional[str] = None, *, timestamp: Optional[Union[str, datetime]] = None, srid: Optional[int] = 4326, _inner=None) -> None: super().__init__(string=string, value=point, timestamp=timestamp, - _inner=_inner) + _inner=_inner) if self._inner is None: p = f'POINT({point[0]} {point[1]})' if isinstance(point, tuple) \ else f'{point}' @@ -1784,7 +1790,7 @@ def __init__(self, string: Optional[str] = None, *, class TGeomPointSeq(TPointSeq['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', - 'TGeomPointSeqSet'], TGeomPoint): +'TGeomPointSeqSet'], TGeomPoint): """ Class for representing temporal geometric points over a period of time. """ @@ -1797,24 +1803,24 @@ def __init__(self, string: Optional[str] = None, *, interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, - lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, - normalize=normalize, _inner=_inner) + lower_inc=lower_inc, upper_inc=upper_inc, + expandable=expandable, interpolation=interpolation, + normalize=normalize, _inner=_inner) class TGeogPointSeq(TPointSeq['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', - 'TGeogPointSeqSet'], TGeogPoint): +'TGeogPointSeqSet'], TGeogPoint): """ Class for representing temporal geographic points over a period of time. """ ComponentClass = TGeogPointInst - def __init__(self, string: Optional[str] = None, *, - instant_list: Optional[List[Union[str, TGeogPointInst]]] = None, - lower_inc: bool = True, upper_inc: bool = False, - expandable: Union[bool, int] = False, - interpolation: TInterpolation = TInterpolation.LINEAR, - normalize: bool = True, _inner=None): + def __init__(self, string: Optional[str] = None, *, + instant_list: Optional[List[Union[str, TGeogPointInst]]] = None, + lower_inc: bool = True, upper_inc: bool = False, + expandable: Union[bool, int] = False, + interpolation: TInterpolation = TInterpolation.LINEAR, + normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, expandable=expandable, interpolation=interpolation, @@ -1822,7 +1828,7 @@ def __init__(self, string: Optional[str] = None, *, class TGeomPointSeqSet(TPointSeqSet['TGeomPoint', 'TGeomPointInst', - 'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): +'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): """ Class for representing temporal geometric points over a period of time with gaps. @@ -1831,14 +1837,14 @@ class TGeomPointSeqSet(TPointSeqSet['TGeomPoint', 'TGeomPointInst', def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, TGeomPointSeq]]] = None, - expandable: Union[bool, int] = False, + expandable: Union[bool, int] = False, normalize: bool = True, _inner=None): - super().__init__(string=string, sequence_list=sequence_list, + super().__init__(string=string, sequence_list=sequence_list, expandable=expandable, normalize=normalize, _inner=_inner) class TGeogPointSeqSet(TPointSeqSet['TGeogPoint', 'TGeogPointInst', - 'TGeogPointSeq', 'TGeogPointSeqSet'], TGeogPoint): +'TGeogPointSeq', 'TGeogPointSeqSet'], TGeogPoint): """ Class for representing temporal geographic points over a period of time with gaps. @@ -1847,8 +1853,8 @@ class TGeogPointSeqSet(TPointSeqSet['TGeogPoint', 'TGeogPointInst', def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, TGeogPointSeq]]] = None, - expandable: Union[bool, int] = False, + expandable: Union[bool, int] = False, normalize: bool = True, _inner=None): - super().__init__(string=string, sequence_list=sequence_list, - expandable=expandable, normalize=normalize, + super().__init__(string=string, sequence_list=sequence_list, + expandable=expandable, normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/ttext.py b/pymeos/pymeos/main/ttext.py index 1280e6b7..ef66b752 100644 --- a/pymeos/pymeos/main/ttext.py +++ b/pymeos/pymeos/main/ttext.py @@ -51,7 +51,7 @@ def from_base_time(value: str, base: datetime) -> TTextInst: @staticmethod @overload def from_base_time(value: str, base: Union[TimestampSet, Period]) -> \ - TTextSeq: + TTextSeq: ... @staticmethod @@ -77,16 +77,16 @@ def from_base_time(value: str, base: Time) -> TText: """ if isinstance(base, datetime): return TTextInst(_inner=ttextinst_make(value, - datetime_to_timestamptz(base))) + datetime_to_timestamptz(base))) elif isinstance(base, TimestampSet): return TTextSeq(_inner=ttextseq_from_base_timestampset(value, - base._inner)) + base._inner)) elif isinstance(base, Period): return TTextSeq(_inner=ttextseq_from_base_period(value, - base._inner)) + base._inner)) elif isinstance(base, PeriodSet): return TTextSeqSet(_inner=ttextseqset_from_base_periodset(value, - base._inner)) + base._inner)) raise TypeError(f'Operation not supported with type {base.__class__}') # ------------------------- Output ---------------------------------------- @@ -216,7 +216,7 @@ def value_at_timestamp(self, timestamp: datetime) -> str: ttext_value_at_timestamp """ result = ttext_value_at_timestamp(self._inner, - datetime_to_timestamptz(timestamp), True) + datetime_to_timestamptz(timestamp), True) return text2cstring(result[0]) # ------------------------- Ever and Always Comparisons ------------------- @@ -635,7 +635,7 @@ def temporal_greater_or_equal(self, other: Union[str, Temporal]) -> Temporal: # ------------------------- Restrictions ---------------------------------- def at(self, other: Union[str, List[str], datetime, TimestampSet, Period, - PeriodSet]) -> TText: + PeriodSet]) -> TText: """ Returns a new temporal string with the values of `self` restricted to the time or value `other`. @@ -659,7 +659,7 @@ def at(self, other: Union[str, List[str], datetime, TimestampSet, Period, return Temporal._factory(result) def minus(self, other: Union[str, List[str], datetime, TimestampSet, - Period, PeriodSet]) -> TText: + Period, PeriodSet]) -> TText: """ Returns a new temporal string with the values of `self` restricted to the complement of the time or value `other`. @@ -768,7 +768,7 @@ def read_from_cursor(value, _=None): class TTextInst(TInstant[str, 'TText', 'TTextInst', 'TTextSeq', - 'TTextSeqSet'], TText): +'TTextSeqSet'], TText): """ Class for representing temporal strings at a single instant. """ @@ -780,11 +780,11 @@ def __init__(self, string: Optional[str] = None, *, timestamp: Optional[Union[str, datetime]] = None, _inner=None): super().__init__(string=string, value=value, timestamp=timestamp, - _inner=_inner) + _inner=_inner) class TTextSeq(TSequence[str, 'TText', 'TTextInst', 'TTextSeq', - 'TTextSeqSet'], TText): +'TTextSeqSet'], TText): """ Class for representing temporal strings over a period of time. """ @@ -797,20 +797,20 @@ def __init__(self, string: Optional[str] = None, *, interpolation: TInterpolation = TInterpolation.STEPWISE, normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, - lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, - normalize=normalize, _inner=_inner) + lower_inc=lower_inc, upper_inc=upper_inc, + expandable=expandable, interpolation=interpolation, + normalize=normalize, _inner=_inner) class TTextSeqSet(TSequenceSet[str, 'TText', 'TTextInst', 'TTextSeq', - 'TTextSeqSet'], TText): +'TTextSeqSet'], TText): """ Class for representing temporal strings over a period of time with gaps. """ ComponentClass = TTextSeq def __init__(self, string: Optional[str] = None, *, - sequence_list: Optional[List[Union[str, TTextSeq]]] = None, - normalize: bool = True, _inner=None): + sequence_list: Optional[List[Union[str, TTextSeq]]] = None, + normalize: bool = True, _inner=None): super().__init__(string=string, sequence_list=sequence_list, - normalize=normalize, _inner=_inner) + normalize=normalize, _inner=_inner) diff --git a/pymeos/tests/collections/text/__init__.py b/pymeos/tests/collections/text/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymeos/tests/collections/text/textset_test.py b/pymeos/tests/collections/text/textset_test.py new file mode 100644 index 00000000..8bfd4e87 --- /dev/null +++ b/pymeos/tests/collections/text/textset_test.py @@ -0,0 +1,273 @@ +from copy import copy +from datetime import datetime, timezone, timedelta +from typing import List + +import pytest + +from pymeos import TextSet +from tests.conftest import TestPyMEOS + + +class TestTextSet(TestPyMEOS): + tset = TextSet('{A, BB, ccc}') + + @staticmethod + def assert_textset_equality(tset: TextSet, + elements: List[str]): + assert tset.num_elements() == len(elements) + assert tset.elements() == elements + + +class TestTextSetConstructors(TestTextSet): + + def test_string_constructor(self): + self.assert_textset_equality(self.tset, ['A', 'BB', 'ccc']) + + def test_list_constructor(self): + ts_set = TextSet(elements=['A', 'BB', 'ccc']) + self.assert_textset_equality(ts_set, ['A', 'BB', 'ccc']) + + def test_hexwkb_constructor(self): + ts_set = TextSet.from_hexwkb( + '011A000103000000020000000000000041000300000000000000424200040000000000000063636300') + self.assert_textset_equality(ts_set, ['A', 'BB', 'ccc']) + + def test_from_as_constructor(self): + assert self.tset == TextSet(str(self.tset)) + assert self.tset == TextSet.from_wkb(self.tset.as_wkb()) + assert self.tset == TextSet.from_hexwkb(self.tset.as_hexwkb()) + + def test_copy_constructor(self): + ts_set_copy = copy(self.tset) + assert self.tset == ts_set_copy + assert self.tset is not ts_set_copy + + +class TestTextSetOutputs(TestTextSet): + + def test_str(self): + assert str(self.tset) == '{"A", "BB", "ccc"}' + + def test_repr(self): + assert repr(self.tset) == 'TextSet({"A", "BB", "ccc"})' + + def test_as_hexwkb(self): + assert self.tset.as_hexwkb() == ('011A00010300000002000000000000004' + '1000300000000000000424200040000000000000063636300') + + +class TestTimestampConversions(TestTextSet): + + def test_to_spanset(self): + with pytest.raises(NotImplementedError): + self.tset.to_spanset() + + def test_to_span(self): + with pytest.raises(NotImplementedError): + self.tset.to_span() + + +class TestTextSetAccessors(TestTextSet): + + def test_num_elements(self): + assert self.tset.num_elements() == 3 + assert len(self.tset) == 3 + + def test_start_element(self): + assert self.tset.start_element() == 'A' + + def test_end_element(self): + assert self.tset.end_element() == 'ccc' + + def test_element_n(self): + assert self.tset.element_n(1) == 'BB' + + def test_element_n_out_of_range(self): + with pytest.raises(IndexError): + self.tset.element_n(3) + + def test_elements(self): + assert self.tset.elements() == ['A', 'BB', 'ccc', ] + + def test_hash(self): + assert hash(self.tset) == 3145376687 + + +class TestTextSetPositionFunctions(TestTextSet): + string = 'A' + other = TextSet('{a, BB, ccc}') + # period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') + # periodset = PeriodSet( + # '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') + # timestamp = datetime(year=2020, month=1, day=1) + # TextSet = TextSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') + # instant = TFloatInst('1.0@2020-01-01') + # discrete_sequence = TFloatSeq('{1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31}') + # stepwise_sequence = TFloatSeq('Interp=Step;(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') + # continuous_sequence = TFloatSeq('(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') + # sequence_set = TFloatSeqSet('{(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31], ' + # '(1.0@2021-01-01, 3.0@2021-01-10, 10.0@2021-01-20, 0.0@2021-01-31]}') + + @pytest.mark.parametrize( + 'other', + [string, other], + ids=['string', 'TextSet'] + ) + def test_is_adjacent(self, other): + self.tset.is_adjacent(other) + + # @pytest.mark.parametrize( + # 'other', + # [TextSet, period, periodset, instant, discrete_sequence, stepwise_sequence, continuous_sequence, + # sequence_set], + # ids=['TextSet', 'period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', + # 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_contained_in(self, other): + # self.tset.is_contained_in(other) + # + # @pytest.mark.parametrize( + # 'other', + # [timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence], + # ids=['timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set'] + # ) + # def test_contains(self, other): + # self.tset.contains(other) + # _ = other in self.tset + # + # # + # @pytest.mark.parametrize( + # 'other', + # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence], + # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_overlaps(self, other): + # self.tset.overlaps(other) + # + # @pytest.mark.parametrize( + # 'other', + # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence], + # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_same(self, other): + # self.periodset.is_same(other) + # + # @pytest.mark.parametrize( + # 'other', + # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence], + # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_before(self, other): + # self.tset.is_before(other) + # + # @pytest.mark.parametrize( + # 'other', + # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence], + # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_over_or_before(self, other): + # self.tset.is_over_or_before(other) + # + # @pytest.mark.parametrize( + # 'other', + # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence], + # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_after(self, other): + # self.tset.is_after(other) + # + # @pytest.mark.parametrize( + # 'other', + # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence], + # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_over_or_after(self, other): + # self.tset.is_over_or_after(other) + # + # @pytest.mark.parametrize( + # 'other', + # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence], + # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_distance(self, other): + # self.tset.distance(other) + + +class TestTextSetSetFunctions(TestTextSet): + string = 'A' + other = TextSet('{a, BB, ccc}') + + @pytest.mark.parametrize( + 'other, expected', + [(string, 'A'), + (other, TextSet('{BB, ccc}'))], + ids=['string', 'TextSet'] + ) + def test_intersection(self, other, expected): + assert self.tset.intersection(other) == expected + assert self.tset * other == expected + + @pytest.mark.parametrize( + 'other, expected', + [(string, TextSet('{A, BB, ccc}')), + (other, TextSet('{A, a, BB, ccc}'))], + ids=['string', 'TextSet'] + ) + def test_union(self, other, expected): + assert self.tset.union(other) == expected + assert self.tset + other == expected + + @pytest.mark.parametrize( + 'other, expected', + [(string, TextSet('{BB, ccc}')), + (other, TextSet('{A}'))], + ids=['string', 'TextSet'] + ) + def test_minus(self, other, expected): + assert self.tset.minus(other) == expected + assert self.tset - other == expected + + +class TestTextSetComparisons(TestTextSet): + other = TextSet('{2020-01-02 00:00:00+0, 2020-03-31 00:00:00+0}') + + def test_eq(self): + _ = self.tset == self.other + + def test_ne(self): + _ = self.tset != self.other + + def test_lt(self): + _ = self.tset < self.other + + def test_le(self): + _ = self.tset <= self.other + + def test_gt(self): + _ = self.tset > self.other + + def test_ge(self): + _ = self.tset >= self.other + + +class TestTextSetMiscFunctions(TestTextSet): + + def test_hash(self): + hash(self.tset) diff --git a/pymeos_cffi/docker/MobilityDB.Dockerfile b/pymeos_cffi/docker/MobilityDB.Dockerfile new file mode 100644 index 00000000..4a1d064f --- /dev/null +++ b/pymeos_cffi/docker/MobilityDB.Dockerfile @@ -0,0 +1,10 @@ +FROM debian:buster-slim + +RUN apt install build-essential cmake postgresql-server-dev-11 liblwgeom-dev libproj-dev libjson-c-dev libprotobuf-c-dev + +RUN git clone --branch develop https://github.com/MobilityDB/MobilityDB +WORKDIR MobilityDB/build + + + + diff --git a/pymeos_examples/PyMEOS Examples/AIS.ipynb b/pymeos_examples/PyMEOS Examples/AIS.ipynb index 23ed2d20..813a3f97 100644 --- a/pymeos_examples/PyMEOS Examples/AIS.ipynb +++ b/pymeos_examples/PyMEOS Examples/AIS.ipynb @@ -34,8 +34,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:54:53.346701400Z", - "start_time": "2023-09-26T08:54:52.664004300Z" + "end_time": "2023-09-26T09:14:39.780045600Z", + "start_time": "2023-09-26T09:14:39.136882600Z" } } }, @@ -80,8 +80,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:54:56.235929900Z", - "start_time": "2023-09-26T08:54:56.093929900Z" + "end_time": "2023-09-26T09:14:39.883043Z", + "start_time": "2023-09-26T09:14:39.773045900Z" } } }, @@ -118,8 +118,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:55:02.898319900Z", - "start_time": "2023-09-26T08:54:58.467648800Z" + "end_time": "2023-09-26T09:14:43.943454700Z", + "start_time": "2023-09-26T09:14:39.900042800Z" } } }, @@ -140,8 +140,8 @@ "outputs": [ { "data": { - "text/plain": " trajectory \\\nmmsi \n219001559 [POINT(57.592245 9.975512)@2021-01-08 00:00:05... \n219027804 [POINT(55.94244 11.866278)@2021-01-08 00:00:01... \n257136000 [POINT(56.911257 7.122958)@2021-01-08 00:02:57... \n265513270 [POINT(57.059 12.272388)@2021-01-08 00:00:00+0... \n566948000 [POINT(55.574352 4.617153)@2021-01-08 00:00:04... \n\n sog distance \nmmsi \n219001559 [0.1@2021-01-08 00:00:05+01, 0.1@2021-01-08 00... 17765.588739 \n219027804 [0@2021-01-08 00:00:01+01, 0@2021-01-08 00:34:... 94963.575026 \n257136000 [14@2021-01-08 00:02:57+01, 13.9@2021-01-08 00... 940404.131239 \n265513270 [0@2021-01-08 00:00:00+01, 0@2021-01-08 00:00:... 1629.539165 \n566948000 [0.5@2021-01-08 00:00:04+01, 0.5@2021-01-08 00... 28215.190430 ", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
trajectorysogdistance
mmsi
219001559[POINT(57.592245 9.975512)@2021-01-08 00:00:05...[0.1@2021-01-08 00:00:05+01, 0.1@2021-01-08 00...17765.588739
219027804[POINT(55.94244 11.866278)@2021-01-08 00:00:01...[0@2021-01-08 00:00:01+01, 0@2021-01-08 00:34:...94963.575026
257136000[POINT(56.911257 7.122958)@2021-01-08 00:02:57...[14@2021-01-08 00:02:57+01, 13.9@2021-01-08 00...940404.131239
265513270[POINT(57.059 12.272388)@2021-01-08 00:00:00+0...[0@2021-01-08 00:00:00+01, 0@2021-01-08 00:00:...1629.539165
566948000[POINT(55.574352 4.617153)@2021-01-08 00:00:04...[0.5@2021-01-08 00:00:04+01, 0.5@2021-01-08 00...28215.190430
\n
" + "text/plain": " trajectory \\\nmmsi \n219001559 [POINT(57.592245 9.975512)@2021-01-08 00:00:05... \n219027804 [POINT(55.94244 11.866278)@2021-01-08 00:00:01... \n257136000 [POINT(56.911257 7.122958)@2021-01-08 00:02:57... \n265513270 [POINT(57.059 12.272388)@2021-01-08 00:00:00+0... \n566948000 [POINT(55.574352 4.617153)@2021-01-08 00:00:04... \n\n sog distance \nmmsi \n219001559 [0.1@2021-01-08 00:00:05+01, 0.1@2021-01-08 00... 17765.588739 \n219027804 [0@2021-01-08 00:00:01+01, 0@2021-01-08 00:34:... 94963.575024 \n257136000 [14@2021-01-08 00:02:57+01, 13.9@2021-01-08 00... 940404.131214 \n265513270 [0@2021-01-08 00:00:00+01, 0@2021-01-08 00:00:... 1629.539165 \n566948000 [0.5@2021-01-08 00:00:04+01, 0.5@2021-01-08 00... 28215.190429 ", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
trajectorysogdistance
mmsi
219001559[POINT(57.592245 9.975512)@2021-01-08 00:00:05...[0.1@2021-01-08 00:00:05+01, 0.1@2021-01-08 00...17765.588739
219027804[POINT(55.94244 11.866278)@2021-01-08 00:00:01...[0@2021-01-08 00:00:01+01, 0@2021-01-08 00:34:...94963.575024
257136000[POINT(56.911257 7.122958)@2021-01-08 00:02:57...[14@2021-01-08 00:02:57+01, 13.9@2021-01-08 00...940404.131214
265513270[POINT(57.059 12.272388)@2021-01-08 00:00:00+0...[0@2021-01-08 00:00:00+01, 0@2021-01-08 00:00:...1629.539165
566948000[POINT(55.574352 4.617153)@2021-01-08 00:00:04...[0.5@2021-01-08 00:00:04+01, 0.5@2021-01-08 00...28215.190429
\n
" }, "execution_count": 4, "metadata": {}, @@ -161,8 +161,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:55:03.452322100Z", - "start_time": "2023-09-26T08:55:02.850797400Z" + "end_time": "2023-09-26T09:14:44.673517300Z", + "start_time": "2023-09-26T09:14:43.938455900Z" } } }, @@ -197,8 +197,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:55:03.892692Z", - "start_time": "2023-09-26T08:55:03.737322100Z" + "end_time": "2023-09-26T09:14:44.674518200Z", + "start_time": "2023-09-26T09:14:44.619518100Z" } } }, @@ -234,8 +234,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:55:10.720449900Z", - "start_time": "2023-09-26T08:55:05.774912600Z" + "end_time": "2023-09-26T09:14:49.684675500Z", + "start_time": "2023-09-26T09:14:44.633516300Z" } } }, @@ -287,8 +287,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:55:15.168755900Z", - "start_time": "2023-09-26T08:55:15.131758Z" + "end_time": "2023-09-26T09:14:49.742716800Z", + "start_time": "2023-09-26T09:14:49.676674700Z" } } }, @@ -314,8 +314,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:55:17.530081800Z", - "start_time": "2023-09-26T08:55:17.516083700Z" + "end_time": "2023-09-26T09:14:49.743717700Z", + "start_time": "2023-09-26T09:14:49.713716200Z" } } }, @@ -341,8 +341,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:55:20.000617300Z", - "start_time": "2023-09-26T08:55:19.246090300Z" + "end_time": "2023-09-26T09:14:50.483807800Z", + "start_time": "2023-09-26T09:14:49.716717300Z" } } }, @@ -366,8 +366,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:55:21.021821200Z", - "start_time": "2023-09-26T08:55:20.928825600Z" + "end_time": "2023-09-26T09:14:50.571835900Z", + "start_time": "2023-09-26T09:14:50.477807800Z" } } }, @@ -394,8 +394,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:55:24.320559400Z", - "start_time": "2023-09-26T08:55:22.912704800Z" + "end_time": "2023-09-26T09:14:51.926130500Z", + "start_time": "2023-09-26T09:14:50.579836200Z" } } }, @@ -420,8 +420,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-09-26T08:55:25.827246600Z", - "start_time": "2023-09-26T08:55:25.810248300Z" + "end_time": "2023-09-26T09:14:51.978130400Z", + "start_time": "2023-09-26T09:14:51.920528300Z" } } } From 3d4b78b2338b52e017b7b8f629402894935a0396 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 28 Sep 2023 09:36:02 +0200 Subject: [PATCH 047/101] Improve tests --- pymeos/pymeos/collections/text/textset.py | 26 +++++++++++++++++++++++ pymeos_cffi/pymeos_cffi/__init__.py | 2 ++ pymeos_cffi/pymeos_cffi/builder/meos.h | 2 ++ pymeos_cffi/pymeos_cffi/functions.py | 14 ++++++++++++ 4 files changed, 44 insertions(+) diff --git a/pymeos/pymeos/collections/text/textset.py b/pymeos/pymeos/collections/text/textset.py index 892404a6..1a9dc43a 100644 --- a/pymeos/pymeos/collections/text/textset.py +++ b/pymeos/pymeos/collections/text/textset.py @@ -108,6 +108,32 @@ def elements(self): elems = textset_values(self._inner) return [elems[i] for i in range(self.num_elements())] + + # ------------------------- Transformations -------------------------------- + def lowercase(self): + """ + Returns a new textset that is the result of appling uppercase to ``self`` + + Returns: + A :class:`str` instance + + MEOS Functions: + textset_lowercase + """ + return textset_lowercase(self._inner) + + def uppercase(self): + """ + Returns a new textset that is the result of appling uppercase to ``self`` + + Returns: + A :class:`str` instance + + MEOS Functions: + textset_uppercase + """ + return textset_uppercase(self._inner) + # ------------------------- Set Operations -------------------------------- @overload diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 0efe0b74..b9ba0247 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -259,6 +259,8 @@ 'period_tprecision', 'periodset_shift_scale', 'periodset_tprecision', + 'textset_lowercase', + 'textset_uppercase', 'timestamp_tprecision', 'timestampset_shift_scale', 'intersection_bigintset_bigint', diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 07f6acc5..fa5c85ca 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -980,6 +980,8 @@ extern Span *period_shift_scale(const Span *p, const Interval *shift, const Inte extern Span *period_tprecision(const Span *s, const Interval *duration, TimestampTz torigin); extern SpanSet *periodset_shift_scale(const SpanSet *ss, const Interval *shift, const Interval *duration); extern SpanSet *periodset_tprecision(const SpanSet *ss, const Interval *duration, TimestampTz torigin); +extern Set *textset_lowercase(const Set *s); +extern Set *textset_uppercase(const Set *s); extern TimestampTz timestamp_tprecision(TimestampTz t, const Interval *duration, TimestampTz torigin); extern Set *timestampset_shift_scale(const Set *ts, const Interval *shift, const Interval *duration); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 9a7396c7..ab9acb76 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -1732,6 +1732,20 @@ def periodset_tprecision(ss: 'const SpanSet *', duration: 'const Interval *', to return result if result != _ffi.NULL else None +def textset_lowercase(s: 'const Set *') -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.textset_lowercase(s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def textset_uppercase(s: 'const Set *') -> 'Set *': + s_converted = _ffi.cast('const Set *', s) + result = _lib.textset_uppercase(s_converted) + _check_error() + return result if result != _ffi.NULL else None + + def timestamp_tprecision(t: int, duration: 'const Interval *', torigin: int) -> 'TimestampTz': t_converted = _ffi.cast('TimestampTz', t) duration_converted = _ffi.cast('const Interval *', duration) From 289268253688afb2b9540caf2d22e455809db60d Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 28 Sep 2023 10:16:05 +0200 Subject: [PATCH 048/101] Add missing collections in init --- pymeos/pymeos/collections/__init__.py | 4 +++- pymeos/pymeos/collections/number/__init__.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 pymeos/pymeos/collections/number/__init__.py diff --git a/pymeos/pymeos/collections/__init__.py b/pymeos/pymeos/collections/__init__.py index 5bffaf62..076fbb1f 100644 --- a/pymeos/pymeos/collections/__init__.py +++ b/pymeos/pymeos/collections/__init__.py @@ -9,5 +9,7 @@ 'Set', 'Span', 'SpanSet', 'Time', 'TimestampSet', 'Period', 'PeriodSet', 'datetime', 'timedelta', 'TextSet', - 'GeometrySet', 'GeographySet' + 'GeometrySet', 'GeographySet', + 'IntSet', 'IntSpan', 'IntSpanSet', + 'FloatSet', 'FloatSpan', 'FloatSpanSet' ] diff --git a/pymeos/pymeos/collections/number/__init__.py b/pymeos/pymeos/collections/number/__init__.py new file mode 100644 index 00000000..445e0ef3 --- /dev/null +++ b/pymeos/pymeos/collections/number/__init__.py @@ -0,0 +1,11 @@ +from .intset import IntSet +from .intspan import IntSpan +from .intspanset import IntSpanSet +from .floatset import FloatSet +from .floatspan import FloatSpan +from .floatspanset import FloatSpanSet + +__all__ = [ + 'IntSet', 'IntSpan', 'IntSpanSet', + 'FloatSet', 'FloatSpan', 'FloatSpanSet' +] From f072d57fcc7e3278500b72db8d343eb6b51e67e0 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 28 Sep 2023 10:18:09 +0200 Subject: [PATCH 049/101] Add subtract_from implementation in timestampset --- pymeos/pymeos/collections/time/timestampset.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py index c006d30b..c752b8c4 100644 --- a/pymeos/pymeos/collections/time/timestampset.py +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -637,6 +637,24 @@ def minus(self, other: Union[Time, Temporal, Box]) -> Optional[Time]: else: return super().minus(other) + def subtract_from(self, other: datetime) -> Optional[datetime]: + """ + Returns the difference of ``other`` and ``self``. + + Args: + other: A :class:`datetime` instance + + Returns: + A :class:`datetime` instance or ``None`` if the difference is empty. + + MEOS Functions: + minus_timestamp_timestampset + + See Also: + :meth:`minus` + """ + return timestamptz_to_datetime(minus_timestamp_timestampset(datetime_to_timestamptz(other), self._inner)) + @overload def union(self, other: Union[datetime, TimestampSet]) -> TimestampSet: ... From 4dc2c621ed787e2d0b2718bbb918ad1e73889500 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 28 Sep 2023 10:19:02 +0200 Subject: [PATCH 050/101] Update imports --- pymeos/pymeos/collections/number/floatset.py | 12 ++++++----- pymeos/pymeos/collections/number/floatspan.py | 20 +++++++++---------- pymeos/pymeos/collections/number/intset.py | 5 ++--- pymeos/pymeos/collections/number/intspan.py | 15 ++++++-------- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/pymeos/pymeos/collections/number/floatset.py b/pymeos/pymeos/collections/number/floatset.py index df7f5e35..906b1214 100644 --- a/pymeos/pymeos/collections/number/floatset.py +++ b/pymeos/pymeos/collections/number/floatset.py @@ -2,14 +2,16 @@ from typing import List, Union, overload, Optional -from pymeos_cffi import floatset_in, floatset_make, floatset_out, set_to_spanset, floatset_start_value, floatset_end_value, \ - floatset_value_n, floatset_values, contains_floatset_float, intersection_floatset_float, intersection_set_set, minus_floatset_float, \ - minus_set_set, union_set_set, union_floatset_float, floatset_shift_scale, minus_float_floatset, distance_floatset_float +from pymeos_cffi import floatset_in, floatset_make, floatset_out, floatset_start_value, \ + floatset_end_value, \ + floatset_value_n, floatset_values, contains_floatset_float, intersection_floatset_float, intersection_set_set, \ + minus_floatset_float, \ + minus_set_set, union_set_set, union_floatset_float, floatset_shift_scale, minus_float_floatset, \ + distance_floatset_float -from .. import Span, SpanSet -from ..base import Set from .floatspan import FloatSpan from .floatspanset import FloatSpanSet +from ..base import Set class FloatSet(Set[float]): diff --git a/pymeos/pymeos/collections/number/floatspan.py b/pymeos/pymeos/collections/number/floatspan.py index e896b138..7d6bca88 100644 --- a/pymeos/pymeos/collections/number/floatspan.py +++ b/pymeos/pymeos/collections/number/floatspan.py @@ -1,18 +1,16 @@ from __future__ import annotations -from typing import List, Union, overload, Optional, TYPE_CHECKING - -from pymeos_cffi import floatset_in, floatset_make, floatset_out, set_to_spanset, floatset_start_value, floatset_end_value, \ - floatset_value_n, floatset_values, contains_floatset_float, intersection_floatset_float, intersection_set_set, minus_floatset_float, \ - minus_set_set, union_set_set, union_floatset_float, floatset_shift_scale, minus_float_floatset, distance_floatset_float, \ - floatspan_in, floatspan_lower, floatspan_upper, numspan_shift_scale, contains_floatspan_float, adjacent_floatspan_float, \ - adjacent_span_span, float_to_floatspan, overlaps_span_span, span_eq, left_floatspan_float, overleft_floatspan_float, \ - right_floatspan_float, overright_floatspan_float, intersection_span_span, intersection_spanset_spanset, \ - intersection_spanset_span, minus_floatspan_float, minus_span_span, minus_spanset_span, union_floatspan_float, \ +from typing import Union, overload, Optional, TYPE_CHECKING + +from pymeos_cffi import intersection_floatset_float, distance_floatset_float, \ + floatspan_in, floatspan_lower, floatspan_upper, numspan_shift_scale, contains_floatspan_float, \ + adjacent_floatspan_float, \ + float_to_floatspan, overlaps_span_span, span_eq, left_floatspan_float, overleft_floatspan_float, \ + right_floatspan_float, overright_floatspan_float, intersection_span_span, intersection_spanset_span, \ + minus_floatspan_float, minus_span_span, minus_spanset_span, union_floatspan_float, \ union_span_span, union_spanset_span, floatspan_out -from .. import Span, SpanSet -from ..base import Set +from .. import Span if TYPE_CHECKING: from ...boxes import TBox diff --git a/pymeos/pymeos/collections/number/intset.py b/pymeos/pymeos/collections/number/intset.py index 5dcfd4ee..3b376d90 100644 --- a/pymeos/pymeos/collections/number/intset.py +++ b/pymeos/pymeos/collections/number/intset.py @@ -2,14 +2,13 @@ from typing import List, Union, overload, Optional -from pymeos_cffi import intset_in, intset_make, intset_out, set_to_spanset, intset_start_value, intset_end_value, \ +from pymeos_cffi import intset_in, intset_make, intset_out, intset_start_value, intset_end_value, \ intset_value_n, intset_values, contains_intset_int, intersection_intset_int, intersection_set_set, minus_intset_int, \ minus_set_set, union_set_set, union_intset_int, intset_shift_scale, minus_int_intset, distance_intset_int -from .. import Span, SpanSet -from ..base import Set from .intspan import IntSpan from .intspanset import IntSpanSet +from ..base import Set class IntSet(Set[int]): diff --git a/pymeos/pymeos/collections/number/intspan.py b/pymeos/pymeos/collections/number/intspan.py index c15230f9..0bb26b4c 100644 --- a/pymeos/pymeos/collections/number/intspan.py +++ b/pymeos/pymeos/collections/number/intspan.py @@ -1,18 +1,15 @@ from __future__ import annotations -from typing import List, Union, overload, Optional, TYPE_CHECKING +from typing import Union, overload, Optional, TYPE_CHECKING -from pymeos_cffi import intset_in, intset_make, intset_out, set_to_spanset, intset_start_value, intset_end_value, \ - intset_value_n, intset_values, contains_intset_int, intersection_intset_int, intersection_set_set, minus_intset_int, \ - minus_set_set, union_set_set, union_intset_int, intset_shift_scale, minus_int_intset, distance_intset_int, \ +from pymeos_cffi import intersection_intset_int, distance_intset_int, \ intspan_in, intspan_lower, intspan_upper, numspan_shift_scale, contains_intspan_int, adjacent_intspan_int, \ - adjacent_span_span, int_to_intspan, overlaps_span_span, span_eq, left_intspan_int, overleft_intspan_int, \ - right_intspan_int, overright_intspan_int, intersection_span_span, intersection_spanset_spanset, \ - intersection_spanset_span, minus_intspan_int, minus_span_span, minus_spanset_span, union_intspan_int, \ + int_to_intspan, overlaps_span_span, span_eq, left_intspan_int, overleft_intspan_int, \ + right_intspan_int, overright_intspan_int, intersection_span_span, intersection_spanset_span, minus_intspan_int, \ + minus_span_span, minus_spanset_span, union_intspan_int, \ union_span_span, union_spanset_span, intspan_out -from .. import Span, SpanSet -from ..base import Set +from .. import Span if TYPE_CHECKING: from ...boxes import TBox From 77031eecac647a284a0c6e79f98233cbf96de1df Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 28 Sep 2023 11:24:43 +0200 Subject: [PATCH 051/101] Improve tests --- pymeos/pymeos/__init__.py | 4 ++- pymeos/pymeos/boxes/tbox.py | 2 +- pymeos/pymeos/collections/base/set.py | 3 ++- pymeos/pymeos/collections/base/span.py | 4 +-- pymeos/pymeos/collections/text/textset.py | 8 +++--- pymeos/tests/boxes/tbox_test.py | 30 +++++++++++------------ pymeos_cffi/pymeos_cffi/__init__.py | 6 +++-- pymeos_cffi/pymeos_cffi/builder/meos.h | 6 +++-- pymeos_cffi/pymeos_cffi/functions.py | 22 ++++++++++++++--- 9 files changed, 53 insertions(+), 32 deletions(-) diff --git a/pymeos/pymeos/__init__.py b/pymeos/pymeos/__init__.py index 0d0f9ec5..3c939e42 100644 --- a/pymeos/pymeos/__init__.py +++ b/pymeos/pymeos/__init__.py @@ -3,7 +3,7 @@ from .main import * from .meos_init import * from .temporal import * -from .collections import Period, PeriodSet, TimestampSet, Time, TextSet +from .collections import * __version__ = '1.1.3a1' __all__ = [ @@ -24,6 +24,8 @@ # Collections 'Time', 'Period', 'TimestampSet', 'PeriodSet', 'TextSet', + 'IntSet', 'IntSpan', 'IntSpanSet', + 'FloatSet', 'FloatSpan', 'FloatSpanSet' # extras 'TInterpolation', # aggregators diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index 62b0a178..41e043ae 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -292,7 +292,7 @@ def as_hexwkb(self) -> str: return tbox_as_hexwkb(self._inner, -1)[0] # ------------------------- Conversions ---------------------------------- - def to_span(self) -> Span: + def to_floatspan(self) -> Span: """ Returns the numeric span of ``self``. diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py index 94fd405e..a5183e72 100644 --- a/pymeos/pymeos/collections/base/set.py +++ b/pymeos/pymeos/collections/base/set.py @@ -28,7 +28,8 @@ class Set(Collection[T], ABC): _make_function: Callable[[Iterable[Any]], 'CData'] = None # ------------------------- Constructors ---------------------------------- - def __init__(self, string: Optional[str] = None, *, elements: Optional[List[Union[str, T]]] = None, + def __init__(self, string: Optional[str] = None, *, + elements: Optional[List[Union[str, T]]] = None, _inner=None): super().__init__() assert (_inner is not None) or ((string is not None) != (elements is not None)), \ diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index 4d62eb22..425a782c 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -32,8 +32,8 @@ class Span(Collection[T], ABC): def __init__(self, string: Optional[str] = None, *, lower: Optional[Union[str, T]] = None, upper: Optional[Union[str, T]] = None, - lower_inc: bool = True, - upper_inc: bool = False, + lower_inc: Optional[bool] = True, + upper_inc: Optional[bool] = False, _inner=None): super().__init__() assert (_inner is not None) or ((string is not None) != (lower is not None and upper is not None)), \ diff --git a/pymeos/pymeos/collections/text/textset.py b/pymeos/pymeos/collections/text/textset.py index 7d28ad27..4d4728a6 100644 --- a/pymeos/pymeos/collections/text/textset.py +++ b/pymeos/pymeos/collections/text/textset.py @@ -140,9 +140,9 @@ def lowercase(self): A :class:`str` instance MEOS Functions: - textset_lowercase + textset_lower """ - return textset_lowercase(self._inner) + return textset_lower(self._inner) def uppercase(self): """ @@ -152,9 +152,9 @@ def uppercase(self): A :class:`str` instance MEOS Functions: - textset_uppercase + textset_upper """ - return textset_uppercase(self._inner) + return textset_upper(self._inner) # ------------------------- Set Operations -------------------------------- diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index 83237658..ac3ba6ff 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -1,10 +1,10 @@ from copy import copy from datetime import datetime, timezone, timedelta -from spans.types import intrange, floatrange import pytest from pymeos import TBox, TInterpolation, TimestampSet, Period, PeriodSet, \ + IntSpan, FloatSpan, \ TInt, TIntInst, TIntSeq, TIntSeqSet, TFloat, TFloatInst, TFloatSeq, TFloatSeqSet from tests.conftest import TestPyMEOS @@ -74,10 +74,10 @@ def test_hexwkb_constructor(self): [ (1, 'TBOXINT X([1, 2))'), (1.5, 'TBOXFLOAT X([1.5, 1.5])'), - (intrange(1, 2, True, True), 'TBOXINT X([1, 3))'), - (floatrange(1.5, 2.5, True, True), 'TBOXFLOAT X([1.5, 2.5])'), + (IntSpan(1, 2, True, True), 'TBOXINT X([1, 3))'), + (FloatSpan(1.5, 2.5, True, True), 'TBOXFLOAT X([1.5, 2.5])'), ], - ids=['int', 'float', 'intrange', 'floatrange'] + ids=['int', 'float', 'IntSpan', 'FloatSpan'] ) def test_from_value_constructor(self, value, expected): tb = TBox.from_value(value) @@ -110,21 +110,21 @@ def test_from_time_constructor(self, time, expected): 'TBOXINT XT([1, 2),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), (1.5, datetime(2019, 9, 1), 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - (intrange(1, 2, True, True), datetime(2019, 9, 1), + (IntSpan(1, 2, True, True), datetime(2019, 9, 1), 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - (floatrange(1.5, 2.5, True, True), datetime(2019, 9, 1), + (FloatSpan(1.5, 2.5, True, True), datetime(2019, 9, 1), 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), (1, Period('[2019-09-01, 2019-09-02]'), 'TBOXINT XT([1, 2),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), (1.5, Period('[2019-09-01, 2019-09-02]'), 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - (intrange(1, 2, True, True), Period('[2019-09-01, 2019-09-02]'), + (IntSpan(1, 2, True, True), Period('[2019-09-01, 2019-09-02]'), 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - (floatrange(1.5, 2.5, True, True), Period('[2019-09-01, 2019-09-02]'), + (FloatSpan(1.5, 2.5, True, True), Period('[2019-09-01, 2019-09-02]'), 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), ], - ids=['int-Timestamp', 'float-Timestamp', 'intrange-Timestamp', 'floatrange-Timestamp', - 'int-Period', 'float-Period', 'intrange-Period', 'floatrange-Period',] + ids=['int-Timestamp', 'float-Timestamp', 'IntSpan-Timestamp', 'FloatSpan-Timestamp', + 'int-Period', 'float-Period', 'IntSpan-Period', 'FloatSpan-Period',] ) def test_from_value_time_constructor(self, value, time, expected): tb = TBox.from_value_time(value, time) @@ -219,14 +219,14 @@ def test_as_hexwkb(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbfx, floatrange(1.0, 2.0, True, True)), - (tbfxt, floatrange(1.0, 2.0, True, True)), + (tbfx, FloatSpan(1.0, 2.0, True, True)), + (tbfxt, FloatSpan(1.0, 2.0, True, True)), ], ids=['TBoxFloat X', 'TBoxFloat XT'] ) - def test_to_floatrange(self, tbox, expected): - tb = tbox.to_floatrange() - assert isinstance(tb, floatrange) + def test_to_floatspan(self, tbox, expected): + tb = tbox.to_floatspan() + assert isinstance(tb, floatspan) assert tb == expected @pytest.mark.parametrize( diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index b9ba0247..3e3b6ab6 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -259,8 +259,8 @@ 'period_tprecision', 'periodset_shift_scale', 'periodset_tprecision', - 'textset_lowercase', - 'textset_uppercase', + 'textset_lower', + 'textset_upper', 'timestamp_tprecision', 'timestampset_shift_scale', 'intersection_bigintset_bigint', @@ -895,6 +895,7 @@ 'sub_tint_int', 'sub_tnumber_tnumber', 'tfloat_round', + 'tfloatarr_round', 'tfloat_degrees', 'tfloat_derivative', 'tfloat_radians', @@ -951,6 +952,7 @@ 'tpoint_expand_space', 'tpoint_make_simple', 'tpoint_round', + 'tpointarr_round', 'tpoint_set_srid', 'tpoint_to_geo_meas', 'econtains_geo_tpoint', diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index fa5c85ca..9dbad14a 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -980,8 +980,8 @@ extern Span *period_shift_scale(const Span *p, const Interval *shift, const Inte extern Span *period_tprecision(const Span *s, const Interval *duration, TimestampTz torigin); extern SpanSet *periodset_shift_scale(const SpanSet *ss, const Interval *shift, const Interval *duration); extern SpanSet *periodset_tprecision(const SpanSet *ss, const Interval *duration, TimestampTz torigin); -extern Set *textset_lowercase(const Set *s); -extern Set *textset_uppercase(const Set *s); +extern Set *textset_lower(const Set *s); +extern Set *textset_upper(const Set *s); extern TimestampTz timestamp_tprecision(TimestampTz t, const Interval *duration, TimestampTz torigin); extern Set *timestampset_shift_scale(const Set *ts, const Interval *shift, const Interval *duration); @@ -1767,6 +1767,7 @@ extern Temporal *sub_tfloat_float(const Temporal *tnumber, double d); extern Temporal *sub_tint_int(const Temporal *tnumber, int i); extern Temporal *sub_tnumber_tnumber(const Temporal *tnumber1, const Temporal *tnumber2); extern Temporal *tfloat_round(const Temporal *temp, int maxdd); +extern Temporal **tfloatarr_round(const Temporal **temp, int count, int maxdd); extern Temporal *tfloat_degrees(const Temporal *temp, bool normalize); extern Temporal *tfloat_derivative(const Temporal *temp); extern Temporal *tfloat_radians(const Temporal *temp); @@ -1845,6 +1846,7 @@ bool tpoint_AsMVTGeom(const Temporal *temp, const STBox *bounds, int32_t extent, extern STBox *tpoint_expand_space(const Temporal *temp, double d); extern Temporal **tpoint_make_simple(const Temporal *temp, int *count); extern Temporal *tpoint_round(const Temporal *temp, int maxdd); +extern Temporal **tpointarr_round(const Temporal **temp, int count, int maxdd); extern Temporal *tpoint_set_srid(const Temporal *temp, int32 srid); bool tpoint_to_geo_meas(const Temporal *tpoint, const Temporal *measure, bool segmentize, GSERIALIZED **result); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index ab9acb76..a4ecab0e 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -1732,16 +1732,16 @@ def periodset_tprecision(ss: 'const SpanSet *', duration: 'const Interval *', to return result if result != _ffi.NULL else None -def textset_lowercase(s: 'const Set *') -> 'Set *': +def textset_lower(s: 'const Set *') -> 'Set *': s_converted = _ffi.cast('const Set *', s) - result = _lib.textset_lowercase(s_converted) + result = _lib.textset_lower(s_converted) _check_error() return result if result != _ffi.NULL else None -def textset_uppercase(s: 'const Set *') -> 'Set *': +def textset_upper(s: 'const Set *') -> 'Set *': s_converted = _ffi.cast('const Set *', s) - result = _lib.textset_uppercase(s_converted) + result = _lib.textset_upper(s_converted) _check_error() return result if result != _ffi.NULL else None @@ -6727,6 +6727,13 @@ def tfloat_round(temp: 'const Temporal *', maxdd: int) -> 'Temporal *': return result if result != _ffi.NULL else None +def tfloatarr_round(temp: 'const Temporal **', count: int, maxdd: int) -> 'Temporal **': + temp_converted = [_ffi.cast('const Temporal *', x) for x in temp] + result = _lib.tfloatarr_round(temp_converted, count, maxdd) + _check_error() + return result if result != _ffi.NULL else None + + def tfloat_degrees(temp: 'const Temporal *', normalize: bool) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) result = _lib.tfloat_degrees(temp_converted, normalize) @@ -7161,6 +7168,13 @@ def tpoint_round(temp: 'const Temporal *', maxdd: int) -> 'Temporal *': return result if result != _ffi.NULL else None +def tpointarr_round(temp: 'const Temporal **', count: int, maxdd: int) -> 'Temporal **': + temp_converted = [_ffi.cast('const Temporal *', x) for x in temp] + result = _lib.tpointarr_round(temp_converted, count, maxdd) + _check_error() + return result if result != _ffi.NULL else None + + def tpoint_set_srid(temp: 'const Temporal *', srid: int) -> 'Temporal *': temp_converted = _ffi.cast('const Temporal *', temp) srid_converted = _ffi.cast('int32', srid) From a16777898a800826874eece32be4f3a6f8af1357 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 28 Sep 2023 12:06:22 +0200 Subject: [PATCH 052/101] Improve tests --- pymeos/pymeos/boxes/tbox.py | 15 +++--- pymeos/pymeos/main/tint.py | 19 +++---- pymeos/tests/boxes/tbox_test.py | 18 ++++--- pymeos/tests/main/tfloat_test.py | 92 +++++++++++++++++++------------- pymeos/tests/main/tint_test.py | 66 +++++++++++------------ 5 files changed, 114 insertions(+), 96 deletions(-) diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index 41e043ae..07fba85a 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -3,7 +3,6 @@ from typing import Optional, Union, List from pymeos_cffi import * -from spans import intrange, floatrange from ..main import TNumber from ..collections import * @@ -33,7 +32,7 @@ class TBox: Note that you can create a TBox with only the numerical or the temporal dimension. In these cases, it will be equivalent to a :class:`~pymeos.time.period.Period` (if it only has temporal dimension) or - to a :class:`~spans.floatrange` (if it only has the numeric dimension). + to a :class:`FloatSpan` (if it only has the numeric dimension). """ __slots__ = ['_inner'] @@ -128,9 +127,9 @@ def from_hexwkb(hexwkb: str) -> TBox: return TBox(_inner=result) @staticmethod - def from_value(value: Union[int, float, intrange, floatrange]) -> TBox: + def from_value(value: Union[int, float, IntSpan, FloatSpan]) -> TBox: """ - Returns a `TBox` from a numeric value or range. The created `TBox` will + Returns a `TBox` from a numeric value or span. The created `TBox` will only have a numerical dimension. Args: @@ -146,10 +145,10 @@ def from_value(value: Union[int, float, intrange, floatrange]) -> TBox: result = int_to_tbox(value) elif isinstance(value, float): result = float_to_tbox(value) - elif isinstance(value, intrange): - result = numspan_to_tbox(intrange_to_intspan(value)) - elif isinstance(value, floatrange): - result = numspan_to_tbox(floatrange_to_floatspan(value)) + elif isinstance(value, IntSpan): + result = numspan_to_tbox(value) + elif isinstance(value, FloatSpan): + result = numspan_to_tbox(value) else: raise TypeError(f'Operation not supported with type {value.__class__}') return TBox(_inner=result) diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index eda98afd..431df4db 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -126,32 +126,32 @@ def to_tfloat(self) -> TFloat: from ..factory import _TemporalFactory return _TemporalFactory.create_temporal(tint_to_tfloat(self._inner)) - def to_intrange(self) -> intrange: + def to_intspan(self) -> IntSpan: """ Returns value span of `self`. Returns: - An :class:`intrange` with the value span of `self`. + An :class:`IntSpan` with the value span of `self`. MEOS Functions: tnumber_to_span """ - return intspan_to_intrange(tnumber_to_span(self._inner)) + return IntSpan(_inner=tnumber_to_span(self._inner)) # ------------------------- Accessors ------------------------------------- - def value_range(self) -> intrange: + def value_span(self) -> IntSpan: """ Returns the value span of `self`. Returns: - An :class:`intrange` with the value span of `self`. + An :class:`IntSpan` with the value span of `self`. MEOS Functions: tnumber_to_span """ - return self.to_intrange() + return self.to_intspan() - def value_ranges(self) -> List[intrange]: + def value_spans(self) -> IntSpanSet: """ Returns the value spans of `self` taking into account gaps. @@ -161,10 +161,7 @@ def value_ranges(self) -> List[intrange]: MEOS Functions: tint_spanset """ - spanset = tnumber_valuespans(self._inner) - spans = spanset_spans(spanset) - count = spanset_num_spans(spanset) - return [intspan_to_intrange(spans[i]) for i in range(count)] + return IntSpanSet(_inner=tnumber_valuespans(self._inner)) def start_value(self) -> int: """ diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index ac3ba6ff..6907f388 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -74,8 +74,10 @@ def test_hexwkb_constructor(self): [ (1, 'TBOXINT X([1, 2))'), (1.5, 'TBOXFLOAT X([1.5, 1.5])'), - (IntSpan(1, 2, True, True), 'TBOXINT X([1, 3))'), - (FloatSpan(1.5, 2.5, True, True), 'TBOXFLOAT X([1.5, 2.5])'), + (IntSpan(lower=1, upper=2, lower_inc=True, upper_inc=True), + 'TBOXINT X([1, 3))'), + (FloatSpan(lower=1.5, upper=2.5, lower_inc=True, upper_inc=True), + 'TBOXFLOAT X([1.5, 2.5])'), ], ids=['int', 'float', 'IntSpan', 'FloatSpan'] ) @@ -110,17 +112,17 @@ def test_from_time_constructor(self, time, expected): 'TBOXINT XT([1, 2),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), (1.5, datetime(2019, 9, 1), 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - (IntSpan(1, 2, True, True), datetime(2019, 9, 1), + (IntSpan(lower=1, upper=2, lower_inc=True, upper_inc=True), datetime(2019, 9, 1), 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), - (FloatSpan(1.5, 2.5, True, True), datetime(2019, 9, 1), + (FloatSpan(lower=1.5, upper=2.5, lower_inc=True, upper_inc=True), datetime(2019, 9, 1), 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00])'), (1, Period('[2019-09-01, 2019-09-02]'), 'TBOXINT XT([1, 2),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), (1.5, Period('[2019-09-01, 2019-09-02]'), 'TBOXFLOAT XT([1.5, 1.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - (IntSpan(1, 2, True, True), Period('[2019-09-01, 2019-09-02]'), + (IntSpan(lower=1, upper=2, lower_inc=True, upper_inc=True), Period('[2019-09-01, 2019-09-02]'), 'TBOXINT XT([1, 3),[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), - (FloatSpan(1.5, 2.5, True, True), Period('[2019-09-01, 2019-09-02]'), + (FloatSpan(lower=1.5, upper=2.5, lower_inc=True, upper_inc=True), Period('[2019-09-01, 2019-09-02]'), 'TBOXFLOAT XT([1.5, 2.5],[2019-09-01 00:00:00+00, 2019-09-02 00:00:00+00])'), ], ids=['int-Timestamp', 'float-Timestamp', 'IntSpan-Timestamp', 'FloatSpan-Timestamp', @@ -219,8 +221,8 @@ def test_as_hexwkb(self, tbox, expected): @pytest.mark.parametrize( 'tbox, expected', [ - (tbfx, FloatSpan(1.0, 2.0, True, True)), - (tbfxt, FloatSpan(1.0, 2.0, True, True)), + (tbfx, FloatSpan(lower=1.0, upper=2.0, lower_inc=True, upper_inc=True)), + (tbfxt, FloatSpan(lower=1.0, upper=2.0, lower_inc=True, upper_inc=True)), ], ids=['TBoxFloat X', 'TBoxFloat XT'] ) diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index ee4a735e..c98ca64d 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -1,14 +1,13 @@ from copy import copy from operator import not_ from datetime import datetime, timezone, timedelta -from spans.types import intrange, floatrange import pytest from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, \ TFloat, TFloatInst, TFloatSeq, TFloatSeqSet, \ TInt, TIntInst, TIntSeq, TIntSeqSet, \ - TInterpolation, TBox, TimestampSet, Period, PeriodSet + TInterpolation, TBox, TimestampSet, Period, PeriodSet, IntSpan, FloatSpan from tests.conftest import TestPyMEOS @@ -441,10 +440,10 @@ def test_values(self, temporal, expected): @pytest.mark.parametrize( 'temporal, expected', [ - (tfi, floatrange(1.5, 1.5, True, True)), - (tfds, floatrange(1.5, 2.5, True, True)), - (tfs, floatrange(1.5, 2.5, True, True)), - (tfss, floatrange(1.5, 2.5, True, True)), + (tfi, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)), + (tfds, FloatSpan(lower=1.5, upper=2.5, lower_inc=True, upper_inc=True)), + (tfs, FloatSpan(lower=1.5, upper=2.5, lower_inc=True, upper_inc=True)), + (tfss, FloatSpan(lower=1.5, upper=2.5, lower_inc=True, upper_inc=True)), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -454,10 +453,11 @@ def test_value_range(self, temporal, expected): @pytest.mark.parametrize( 'temporal, expected', [ - (tfi, [floatrange(1.5, 1.5, True, True)]), - (tfds, [floatrange(1.5, 1.5, True, True),floatrange(2.5, 2.5, True, True)]), - (tfs, [floatrange(1.5, 2.5, True, True)]), - (tfss, [floatrange(1.5, 2.5, True, True)]), + (tfi, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)]), + (tfds, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), + (tfs, [FloatSpan(lower=1.5, upper=2.5, lower_inc=True, upper_inc=True)]), + (tfss, [FloatSpan(lower=1.5, upper=2.5, lower_inc=True, upper_inc=True)]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1496,31 +1496,39 @@ def test_at_time(self, temporal, restrictor, expected): [ (tfi, 1.5, TFloatInst('1.5@2019-09-01')), (tfi, 2.5, None), - (tfi, floatrange(1.5, 1.5, True, True), TFloatInst('1.5@2019-09-01')), + (tfi, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + TFloatInst('1.5@2019-09-01')), (tfi, [1.5,2.5], tfi), - # (tfi, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # (tfi, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], # TFloatInst('1.5@2019-09-01')), (tfds, 1.5, TFloatSeq('{1.5@2019-09-01}')), (tfds, 2.5, TFloatSeq('{2.5@2019-09-02}')), - (tfds, floatrange(1.5, 1.5, True, True), TFloatInst('1.5@2019-09-01')), + (tfds, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + TFloatInst('1.5@2019-09-01')), (tfds, [1.5,2.5], tfds), - # (tfds, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # (tfds, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], # TFloatInst('1.5@2019-09-01')), (tfs, 1.5, TFloatSeq('[1.5@2019-09-01]')), (tfs, 2.5, TFloatSeq('[2.5@2019-09-02]')), - (tfs, floatrange(1.5, 1.5, True, True), TFloatInst('1.5@2019-09-01')), + (tfs, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + TFloatInst('1.5@2019-09-01')), (tfs, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02]}')), - # (tfs, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # (tfs, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], # TFloatInst('1.5@2019-09-01')), (tfss, 1.5, TFloatSeqSet('{[1.5@2019-09-01],[1.5@2019-09-03, 1.5@2019-09-05]}')), (tfss, 2.5, TFloatSeqSet('{[2.5@2019-09-02]}')), - (tfss, floatrange(1.5, 1.5, True, True), + (tfss, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), TFloatSeqSet('{[1.5@2019-09-01],[1.5@2019-09-03, 1.5@2019-09-05]}')), - (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')) - # (tfss, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02],' + '[1.5@2019-09-03, 1.5@2019-09-05]}')) + # (tfss, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], # TFloatInst('1.5@2019-09-01')), ], ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', @@ -1604,30 +1612,38 @@ def test_minus_time(self, temporal, restrictor, expected): [ (tfi, 1.5, None), (tfi, 2.5, TFloatInst('1.5@2019-09-01')), - (tfi, floatrange(1.5, 1.5, True, True), None), + (tfi, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), None), # (tfi, [1.5,2.5], TFloatInst('1.5@2019-09-01')), - # (tfi, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # (tfi, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], # TFloatInst('1.5@2019-09-01')), (tfds, 1.5, TFloatSeq('{2.5@2019-09-02}')), (tfds, 2.5, TFloatSeq('{1.5@2019-09-01}')), - (tfds, floatrange(1.5, 1.5, True, True), TFloatSeq('{2.5@2019-09-02}')), + (tfds, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + TFloatSeq('{2.5@2019-09-02}')), # (tfds, [1.5,2.5], TFloatSeq('{1.5@2019-09-01}')), - # (tfds, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # (tfds, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], # TFloatInst('1.5@2019-09-01')), (tfs, 1.5, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), (tfs, 2.5, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), - (tfs, floatrange(1.5, 1.5, True, True), TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), + (tfs, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), # (tfs, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), - # (tfs, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + # (tfs, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], # TFloatInst('1.5@2019-09-01')), (tfss, 1.5, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), (tfss, 2.5, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),[1.5@2019-09-03, 1.5@2019-09-05]}')), - (tfs, floatrange(1.5, 1.5, True, True), TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), - # (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),[1.5@2019-09-03, 1.5@2019-09-05]}')) - # (tfss, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)], + (tfs, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), + # (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),' + # '[1.5@2019-09-03, 1.5@2019-09-05]}')) + # (tfss, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], # TFloatInst('1.5@2019-09-01')), ], ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', @@ -1714,27 +1730,31 @@ def test_at_minus_time(self, temporal, restrictor): [ (tfi, 1.5), (tfi, 2.5), - (tfi, floatrange(1.5, 1.5, True, True)), + (tfi, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)), (tfi, [1.5,2.5]), - # (tfi, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), + # (tfi, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), (tfds, 1.5), (tfds, 2.5), - (tfds, floatrange(1.5, 1.5, True, True)), + (tfds, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)), (tfds, [1.5,2.5]), - # (tfds, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), + # (tfds, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), (tfs, 1.5), (tfs, 2.5), - (tfs, floatrange(1.5, 1.5, True, True)), + (tfs, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)), (tfs, [1.5,2.5]), - # (tfs, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), + # (tfs, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), (tfss, 1.5), (tfss, 2.5), - (tfss, floatrange(1.5, 1.5, True, True)), + (tfss, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)), (tfss, [1.5,2.5]), - # (tfss, [floatrange(1.5, 1.5, True, True), floatrange(2.5, 2.5, True, True)]), + # (tfss, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), ], ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', 'Instant-ValueList', # 'Instant-RangeList', diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index 2340ac21..90d60538 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -8,7 +8,7 @@ from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, \ TFloat, TFloatInst, TFloatSeq, TFloatSeqSet, \ TInt, TIntInst, TIntSeq, TIntSeqSet, \ - TInterpolation, TBox, TimestampSet, Period, PeriodSet + TInterpolation, TBox, TimestampSet, Period, PeriodSet, IntSpan, FloatSpan from tests.conftest import TestPyMEOS @@ -426,10 +426,10 @@ def test_values(self, temporal, expected): @pytest.mark.parametrize( 'temporal, expected', [ - (tii, intrange(1, 1, True, True)), - (tids, intrange(1, 2, True, True)), - (tis, intrange(1, 2, True, True)), - (tiss, intrange(1, 2, True, True)), + (tii, IntSpan(1, 1, True, True)), + (tids, IntSpan(1, 2, True, True)), + (tis, IntSpan(1, 2, True, True)), + (tiss, IntSpan(1, 2, True, True)), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -439,10 +439,10 @@ def test_value_range(self, temporal, expected): @pytest.mark.parametrize( 'temporal, expected', [ - (tii, [intrange(1, 1, True, True)]), - (tids, [intrange(1, 2, True, True)]), - (tis, [intrange(1, 2, True, True)]), - (tiss, [intrange(1, 2, True, True)]), + (tii, [IntSpan(1, 1, True, True)]), + (tids, [IntSpan(1, 2, True, True)]), + (tis, [IntSpan(1, 2, True, True)]), + (tiss, [IntSpan(1, 2, True, True)]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1373,29 +1373,29 @@ def test_at_time(self, temporal, restrictor, expected): [ (tii, 1, TIntInst('1@2019-09-01')), (tii, 2, None), - (tii, intrange(1, 1, True, True), TIntInst('1@2019-09-01')), + (tii, IntSpan(1, 1, True, True), TIntInst('1@2019-09-01')), (tii, [1,2], tii), - # (tii, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # (tii, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], # TIntInst('1@2019-09-01')), (tids, 1, TIntSeq('{1@2019-09-01}')), (tids, 2, TIntSeq('{2@2019-09-02}')), - (tids, intrange(1, 1, True, True), TIntSeq('{1@2019-09-01}')), + (tids, IntSpan(1, 1, True, True), TIntSeq('{1@2019-09-01}')), (tids, [1,2], tids), - # (tids, [intrange(1, 1, True, True), tids), + # (tids, [IntSpan(1, 1, True, True), tids), (tis, 1, TIntSeq('[1@2019-09-01, 1@2019-09-02)')), (tis, 2, TIntSeq('[2@2019-09-02]')), - (tis, intrange(1, 1, True, True), TIntSeq('[1@2019-09-01, 1@2019-09-02)')), + (tis, IntSpan(1, 1, True, True), TIntSeq('[1@2019-09-01, 1@2019-09-02)')), (tis, [1,2], tis), - # (tis, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # (tis, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], # TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), (tiss, 1, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), (tiss, 2, TIntSeqSet('{[2@2019-09-02]}')), - (tiss, intrange(1, 1, True, True), TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), + (tiss, IntSpan(1, 1, True, True), TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), (tiss, [1,2], tiss) - # (tiss, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # (tiss, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], # tiss), ], ids=['Instant-1', 'Instant-2', 'Instant-Range', @@ -1481,30 +1481,30 @@ def test_minus_time(self, temporal, restrictor, expected): [ (tii, 1, None), (tii, 2, TIntInst('1@2019-09-01')), - (tii, intrange(1, 1, True, True), None), + (tii, IntSpan(1, 1, True, True), None), (tii, [1,2], None), - # (tii, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # (tii, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], # None), (tids, 1, TIntSeq('{2@2019-09-02}')), (tids, 2, TIntSeq('{1@2019-09-01}')), - (tids, intrange(1, 1, True, True), TIntSeq('{2@2019-09-02}')), + (tids, IntSpan(1, 1, True, True), TIntSeq('{2@2019-09-02}')), (tids, [1,2], None), - # (tids, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # (tids, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], # None), (tis, 1, TIntSeqSet('{[2@2019-09-02]}')), (tis, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02)}')), - (tis, intrange(1, 1, True, True), TIntSeqSet('{[2@2019-09-02]}')), + (tis, IntSpan(1, 1, True, True), TIntSeqSet('{[2@2019-09-02]}')), (tis, [1,2], None), - # (tis, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # (tis, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], # None), (tiss, 1, TIntSeqSet('{[2@2019-09-02]}')), (tiss, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), - (tis, intrange(1, 1, True, True), TIntSeqSet('{[2@2019-09-02]}')), + (tis, IntSpan(1, 1, True, True), TIntSeqSet('{[2@2019-09-02]}')), (tiss, [1,2], None) - # (tiss, [intrange(1, 1, True, True), intrange(2, 2, True, True)], + # (tiss, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], # None), ], ids=['Instant-1', 'Instant-2', 'Instant-Range', @@ -1558,9 +1558,9 @@ def test_minus_min(self, temporal, expected): (tii, period_set), (tii, 1), (tii, 2), - (tii, intrange(1, 1, True, True)), + (tii, IntSpan(1, 1, True, True)), (tii, [1,2]), - # (tii, [intrange(1, 1, True, True), intrange(2, 2, True, True)]), + # (tii, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)]), (tids, timestamp), (tids, timestamp_set), @@ -1568,9 +1568,9 @@ def test_minus_min(self, temporal, expected): (tids, period_set), (tids, 1), (tids, 2), - (tids, intrange(1, 1, True, True)), + (tids, IntSpan(1, 1, True, True)), (tids, [1,2]), - # (tds, [intrange(1, 1, True, True), intrange(2, 2, True, True)]), + # (tds, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)]), (tis, timestamp), (tis, timestamp_set), @@ -1578,9 +1578,9 @@ def test_minus_min(self, temporal, expected): (tis, period_set), (tis, 1), (tis, 2), - (tis, intrange(1, 1, True, True)), + (tis, IntSpan(1, 1, True, True)), (tis, [1,2]), - # (tis, [intrange(1, 1, True, True), intrange(2, 2, True, True)]), + # (tis, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)]), (tiss, timestamp), (tiss, timestamp_set), @@ -1588,9 +1588,9 @@ def test_minus_min(self, temporal, expected): (tiss, period_set), (tiss, 1), (tiss, 2), - (tiss, intrange(1, 1, True, True)), + (tiss, IntSpan(1, 1, True, True)), (tiss, [1,2]), - # (tiss, [intrange(1, 1, True, True), intrange(2, 2, True, True)]), + # (tiss, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)]), ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', 'Instant-PeriodSet', 'Instant-1', 'Instant-2', From 199dcc0c1270547f30887a67236109255d5cb866 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 28 Sep 2023 12:02:46 +0200 Subject: [PATCH 053/101] Remove read_from_cursor from time collections since generic method in base collection works fine. --- pymeos/pymeos/collections/time/periodset.py | 11 ----------- pymeos/pymeos/collections/time/timestampset.py | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py index bb6947d8..efd8aa9c 100644 --- a/pymeos/pymeos/collections/time/periodset.py +++ b/pymeos/pymeos/collections/time/periodset.py @@ -859,14 +859,3 @@ def __add__(self, other): def plot(self, *args, **kwargs): from ...plotters import TimePlotter return TimePlotter.plot_periodset(self, *args, **kwargs) - - # ------------------------- Database Operations --------------------------- - @staticmethod - def read_from_cursor(value, _=None): - """ - Reads a :class:`PeriodSet` from a database cursor. Used when automatically loading objects from the database. - Users should use the class constructor instead. - """ - if not value: - return None - return PeriodSet(string=value) diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py index c752b8c4..64a11f43 100644 --- a/pymeos/pymeos/collections/time/timestampset.py +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -699,14 +699,3 @@ def union(self, other: Union[Time, Temporal, Box]) -> Union[PeriodSet, Timestamp def plot(self, *args, **kwargs): from ...plotters import TimePlotter return TimePlotter.plot_timestampset(self, *args, **kwargs) - - # ------------------------- Database Operations --------------------------- - @staticmethod - def read_from_cursor(value, _=None): - """ - Reads a :class:`TimestampSet` from a database cursor. Used when automatically loading objects from the database. - Users should use the class constructor instead. - """ - if not value: - return None - return TimestampSet(string=value) From 0fe85c16ab0413e42b7531698dc9995c8ab0266b Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 28 Sep 2023 12:14:07 +0200 Subject: [PATCH 054/101] Fix wrong use of *span_in instead of *span_make --- pymeos/pymeos/collections/number/floatspan.py | 4 ++-- pymeos/pymeos/collections/number/intspan.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pymeos/pymeos/collections/number/floatspan.py b/pymeos/pymeos/collections/number/floatspan.py index 7d6bca88..95b583ac 100644 --- a/pymeos/pymeos/collections/number/floatspan.py +++ b/pymeos/pymeos/collections/number/floatspan.py @@ -8,7 +8,7 @@ float_to_floatspan, overlaps_span_span, span_eq, left_floatspan_float, overleft_floatspan_float, \ right_floatspan_float, overright_floatspan_float, intersection_span_span, intersection_spanset_span, \ minus_floatspan_float, minus_span_span, minus_spanset_span, union_floatspan_float, \ - union_span_span, union_spanset_span, floatspan_out + union_span_span, union_spanset_span, floatspan_out, floatspan_make from .. import Span @@ -43,7 +43,7 @@ class FloatSpan(Span[float]): _parse_function = floatspan_in _parse_value_function = float - _make_function = floatspan_in + _make_function = floatspan_make # ------------------------- Output ---------------------------------------- def __str__(self, max_decimals: int = 15) -> str: diff --git a/pymeos/pymeos/collections/number/intspan.py b/pymeos/pymeos/collections/number/intspan.py index 0bb26b4c..cee9806f 100644 --- a/pymeos/pymeos/collections/number/intspan.py +++ b/pymeos/pymeos/collections/number/intspan.py @@ -7,7 +7,7 @@ int_to_intspan, overlaps_span_span, span_eq, left_intspan_int, overleft_intspan_int, \ right_intspan_int, overright_intspan_int, intersection_span_span, intersection_spanset_span, minus_intspan_int, \ minus_span_span, minus_spanset_span, union_intspan_int, \ - union_span_span, union_spanset_span, intspan_out + union_span_span, union_spanset_span, intspan_out, intspan_make from .. import Span @@ -42,7 +42,7 @@ class IntSpan(Span[int]): _parse_function = intspan_in _parse_value_function = int - _make_function = intspan_in + _make_function = intspan_make # ------------------------- Output ---------------------------------------- def __str__(self): From a9ffe7355df49fb12c2991095ece6dd42162ae6c Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 28 Sep 2023 15:31:30 +0200 Subject: [PATCH 055/101] Improve tests --- pymeos/pymeos/boxes/tbox.py | 33 +++-- pymeos/pymeos/collections/text/textset.py | 7 +- pymeos/pymeos/main/tfloat.py | 24 ++-- pymeos/pymeos/main/tint.py | 9 +- pymeos/pymeos/main/tnumber.py | 54 ++++---- pymeos/tests/boxes/tbox_test.py | 2 +- pymeos/tests/collections/text/textset_test.py | 117 ------------------ pymeos/tests/main/tfloat_test.py | 32 ++--- pymeos/tests/main/tint_test.py | 84 ++++++------- 9 files changed, 118 insertions(+), 244 deletions(-) diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index 07fba85a..2d68a3ae 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -146,9 +146,9 @@ def from_value(value: Union[int, float, IntSpan, FloatSpan]) -> TBox: elif isinstance(value, float): result = float_to_tbox(value) elif isinstance(value, IntSpan): - result = numspan_to_tbox(value) + result = numspan_to_tbox(value._inner) elif isinstance(value, FloatSpan): - result = numspan_to_tbox(value) + result = numspan_to_tbox(value._inner) else: raise TypeError(f'Operation not supported with type {value.__class__}') return TBox(_inner=result) @@ -182,7 +182,7 @@ def from_time(time: Time) -> TBox: return TBox(_inner=result) @staticmethod - def from_value_time(value: Union[int, float, intrange, floatrange], + def from_value_time(value: Union[int, float, IntSpan, FloatSpan], time: Union[datetime, Period]) -> TBox: """ Returns a `TBox` from a numerical and a temporal object. @@ -209,16 +209,16 @@ def from_value_time(value: Union[int, float, intrange, floatrange], datetime_to_timestamptz(time)) elif isinstance(value, float) and isinstance(time, Period): result = float_period_to_tbox(value, time._inner) - elif isinstance(value, intrange) and isinstance(time, datetime): - result = span_timestamp_to_tbox(intrange_to_intspan(value), + elif isinstance(value, IntSpan) and isinstance(time, datetime): + result = span_timestamp_to_tbox(value._inner, datetime_to_timestamptz(time)) - elif isinstance(value, intrange) and isinstance(time, Period): - result = span_period_to_tbox(intrange_to_intspan(value), time._inner) - elif isinstance(value, floatrange) and isinstance(time, datetime): - result = span_timestamp_to_tbox(floatrange_to_floatspan(value), + elif isinstance(value, IntSpan) and isinstance(time, Period): + result = span_period_to_tbox(value._inner, time._inner) + elif isinstance(value, FloatSpan) and isinstance(time, datetime): + result = span_timestamp_to_tbox(value._inner, datetime_to_timestamptz(time)) - elif isinstance(value, floatrange) and isinstance(time, Period): - result = span_period_to_tbox(floatrange_to_floatspan(value), time._inner) + elif isinstance(value, FloatSpan) and isinstance(time, Period): + result = span_period_to_tbox(value._inner, time._inner) else: raise TypeError(f'Operation not supported with types {value.__class__} and {time.__class__}') return TBox(_inner=result) @@ -704,7 +704,7 @@ def __mul__(self, other): # ------------------------- Topological Operations ------------------------ def is_adjacent(self, - other: Union[int, float, intrange, floatrange, TBox, TNumber]) -> bool: + other: Union[int, float, IntSpan, FloatSpan, TBox, TNumber]) -> bool: """ Returns whether ``self`` is adjacent to ``other``. That is, they share only the temporal or numerical bound and only one of them contains it. @@ -732,13 +732,12 @@ def is_adjacent(self, elif isinstance(other, float): return adjacent_span_span(self._inner_span(), float_to_floaspan(other)) - elif isinstance(other, intrange): + elif isinstance(other, IntSpan): from pymeos_cffi.functions import _ffi return adjacent_span_span(_ffi.addressof(self._inner, 'span'), - intrange_to_intspan(other)) - elif isinstance(other, floatrange): - return adjacent_span_span(self._inner.span, - floatrange_to_floatspan(other)) + other._inner) + elif isinstance(other, FloatSpan): + return adjacent_span_span(self._inner.span, other._inner) elif isinstance(other, TBox): return adjacent_tbox_tbox(self._inner, other._inner) elif isinstance(other, TNumber): diff --git a/pymeos/pymeos/collections/text/textset.py b/pymeos/pymeos/collections/text/textset.py index 4d4728a6..b878054e 100644 --- a/pymeos/pymeos/collections/text/textset.py +++ b/pymeos/pymeos/collections/text/textset.py @@ -142,7 +142,7 @@ def lowercase(self): MEOS Functions: textset_lower """ - return textset_lower(self._inner) + return self.__class__(_inner=textset_lower(self._inner)) def uppercase(self): """ @@ -154,7 +154,7 @@ def uppercase(self): MEOS Functions: textset_upper """ - return textset_upper(self._inner) + return self.__class__(_inner=textset_upper(self._inner)) # ------------------------- Set Operations -------------------------------- @@ -180,7 +180,8 @@ def intersection(self, other): intersection_textset_text, intersection_set_set """ if isinstance(other, str): - return text2cstring(intersection_textset_text(self._inner, other)[0]) + result = intersection_textset_text(self._inner, other) + return text2cstring(result[0]) if result is not None else None elif isinstance(other, TextSet): result = intersection_set_set(self._inner, other._inner) return TextSet(_inner=result) if result is not None else None diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index fd05df04..e413efb2 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -143,32 +143,32 @@ def to_tint(self) -> TInt: "interpolation to a temporal integer") return _TemporalFactory.create_temporal(tfloat_to_tint(self._inner)) - def to_floatrange(self) -> floatrange: + def to_floatrange(self) -> FloatSpan: """ Returns value span of `self`. Returns: - An :class:`floatrange` with the value span of `self`. + An :class:`FloatSpan` with the value span of `self`. MEOS Functions: tnumber_to_span """ - return floatspan_to_floatrange(tnumber_to_span(self._inner)) + return FloatSpan(_inner=tnumber_to_span(self._inner)) # ------------------------- Accessors ------------------------------------- - def value_range(self) -> floatrange: + def value_span(self) -> FloatSpan: """ Returns the value span of `self`. Returns: - An :class:`floatrange` with the value span of `self`. + An :class:`FloatSpan` with the value span of `self`. MEOS Functions: tnumber_to_span """ return self.to_floatrange() - def value_ranges(self) -> List[floatrange]: + def value_spans(self) -> FloatSpanSet: """ Returns the value spans of `self` taking into account gaps. @@ -822,10 +822,10 @@ def value_split(self, size: float, start: Optional[float] = 0) -> \ MEOS Functions: tfloat_value_split """ - fragments, values, count = tfloat_value_split(self._inner, size, start) + fragments, _, count = tfloat_value_split(self._inner, size, start) from ..factory import _TemporalFactory - return [_TemporalFactory.create_temporal(tiles[i]) for i in \ - range(new_count)] + return [_TemporalFactory.create_temporal(fragments[i]) for i in \ + range(count)] def value_time_split(self, value_size: float, duration: Union[str, timedelta], @@ -858,9 +858,9 @@ def value_time_split(self, value_size: float, dt = timedelta_to_interval(duration) \ if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) - tiles, new_count = tfloat_value_time_split(self._inner, value_size, - value_start, dt, st) - return [Temporal._factory(tiles[i]) for i in range(new_count)] + fragments, _, _, count = tfloat_value_time_split(self._inner, + value_size, dt, value_start, st) + return [Temporal._factory(fragments[i]) for i in range(count)] # ------------------------- Database Operations --------------------------- @staticmethod diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index 431df4db..b46c8676 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -5,7 +5,6 @@ from typing import Optional, Union, List, TYPE_CHECKING, Set, overload from pymeos_cffi import * -from spans.types import intrange, floatrange from .tnumber import TNumber from ..temporal import TInterpolation, Temporal, TInstant, TSequence, TSequenceSet @@ -156,7 +155,7 @@ def value_spans(self) -> IntSpanSet: Returns the value spans of `self` taking into account gaps. Returns: - A list of :class:`intrange` with the value spans of `self`. + An :class:`IntSpanSet` with the value spans of `self`. MEOS Functions: tint_spanset @@ -779,9 +778,9 @@ def value_time_split(self, value_size: int, dt = timedelta_to_interval(duration) \ if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) - tiles, new_count = tint_value_time_split(self._inner, value_size, - value_start, dt, st) - return [Temporal._factory(tiles[i]) for i in range(new_count)] + tiles, _, _, count = tint_value_time_split(self._inner, value_size, dt, + value_start, st) + return [Temporal._factory(tiles[i]) for i in range(count)] # ------------------------- Database Operations --------------------------- @staticmethod diff --git a/pymeos/pymeos/main/tnumber.py b/pymeos/pymeos/main/tnumber.py index 89a4d5d8..4064d7eb 100644 --- a/pymeos/pymeos/main/tnumber.py +++ b/pymeos/pymeos/main/tnumber.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from ..boxes import Box, TBox - from ..collections import Time + from ..collections import Time, IntSpan, IntSpanSet, FloatSpan, FloatSpanSet from .tint import TInt from .tfloat import TFloat @@ -133,8 +133,8 @@ def shift_scale_value(self, shift: Union[int, float] = None, return Temporal._factory(scaled) # ------------------------- Restrictions ---------------------------------- - def at(self, other: Union[intrange, floatrange, List[intrange], - List[floatrange], TBox, Time]) -> TG: + def at(self, other: Union[IntSpan, FloatSpan, IntSpanSet, FloatSpanSet, + TBox, Time]) -> TG: """ Returns a new temporal object with the values of `self` restricted to the value or time `other`. @@ -151,26 +151,23 @@ def at(self, other: Union[intrange, floatrange, List[intrange], temporal_at_period, temporal_at_periodset """ from ..boxes import TBox - if isinstance(other, intrange): - result = tnumber_at_span(self._inner, intrange_to_intspan(other)) - elif isinstance(other, floatrange): - result = tnumber_at_span(self._inner, floatrange_to_floatspan(other)) - elif isinstance(other, list) and isinstance(other[0], intrange): - spans = [intrange_to_intspan(ir) for ir in other] - spanset = spanset_make(spans, len(spans), True) - result = tnumber_at_spanset(self._inner, spanset) - elif isinstance(other, list) and isinstance(other[0], floatrange): - spans = [floatrange_to_floatspan(fr) for fr in other] - spanset = spanset_make(spans, len(spans), True) - result = tnumber_at_spanset(self._inner, spanset) + from ..collections import IntSpan, FloatSpan, IntSpanSet, FloatSpanSet + if isinstance(other, IntSpan): + result = tnumber_at_span(self._inner, other._inner) + elif isinstance(other, FloatSpan): + result = tnumber_at_span(self._inner, other._inner) + elif isinstance(other, IntSpanSet): + result = tnumber_at_spanset(self._inner, other._inner) + elif isinstance(other, FloatSpanSet): + result = tnumber_at_spanset(self._inner, other._inner) elif isinstance(other, TBox): result = tnumber_at_tbox(self._inner, other._inner) else: return super().at(other) return Temporal._factory(result) - def minus(self, other: Union[intrange, floatrange, List[intrange], - List[floatrange], TBox, Time]) -> TG: + def minus(self, other: Union[IntSpan, FloatSpan, IntSpanSet, FloatSpanSet, + TBox, Time]) -> TG: """ Returns a new temporal object with the values of `self` restricted to the complement of the value or time `other`. @@ -188,20 +185,15 @@ def minus(self, other: Union[intrange, floatrange, List[intrange], temporal_minus_period, temporal_minus_periodset """ from ..boxes import TBox - if isinstance(other, intrange): - result = tnumber_minus_span(self._inner, - intrange_to_intspan(other)) - elif isinstance(other, floatrange): - result = tnumber_minus_span(self._inner, - floatrange_to_floatspan(other)) - elif isinstance(other, list) and isinstance(other[0], intrange): - spans = [intrange_to_intspan(ir) for ir in other] - spanset = spanset_make(spans, len(spans), True) - result = tnumber_minus_spanset(self._inner, spanset) - elif isinstance(other, list) and isinstance(other[0], floatrange): - spans = [floatrange_to_floatspan(fr) for fr in other] - spanset = spanset_make(spans, len(spans), True) - result = tnumber_minus_spanset(self._inner, spanset) + from ..collections import IntSpan, FloatSpan, IntSpanSet, FloatSpanSet + if isinstance(other, IntSpan): + result = tnumber_minus_span(self._inner, other._inner) + elif isinstance(other, FloatSpan): + result = tnumber_minus_span(self._inner, other._inner) + elif isinstance(other, IntSpanSet): + result = tnumber_minus_spanset(self._inner, other._inner) + elif isinstance(other, FloatSpanSet): + result = tnumber_minus_spanset(self._inner, other._inner) elif isinstance(other, TBox): result = tnumber_minus_tbox(self._inner, other._inner) else: diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index 6907f388..abc4fccc 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -228,7 +228,7 @@ def test_as_hexwkb(self, tbox, expected): ) def test_to_floatspan(self, tbox, expected): tb = tbox.to_floatspan() - assert isinstance(tb, floatspan) + assert isinstance(tb, FloatSpan) assert tb == expected @pytest.mark.parametrize( diff --git a/pymeos/tests/collections/text/textset_test.py b/pymeos/tests/collections/text/textset_test.py index 8bfd4e87..a0f5ccad 100644 --- a/pymeos/tests/collections/text/textset_test.py +++ b/pymeos/tests/collections/text/textset_test.py @@ -93,123 +93,6 @@ def test_hash(self): assert hash(self.tset) == 3145376687 -class TestTextSetPositionFunctions(TestTextSet): - string = 'A' - other = TextSet('{a, BB, ccc}') - # period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') - # periodset = PeriodSet( - # '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') - # timestamp = datetime(year=2020, month=1, day=1) - # TextSet = TextSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') - # instant = TFloatInst('1.0@2020-01-01') - # discrete_sequence = TFloatSeq('{1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31}') - # stepwise_sequence = TFloatSeq('Interp=Step;(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') - # continuous_sequence = TFloatSeq('(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') - # sequence_set = TFloatSeqSet('{(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31], ' - # '(1.0@2021-01-01, 3.0@2021-01-10, 10.0@2021-01-20, 0.0@2021-01-31]}') - - @pytest.mark.parametrize( - 'other', - [string, other], - ids=['string', 'TextSet'] - ) - def test_is_adjacent(self, other): - self.tset.is_adjacent(other) - - # @pytest.mark.parametrize( - # 'other', - # [TextSet, period, periodset, instant, discrete_sequence, stepwise_sequence, continuous_sequence, - # sequence_set], - # ids=['TextSet', 'period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', - # 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_contained_in(self, other): - # self.tset.is_contained_in(other) - # - # @pytest.mark.parametrize( - # 'other', - # [timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence], - # ids=['timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set'] - # ) - # def test_contains(self, other): - # self.tset.contains(other) - # _ = other in self.tset - # - # # - # @pytest.mark.parametrize( - # 'other', - # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence], - # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_overlaps(self, other): - # self.tset.overlaps(other) - # - # @pytest.mark.parametrize( - # 'other', - # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence], - # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_same(self, other): - # self.periodset.is_same(other) - # - # @pytest.mark.parametrize( - # 'other', - # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence], - # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_before(self, other): - # self.tset.is_before(other) - # - # @pytest.mark.parametrize( - # 'other', - # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence], - # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_over_or_before(self, other): - # self.tset.is_over_or_before(other) - # - # @pytest.mark.parametrize( - # 'other', - # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence], - # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_after(self, other): - # self.tset.is_after(other) - # - # @pytest.mark.parametrize( - # 'other', - # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence], - # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_over_or_after(self, other): - # self.tset.is_over_or_after(other) - # - # @pytest.mark.parametrize( - # 'other', - # [period, periodset, timestamp, TextSet, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence], - # ids=['period', 'periodset', 'timestamp', 'TextSet', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_distance(self, other): - # self.tset.distance(other) - - class TestTextSetSetFunctions(TestTextSet): string = 'A' other = TextSet('{a, BB, ccc}') diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index c98ca64d..500d3c76 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -7,7 +7,8 @@ from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, \ TFloat, TFloatInst, TFloatSeq, TFloatSeqSet, \ TInt, TIntInst, TIntSeq, TIntSeqSet, \ - TInterpolation, TBox, TimestampSet, Period, PeriodSet, IntSpan, FloatSpan + TInterpolation, TBox, TimestampSet, Period, PeriodSet, \ + IntSpan, FloatSpan, IntSpanSet, FloatSpanSet from tests.conftest import TestPyMEOS @@ -447,22 +448,21 @@ def test_values(self, temporal, expected): ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_value_range(self, temporal, expected): - assert temporal.value_range() == expected + def test_value_span(self, temporal, expected): + assert temporal.value_span() == expected - @pytest.mark.parametrize( - 'temporal, expected', - [ - (tfi, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)]), - (tfds, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), - (tfs, [FloatSpan(lower=1.5, upper=2.5, lower_inc=True, upper_inc=True)]), - (tfss, [FloatSpan(lower=1.5, upper=2.5, lower_inc=True, upper_inc=True)]), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - ) - def test_value_ranges(self, temporal, expected): - assert temporal.value_ranges() == expected + # @pytest.mark.parametrize( + # 'temporal, expected', + # [ + # (tfi, FloatSpanSet('{[1.5, 1.5]}')), + # (tfds, FloatSpanSet('{[1.5, 1.5], [2.5, 2.5]}')), + # (tfs, FloatSpanSet('{[1.5, 1.5]}')), + # (tfss, FloatSpanSet('{[1.5, 1.5]}')), + # ], + # ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + # ) + # def test_value_spans(self, temporal, expected): + # assert temporal.value_spans() == expected @pytest.mark.parametrize( 'temporal, expected', diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index 90d60538..f9ed1809 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -426,28 +426,28 @@ def test_values(self, temporal, expected): @pytest.mark.parametrize( 'temporal, expected', [ - (tii, IntSpan(1, 1, True, True)), - (tids, IntSpan(1, 2, True, True)), - (tis, IntSpan(1, 2, True, True)), - (tiss, IntSpan(1, 2, True, True)), + (tii, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True)), + (tids, IntSpan(lower=1, upper=2, lower_inc=True, upper_inc=True)), + (tis, IntSpan(lower=1, upper=2, lower_inc=True, upper_inc=True)), + (tiss, IntSpan(lower=1, upper=2, lower_inc=True, upper_inc=True)), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) - def test_value_range(self, temporal, expected): - assert temporal.value_range() == expected + def test_value_span(self, temporal, expected): + assert temporal.value_span() == expected - @pytest.mark.parametrize( - 'temporal, expected', - [ - (tii, [IntSpan(1, 1, True, True)]), - (tids, [IntSpan(1, 2, True, True)]), - (tis, [IntSpan(1, 2, True, True)]), - (tiss, [IntSpan(1, 2, True, True)]), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - ) - def test_value_ranges(self, temporal, expected): - assert temporal.value_ranges() == expected + # @pytest.mark.parametrize( + # 'temporal, expected', + # [ + # (tii, IntSpanSet('{[1,1]}')), + # (tids, IntSpanSet('{[1,2]}')), + # (tis, IntSpanSet('{[1,2]}')), + # (tiss, IntSpanSet('{[1,2]}')), + # ], + # ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + # ) + # def test_value_spans(self, temporal, expected): + # assert temporal.value_spans() == expected @pytest.mark.parametrize( 'temporal, expected', @@ -1373,29 +1373,29 @@ def test_at_time(self, temporal, restrictor, expected): [ (tii, 1, TIntInst('1@2019-09-01')), (tii, 2, None), - (tii, IntSpan(1, 1, True, True), TIntInst('1@2019-09-01')), + (tii, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntInst('1@2019-09-01')), (tii, [1,2], tii), - # (tii, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], + # (tii, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], # TIntInst('1@2019-09-01')), (tids, 1, TIntSeq('{1@2019-09-01}')), (tids, 2, TIntSeq('{2@2019-09-02}')), - (tids, IntSpan(1, 1, True, True), TIntSeq('{1@2019-09-01}')), + (tids, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeq('{1@2019-09-01}')), (tids, [1,2], tids), - # (tids, [IntSpan(1, 1, True, True), tids), + # (tids, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), tids), (tis, 1, TIntSeq('[1@2019-09-01, 1@2019-09-02)')), (tis, 2, TIntSeq('[2@2019-09-02]')), - (tis, IntSpan(1, 1, True, True), TIntSeq('[1@2019-09-01, 1@2019-09-02)')), + (tis, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeq('[1@2019-09-01, 1@2019-09-02)')), (tis, [1,2], tis), - # (tis, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], + # (tis, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], # TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), (tiss, 1, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), (tiss, 2, TIntSeqSet('{[2@2019-09-02]}')), - (tiss, IntSpan(1, 1, True, True), TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), + (tiss, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), (tiss, [1,2], tiss) - # (tiss, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], + # (tiss, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], # tiss), ], ids=['Instant-1', 'Instant-2', 'Instant-Range', @@ -1481,30 +1481,30 @@ def test_minus_time(self, temporal, restrictor, expected): [ (tii, 1, None), (tii, 2, TIntInst('1@2019-09-01')), - (tii, IntSpan(1, 1, True, True), None), + (tii, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), None), (tii, [1,2], None), - # (tii, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], + # (tii, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], # None), (tids, 1, TIntSeq('{2@2019-09-02}')), (tids, 2, TIntSeq('{1@2019-09-01}')), - (tids, IntSpan(1, 1, True, True), TIntSeq('{2@2019-09-02}')), + (tids, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeq('{2@2019-09-02}')), (tids, [1,2], None), - # (tids, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], + # (tids, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], # None), (tis, 1, TIntSeqSet('{[2@2019-09-02]}')), (tis, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02)}')), - (tis, IntSpan(1, 1, True, True), TIntSeqSet('{[2@2019-09-02]}')), + (tis, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeqSet('{[2@2019-09-02]}')), (tis, [1,2], None), - # (tis, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], + # (tis, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], # None), (tiss, 1, TIntSeqSet('{[2@2019-09-02]}')), (tiss, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), - (tis, IntSpan(1, 1, True, True), TIntSeqSet('{[2@2019-09-02]}')), + (tis, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeqSet('{[2@2019-09-02]}')), (tiss, [1,2], None) - # (tiss, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)], + # (tiss, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], # None), ], ids=['Instant-1', 'Instant-2', 'Instant-Range', @@ -1558,9 +1558,9 @@ def test_minus_min(self, temporal, expected): (tii, period_set), (tii, 1), (tii, 2), - (tii, IntSpan(1, 1, True, True)), + (tii, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True)), (tii, [1,2]), - # (tii, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)]), + # (tii, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)]), (tids, timestamp), (tids, timestamp_set), @@ -1568,9 +1568,9 @@ def test_minus_min(self, temporal, expected): (tids, period_set), (tids, 1), (tids, 2), - (tids, IntSpan(1, 1, True, True)), + (tids, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True)), (tids, [1,2]), - # (tds, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)]), + # (tds, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)]), (tis, timestamp), (tis, timestamp_set), @@ -1578,9 +1578,9 @@ def test_minus_min(self, temporal, expected): (tis, period_set), (tis, 1), (tis, 2), - (tis, IntSpan(1, 1, True, True)), + (tis, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True)), (tis, [1,2]), - # (tis, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)]), + # (tis, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)]), (tiss, timestamp), (tiss, timestamp_set), @@ -1588,9 +1588,9 @@ def test_minus_min(self, temporal, expected): (tiss, period_set), (tiss, 1), (tiss, 2), - (tiss, IntSpan(1, 1, True, True)), + (tiss, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True)), (tiss, [1,2]), - # (tiss, [IntSpan(1, 1, True, True), IntSpan(2, 2, True, True)]), + # (tiss, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)]), ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', 'Instant-PeriodSet', 'Instant-1', 'Instant-2', From 6bf8536ed80234e9a838abf0e999171e7f6b0fa7 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 28 Sep 2023 23:42:15 +0200 Subject: [PATCH 056/101] Improve tests --- pymeos/pymeos/collections/base/collection.py | 12 +- pymeos/pymeos/collections/base/set.py | 76 ++-- .../tests/collections/number/intset_test.py | 324 ++++++++++++++++++ 3 files changed, 361 insertions(+), 51 deletions(-) create mode 100644 pymeos/tests/collections/number/intset_test.py diff --git a/pymeos/pymeos/collections/base/collection.py b/pymeos/pymeos/collections/base/collection.py index 74c25957..49b09162 100644 --- a/pymeos/pymeos/collections/base/collection.py +++ b/pymeos/pymeos/collections/base/collection.py @@ -17,9 +17,9 @@ class Collection(Generic[T], ABC): # ------------------------- Topological Operations ------------------------ - @abstractmethod - def is_adjacent(self, other) -> bool: - raise NotImplementedError() + # @abstractmethod + # def is_adjacent(self, other) -> bool: + # raise NotImplementedError() @abstractmethod def is_contained_in(self, container) -> bool: @@ -37,9 +37,9 @@ def __contains__(self, item): def overlaps(self, other) -> bool: raise NotImplementedError() - @abstractmethod - def is_same(self, other) -> bool: - raise NotImplementedError() + # @abstractmethod + # def is_same(self, other) -> bool: + # raise NotImplementedError() # ------------------------- Position Operations --------------------------- @abstractmethod diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py index a5183e72..bded6265 100644 --- a/pymeos/pymeos/collections/base/set.py +++ b/pymeos/pymeos/collections/base/set.py @@ -232,27 +232,25 @@ def __hash__(self) -> int: return set_hash(self._inner) # ------------------------- Topological Operations ------------------------ - def is_adjacent(self, other) -> bool: - """ - Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them - contains it. - Args: - other: object to compare with - - Returns: - True if adjacent, False otherwise - - MEOS Functions: - adjacent_span_span, adjacent_spanset_span - """ - from .span import Span - from .spanset import SpanSet - if isinstance(other, Span): - return adjacent_span_span(set_span(self._inner), other._inner) - elif isinstance(other, SpanSet): - return adjacent_spanset_spanset(other._inner, set_to_spanset(self._inner)) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') + # def is_adjacent(self, other) -> bool: + # """ + # Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them + # contains it. + # Args: + # other: object to compare with + + # Returns: + # True if adjacent, False otherwise + + # MEOS Functions: + # adjacent_span_span, adjacent_spanset_span + # """ + # if isinstance(other, Span): + # return adjacent_span_span(set_span(self._inner), other._inner) + # elif isinstance(other, SpanSet): + # return adjacent_spanset_spanset(other._inner, set_to_spanset(self._inner)) + # else: + # raise TypeError(f'Operation not supported with type {other.__class__}') def is_contained_in(self, container) -> bool: """ @@ -267,13 +265,7 @@ def is_contained_in(self, container) -> bool: MEOS Functions: contained_span_span, contained_span_spanset, contained_set_set, contained_spanset_spanset """ - from .span import Span - from .spanset import SpanSet - if isinstance(container, Span): - return contained_span_span(set_span(self._inner), container._inner) - elif isinstance(container, SpanSet): - return contained_span_spanset(set_span(self._inner), container._inner) - elif isinstance(container, Set): + if isinstance(container, Set): return contained_set_set(self._inner, container._inner) else: raise TypeError(f'Operation not supported with type {container.__class__}') @@ -325,31 +317,25 @@ def overlaps(self, other) -> bool: MEOS Functions: overlaps_set_set, overlaps_span_span, overlaps_spanset_spanset """ - from .span import Span - from .spanset import SpanSet if isinstance(other, Set): return overlaps_set_set(self._inner, other._inner) - elif isinstance(other, Span): - return overlaps_span_span(set_span(self._inner), other._inner) - elif isinstance(other, SpanSet): - return overlaps_spanset_spanset(set_to_spanset(self._inner), other._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') - def is_same(self, other) -> bool: - """ - Returns whether the bounding span of `self` is the same as the bounding span of `other`. + # def is_same(self, other) -> bool: + # """ + # Returns whether the bounding span of `self` is the same as the bounding span of `other`. - Args: - other: An object to compare to `self`. + # Args: + # other: An object to compare to `self`. - Returns: - True if same, False otherwise. + # Returns: + # True if same, False otherwise. - See Also: - :meth:`Span.is_same` - """ - return self.to_span().is_same(other) + # See Also: + # :meth:`Span.is_same` + # """ + # return self.to_span().is_same(other) # ------------------------- Position Operations --------------------------- def is_left(self, other) -> bool: diff --git a/pymeos/tests/collections/number/intset_test.py b/pymeos/tests/collections/number/intset_test.py new file mode 100644 index 00000000..3d9776b2 --- /dev/null +++ b/pymeos/tests/collections/number/intset_test.py @@ -0,0 +1,324 @@ +from copy import copy +from typing import List + +import pytest + +from pymeos import IntSet, IntSpan, IntSpanSet, TIntInst, TIntSeq, TIntSeqSet, \ + TBox, STBox +from tests.conftest import TestPyMEOS + + +class TestIntSet(TestPyMEOS): + intset = IntSet('{1, 2, 3}') + + @staticmethod + def assert_intset_equality(intset: IntSet, values: List[int]): + assert intset.num_elements() == len(values) + assert intset.elements() == values + + +class TestIntSetConstructors(TestIntSet): + + def test_string_constructor(self): + self.assert_intset_equality(self.intset, [1, 2, 3]) + + def test_list_constructor(self): + intset = IntSet(elements=[1, 2, 3]) + self.assert_intset_equality(intset, [1, 2, 3]) + + def test_hexwkb_constructor(self): + intset = IntSet.from_hexwkb('010C000103000000010000000200000003000000') + self.assert_intset_equality(intset, [1, 2, 3]) + + def test_from_as_constructor(self): + assert self.intset == IntSet(str(self.intset)) + assert self.intset == IntSet.from_wkb(self.intset.as_wkb()) + assert self.intset == IntSet.from_hexwkb(self.intset.as_hexwkb()) + + def test_copy_constructor(self): + intset_copy = copy(self.intset) + assert self.intset == intset_copy + assert self.intset is not intset_copy + + +class TestIntSetOutputs(TestIntSet): + + def test_str(self): + assert str(self.intset) == '{1, 2, 3}' + + def test_repr(self): + assert repr( + self.intset) == 'IntSet({1, 2, 3})' + + def test_as_hexwkb(self): + assert self.intset.as_hexwkb() == '010C000103000000010000000200000003000000' + + +# class TestIntConversions(TestIntSet): + + # def test_to_spanset(self): + # assert self.intset.to_spanset() == IntSpanSet( + # '{[1, 1], [2, 2], [3, 3]}') + + +class TestIntSetAccessors(TestIntSet): + + def test_to_span(self): + assert self.intset.to_span() == IntSpan('[1, 3]') + + def test_num_elements(self): + assert self.intset.num_elements() == 3 + + def test_start_element(self): + assert self.intset.start_element() == 1 + + def test_end_element(self): + assert self.intset.end_element() == 3 + + def test_element_n(self): + assert self.intset.element_n(1) == 2 + + def test_element_n_out_of_range(self): + with pytest.raises(IndexError): + self.intset.element_n(3) + + def test_elements(self): + assert self.intset.elements() == [1, 2, 3] + + def test_hash(self): + assert hash(self.intset) == 3969573766 + + +class TestIntSetTopologicalFunctions(TestIntSet): + value = 5 + other = IntSet('{5, 10}') + + @pytest.mark.parametrize( + 'arg, result', + [ + (other, False), + ], + ids=['other'] + ) + def test_is_contained_in(self, arg, result): + assert self.intset.is_contained_in(arg) == result + + @pytest.mark.parametrize( + 'arg, result', + [ + (value, False), + (other, False), + ], + ids=['value', 'other'] + ) + def test_contains(self, arg, result): + assert self.intset.contains(arg) == result + + @pytest.mark.parametrize( + 'arg, result', + [ + (other, False), + ], + ids=['other'] + ) + def test_overlaps(self, arg, result): + assert self.intset.overlaps(arg) == result + +# class TestIntSetPositionFunctions(TestIntSet): + # value = 5 + # other = IntSet('{5, 10}') + + # @pytest.mark.parametrize( + # 'other', + # [intset, intspan, # intspanset, + # instant, discrete_sequence, stepwise_sequence, continuous_sequence, + # sequence_set, tbox, + # stbox], + # ids=['intset', 'intspan', # 'intspanset', + # 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_contained_in(self, other): + # self.intset.is_contained_in(other) + + # @pytest.mark.parametrize( + # 'other', + # [value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence], + # ids=['value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set'] + # ) + # def test_contains(self, other): + # self.intset.contains(other) + # _ = other in self.intset + + + # @pytest.mark.parametrize( + # 'other', + # [intspan, # intspanset, + # value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence, tbox, stbox], + # ids=['intspan', # 'intspanset', + # 'value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_overlaps(self, other): + # self.intset.overlaps(other) + + # @pytest.mark.parametrize( + # 'other', + # [intspan, # intspanset, + # value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence, tbox, stbox], + # ids=['intspan', # 'intspanset', + # 'value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_same(self, other): + # self.intset.is_same(other) + + # @pytest.mark.parametrize( + # 'other', + # [intspan, # intspanset, + # value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence, tbox, stbox], + # ids=['intspan', # 'intspanset', + # 'value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_before(self, other): + # self.intset.is_before(other) + + # @pytest.mark.parametrize( + # 'other', + # [intspan, # intspanset, + # value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence, tbox, stbox], + # ids=['intspan', # 'intspanset', + # 'value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_over_or_before(self, other): + # self.intset.is_over_or_before(other) + + # @pytest.mark.parametrize( + # 'other', + # [intspan, # intspanset, + # value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, + # continuous_sequence, tbox, stbox], + # ids=['intspan', # 'intspanset', + # 'value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_after(self, other): + # self.intset.is_after(other) + + # @pytest.mark.parametrize( + # 'other', + # [intspan, # intspanset, + # value, intset, instant, discrete_sequence, + # stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], + # ids=['intspan', # 'intspanset', + # 'value', 'intset', 'instant', + # 'discrete_sequence', 'stepwise_sequence', + # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_is_over_or_after(self, other): + # self.intset.is_over_or_after(other) + + # @pytest.mark.parametrize( + # 'other', + # [intspan, # intspanset, + # value, intset, instant, discrete_sequence, + # stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], + # ids=['intspan', # 'intspanset', + # 'value', 'intset', 'instant', + # 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', + # 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_distance(self, other): + # self.intset.distance(other) + + +class TestIntSetSetFunctions(TestIntSet): + value = 1 + intset = IntSet('{1, 10}') + + @pytest.mark.parametrize( + 'other', + [value, intset], + ids=['value', 'intset'] + ) + def test_intersection(self, other): + self.intset.intersection(other) + self.intset * other + + @pytest.mark.parametrize( + 'other', + [value, intset], + ids=['value', 'intset'] + ) + def test_union(self, other): + self.intset.union(other) + self.intset + other + + @pytest.mark.parametrize( + 'other', + [value, intset], + ids=['value', 'intset'] + ) + def test_minus(self, other): + self.intset.minus(other) + self.intset - other + + +class TestIntSetComparisons(TestIntSet): + intset = IntSet('{1, 10}') + other = IntSet('{2, 10}') + + def test_eq(self): + _ = self.intset == self.other + + def test_ne(self): + _ = self.intset != self.other + + def test_lt(self): + _ = self.intset < self.other + + def test_le(self): + _ = self.intset <= self.other + + def test_gt(self): + _ = self.intset > self.other + + def test_ge(self): + _ = self.intset >= self.other + + +# class TestIntSetTransformationFunctions(TestIntSet): + + # @pytest.mark.parametrize( + # 'delta,result', + # [(4, [5, 6, 8]), + # (-4, [-3, -1, 0]), + # ], + # ids=['positive delta', 'negative delta'] + # ) + # def test_shift(self, delta, result): + # shifted = self.intset.shift(delta) + # self.assert_intset_equality(shifted, result) + + # @pytest.mark.parametrize( + # 'delta,result', + # [(6, [1, 4, 7])], + # ids=['positive'] + # ) + # def test_scale(self, delta, result): + # scaled = self.intset.scale(delta) + # self.assert_intset_equality(scaled, result) + + # def test_shift_scale(self): + # shifted_scaled = self.intset.shift_scale(4, 4) + # self.assert_intset_equality(shifted_scaled, [5, 7, 9]) + + From 833f3ae22b6ecc213f44822fe39e62f4cdc60959 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Fri, 29 Sep 2023 10:16:38 +0200 Subject: [PATCH 057/101] Improve tests --- .../collections/time/timestampset_test.py | 105 ++---- pymeos/tests/time/__init__.py | 0 pymeos/tests/time/timestampset_test.py | 333 ------------------ 3 files changed, 27 insertions(+), 411 deletions(-) delete mode 100644 pymeos/tests/time/__init__.py delete mode 100644 pymeos/tests/time/timestampset_test.py diff --git a/pymeos/tests/collections/time/timestampset_test.py b/pymeos/tests/collections/time/timestampset_test.py index 081d4c6b..3d7be503 100644 --- a/pymeos/tests/collections/time/timestampset_test.py +++ b/pymeos/tests/collections/time/timestampset_test.py @@ -56,8 +56,7 @@ def test_str(self): assert str(self.ts_set) == '{"2019-09-01 00:00:00+00", "2019-09-02 00:00:00+00", "2019-09-03 00:00:00+00"}' def test_repr(self): - assert repr( - self.ts_set) == 'TimestampSet({"2019-09-01 00:00:00+00", "2019-09-02 00:00:00+00", "2019-09-03 00:00:00+00"})' + assert repr(self.ts_set) == 'TimestampSet({"2019-09-01 00:00:00+00", "2019-09-02 00:00:00+00", "2019-09-03 00:00:00+00"})' def test_as_hexwkb(self): assert self.ts_set.as_hexwkb() == '012000010300000000A01E4E713402000000F66B853402000060CD8999340200' @@ -108,123 +107,73 @@ def test_hash(self): class TestTimestampSetPositionFunctions(TestTimestampSet): - period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') - periodset = PeriodSet( - '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') timestamp = datetime(year=2020, month=1, day=1) timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') - instant = TFloatInst('1.0@2020-01-01') - discrete_sequence = TFloatSeq('{1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31}') - stepwise_sequence = TFloatSeq('Interp=Step;(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') - continuous_sequence = TFloatSeq('(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') - sequence_set = TFloatSeqSet('{(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31], ' - '(1.0@2021-01-01, 3.0@2021-01-10, 10.0@2021-01-20, 0.0@2021-01-31]}') - tbox = TBox('TBox XT([0, 10),[2020-01-01, 2020-01-31])') - stbox = STBox('STBOX ZT(((1.0,2.0,3.0),(4.0,5.0,6.0)),[2001-01-01, 2001-01-02])') - - @pytest.mark.parametrize( - 'other', - [period, periodset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - ) - def test_is_adjacent(self, other): - self.timestampset.is_adjacent(other) @pytest.mark.parametrize( - 'other', - [timestampset, period, periodset, instant, discrete_sequence, stepwise_sequence, continuous_sequence, - sequence_set, tbox, - stbox], - ids=['timestampset', 'period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', - 'sequence_set', 'tbox', 'stbox'] + 'other, expected', + [(timestampset, False)], + ids=['timestampset'] ) - def test_is_contained_in(self, other): - self.timestampset.is_contained_in(other) + def test_is_contained_in(self, other, expected): + assert self.ts_set.is_contained_in(other) == expected @pytest.mark.parametrize( 'other', - [timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence], - ids=['timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set'] + [timestamp, timestampset], + ids=['timestamp', 'timestampset'] ) def test_contains(self, other): - self.timestampset.contains(other) + self.ts_set.contains(other) _ = other in self.timestampset - # @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + [timestampset], + ids=['timestampset'] ) def test_overlaps(self, other): - self.timestampset.overlaps(other) - - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - ) - def test_is_same(self, other): - self.periodset.is_same(other) + self.ts_set.overlaps(other) @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + [timestamp, timestampset], + ids=['timestamp', 'timestampset'] ) def test_is_before(self, other): - self.timestampset.is_before(other) + self.ts_set.is_before(other) @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + [timestamp, timestampset], + ids=['timestamp', 'timestampset'] ) def test_is_over_or_before(self, other): - self.timestampset.is_over_or_before(other) + self.ts_set.is_over_or_before(other) @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + [timestamp, timestampset], + ids=['timestamp', 'timestampset'] ) def test_is_after(self, other): - self.timestampset.is_after(other) + self.ts_set.is_after(other) @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + [timestamp, timestampset], + ids=['timestamp', 'timestampset'] ) def test_is_over_or_after(self, other): - self.timestampset.is_over_or_after(other) + self.ts_set.is_over_or_after(other) @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] + [timestamp, timestampset], + ids=['timestamp', 'timestampset'] ) def test_distance(self, other): - self.timestampset.distance(other) + self.ts_set.distance(other) class TestTimestampSetSetFunctions(TestTimestampSet): diff --git a/pymeos/tests/time/__init__.py b/pymeos/tests/time/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pymeos/tests/time/timestampset_test.py b/pymeos/tests/time/timestampset_test.py deleted file mode 100644 index b95b0dc2..00000000 --- a/pymeos/tests/time/timestampset_test.py +++ /dev/null @@ -1,333 +0,0 @@ -from copy import copy -from datetime import datetime, timezone, timedelta -from typing import List - -import pytest - -from pymeos import Period, PeriodSet, TimestampSet, TFloatInst, TFloatSeq, STBox, TFloatSeqSet, TBox -from tests.conftest import TestPyMEOS - - -class TestTimestampSet(TestPyMEOS): - ts_set = TimestampSet('{2019-09-01 00:00:00+0, 2019-09-02 00:00:00+0, 2019-09-03 00:00:00+0}') - - @staticmethod - def assert_timestampset_equality(ts_set: TimestampSet, - timestamps: List[datetime]): - assert ts_set.num_elements() == len(timestamps) - assert ts_set.elements() == timestamps - - -class TestTimestampSetConstructors(TestTimestampSet): - - def test_string_constructor(self): - self.assert_timestampset_equality(self.ts_set, [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) - - def test_list_constructor(self): - ts_set = TimestampSet(elements=[datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) - self.assert_timestampset_equality(ts_set, [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) - - def test_hexwkb_constructor(self): - ts_set = TimestampSet.from_hexwkb('012000010300000000A01E4E713402000000F66B853402000060CD8999340200') - self.assert_timestampset_equality(ts_set, [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc)]) - - def test_from_as_constructor(self): - assert self.ts_set == TimestampSet(str(self.ts_set)) - assert self.ts_set == TimestampSet.from_wkb(self.ts_set.as_wkb()) - assert self.ts_set == TimestampSet.from_hexwkb(self.ts_set.as_hexwkb()) - - def test_copy_constructor(self): - ts_set_copy = copy(self.ts_set) - assert self.ts_set == ts_set_copy - assert self.ts_set is not ts_set_copy - - -class TestTimestampSetOutputs(TestTimestampSet): - - def test_str(self): - assert str(self.ts_set) == '{"2019-09-01 00:00:00+00", "2019-09-02 00:00:00+00", "2019-09-03 00:00:00+00"}' - - def test_repr(self): - assert repr( - self.ts_set) == 'TimestampSet({"2019-09-01 00:00:00+00", "2019-09-02 00:00:00+00", "2019-09-03 00:00:00+00"})' - - def test_as_hexwkb(self): - assert self.ts_set.as_hexwkb() == '012000010300000000A01E4E713402000000F66B853402000060CD8999340200' - - -class TestTimestampConversions(TestTimestampSet): - - def test_to_periodset(self): - assert self.ts_set.to_periodset() == PeriodSet( - '{[2019-09-01 00:00:00+00, 2019-09-01 00:00:00+00], ' - '[2019-09-02 00:00:00+00, 2019-09-02 00:00:00+00], ' - '[2019-09-03 00:00:00+00, 2019-09-03 00:00:00+00]}') - - -class TestTimestampSetAccessors(TestTimestampSet): - - def test_duration(self): - assert self.ts_set.duration() == timedelta(days=2) - - def test_period(self): - assert self.ts_set.to_period() == Period('[2019-09-01 00:00:00+00, 2019-09-03 00:00:00+00]') - - def test_num_timestamps(self): - assert self.ts_set.num_elements() == 3 - assert len(self.ts_set) == 3 - - def test_start_timestamp(self): - assert self.ts_set.start_element() == datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc) - - def test_end_timestamp(self): - assert self.ts_set.end_element() == datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc) - - def test_timestamp_n(self): - assert self.ts_set.element_n(1) == datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc) - - def test_timestamp_n_out_of_range(self): - with pytest.raises(IndexError): - self.ts_set.element_n(3) - - def test_timestamps(self): - assert self.ts_set.elements() == [datetime(2019, 9, 1, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 2, 0, 0, 0, tzinfo=timezone.utc), - datetime(2019, 9, 3, 0, 0, 0, tzinfo=timezone.utc), - ] - - def test_hash(self): - assert hash(self.ts_set) == 527267058 - - -class TestTimestampSetPositionFunctions(TestTimestampSet): - period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') - periodset = PeriodSet( - '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') - timestamp = datetime(year=2020, month=1, day=1) - timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') - instant = TFloatInst('1.0@2020-01-01') - discrete_sequence = TFloatSeq('{1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31}') - stepwise_sequence = TFloatSeq('Interp=Step;(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') - continuous_sequence = TFloatSeq('(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') - sequence_set = TFloatSeqSet('{(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31], ' - '(1.0@2021-01-01, 3.0@2021-01-10, 10.0@2021-01-20, 0.0@2021-01-31]}') - tbox = TBox('TBox XT([0, 10),[2020-01-01, 2020-01-31])') - stbox = STBox('STBOX ZT(((1.0,2.0,3.0),(4.0,5.0,6.0)),[2001-01-01, 2001-01-02])') - - @pytest.mark.parametrize( - 'other', - [period, periodset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - ) - def test_is_adjacent(self, other): - self.timestampset.is_adjacent(other) - - @pytest.mark.parametrize( - 'other', - [timestampset, period, periodset, instant, discrete_sequence, stepwise_sequence, continuous_sequence, - sequence_set, tbox, - stbox], - ids=['timestampset', 'period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', - 'sequence_set', 'tbox', 'stbox'] - ) - def test_is_contained_in(self, other): - self.timestampset.is_contained_in(other) - - @pytest.mark.parametrize( - 'other', - [timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence], - ids=['timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set'] - ) - def test_contains(self, other): - self.timestampset.contains(other) - _ = other in self.timestampset - - # - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - ) - def test_overlaps(self, other): - self.timestampset.overlaps(other) - - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - ) - def test_is_same(self, other): - self.periodset.is_same(other) - - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - ) - def test_is_before(self, other): - self.timestampset.is_before(other) - - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - ) - def test_is_over_or_before(self, other): - self.timestampset.is_over_or_before(other) - - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - ) - def test_is_after(self, other): - self.timestampset.is_after(other) - - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - ) - def test_is_over_or_after(self, other): - self.timestampset.is_over_or_after(other) - - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, - continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', - 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - ) - def test_distance(self, other): - self.timestampset.distance(other) - - -class TestTimestampSetSetFunctions(TestTimestampSet): - timestamp = datetime(year=2020, month=1, day=1) - timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') - period = Period('(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0)') - periodset = PeriodSet( - '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') - - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset], - ids=['period', 'periodset', 'timestamp', 'timestampset'] - ) - def test_intersection(self, other): - self.timestampset.intersection(other) - self.timestampset * other - - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset], - ids=['period', 'periodset', 'timestamp', 'timestampset'] - ) - def test_union(self, other): - self.timestampset.union(other) - self.timestampset + other - - @pytest.mark.parametrize( - 'other', - [period, periodset, timestamp, timestampset], - ids=['period', 'periodset', 'timestamp', 'timestampset'] - ) - def test_minus(self, other): - self.timestampset.minus(other) - self.timestampset - other - - -class TestTimestampSetComparisons(TestTimestampSet): - timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') - other = TimestampSet('{2020-01-02 00:00:00+0, 2020-03-31 00:00:00+0}') - - def test_eq(self): - _ = self.timestampset == self.other - - def test_ne(self): - _ = self.timestampset != self.other - - def test_lt(self): - _ = self.timestampset < self.other - - def test_le(self): - _ = self.timestampset <= self.other - - def test_gt(self): - _ = self.timestampset > self.other - - def test_ge(self): - _ = self.timestampset >= self.other - - -class TestTimestampSetManipulationFunctions(TestTimestampSet): - timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-02 00:00:00+0, 2020-01-04 00:00:00+0}') - - @pytest.mark.parametrize( - 'delta,result', - [(timedelta(days=4), - [datetime(2020, 1, 5, tzinfo=timezone.utc), datetime(2020, 1, 6, tzinfo=timezone.utc), - datetime(2020, 1, 8, tzinfo=timezone.utc)]), - (timedelta(days=-4), - [datetime(2019, 12, 28, tzinfo=timezone.utc), datetime(2019, 12, 29, tzinfo=timezone.utc), - datetime(2019, 12, 31, tzinfo=timezone.utc)]), - (timedelta(hours=2), - [datetime(2020, 1, 1, 2, tzinfo=timezone.utc), datetime(2020, 1, 2, 2, tzinfo=timezone.utc), - datetime(2020, 1, 4, 2, tzinfo=timezone.utc)]), - (timedelta(hours=-2), - [datetime(2019, 12, 31, 22, tzinfo=timezone.utc), datetime(2020, 1, 1, 22, tzinfo=timezone.utc), - datetime(2020, 1, 3, 22, tzinfo=timezone.utc)]), - ], - ids=['positive days', 'negative days', 'positive hours', 'negative hours'] - ) - def test_shift(self, delta, result): - shifted = self.timestampset.shift(delta) - self.assert_timestampset_equality(shifted, result) - - @pytest.mark.parametrize( - 'delta,result', - [(timedelta(days=6), - [datetime(2020, 1, 1, tzinfo=timezone.utc), datetime(2020, 1, 3, tzinfo=timezone.utc), - datetime(2020, 1, 7, tzinfo=timezone.utc)]), - (timedelta(hours=3), - [datetime(2020, 1, 1, tzinfo=timezone.utc), datetime(2020, 1, 1, 1, tzinfo=timezone.utc), - datetime(2020, 1, 1, 3, tzinfo=timezone.utc)]), - ], - ids=['days', 'hours'] - ) - def test_scale(self, delta, result): - scaled = self.timestampset.scale(delta) - self.assert_timestampset_equality(scaled, result) - - def test_shift_scale(self): - shifted_scaled = self.timestampset.shift_scale(timedelta(days=4), timedelta(hours=3)) - self.assert_timestampset_equality(shifted_scaled, - [datetime(2020, 1, 5, tzinfo=timezone.utc), - datetime(2020, 1, 5, 1, tzinfo=timezone.utc), - datetime(2020, 1, 5, 3, tzinfo=timezone.utc)]) - From cb69f8f881b467e1346c5a0ab1a5dbdc01c924d3 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Fri, 29 Sep 2023 11:38:56 +0200 Subject: [PATCH 058/101] Improve tests --- pymeos/pymeos/collections/base/set.py | 65 +-- pymeos/pymeos/collections/number/floatset.py | 21 + pymeos/pymeos/collections/number/intset.py | 80 +++- .../tests/collections/number/floatset_test.py | 269 +++++++++++ .../tests/collections/number/intset_test.py | 140 ++---- pymeos_cffi/pymeos_cffi/__init__.py | 51 ++ pymeos_cffi/pymeos_cffi/builder/meos.h | 69 ++- pymeos_cffi/pymeos_cffi/functions.py | 446 ++++++++++++++++-- 8 files changed, 946 insertions(+), 195 deletions(-) create mode 100644 pymeos/tests/collections/number/floatset_test.py diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py index bded6265..10f1c75f 100644 --- a/pymeos/pymeos/collections/base/set.py +++ b/pymeos/pymeos/collections/base/set.py @@ -232,25 +232,6 @@ def __hash__(self) -> int: return set_hash(self._inner) # ------------------------- Topological Operations ------------------------ - # def is_adjacent(self, other) -> bool: - # """ - # Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them - # contains it. - # Args: - # other: object to compare with - - # Returns: - # True if adjacent, False otherwise - - # MEOS Functions: - # adjacent_span_span, adjacent_spanset_span - # """ - # if isinstance(other, Span): - # return adjacent_span_span(set_span(self._inner), other._inner) - # elif isinstance(other, SpanSet): - # return adjacent_spanset_spanset(other._inner, set_to_spanset(self._inner)) - # else: - # raise TypeError(f'Operation not supported with type {other.__class__}') def is_contained_in(self, container) -> bool: """ @@ -322,25 +303,11 @@ def overlaps(self, other) -> bool: else: raise TypeError(f'Operation not supported with type {other.__class__}') - # def is_same(self, other) -> bool: - # """ - # Returns whether the bounding span of `self` is the same as the bounding span of `other`. - - # Args: - # other: An object to compare to `self`. - - # Returns: - # True if same, False otherwise. - - # See Also: - # :meth:`Span.is_same` - # """ - # return self.to_span().is_same(other) - # ------------------------- Position Operations --------------------------- def is_left(self, other) -> bool: """ - Returns whether ``self`` is strictly to the left of ``other``. That is, ``self`` ends before ``other`` starts. + Returns whether ``self`` is strictly to the left of ``other``. That is, + ``self`` ends before ``other`` starts. Args: other: object to compare with @@ -351,21 +318,15 @@ def is_left(self, other) -> bool: MEOS Functions: left_span_span, left_span_spanset """ - from .span import Span - from .spanset import SpanSet if isinstance(other, Set): return left_set_set(self._inner, other._inner) - elif isinstance(other, Span): - return left_span_span(set_span(self._inner), other._inner) - elif isinstance(other, SpanSet): - return left_span_spanset(set_span(self._inner), other._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') def is_over_or_left(self, other) -> bool: """ - Returns whether ``self`` is to the left of ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same value). + Returns whether ``self`` is to the left of ``other`` allowing overlap. + That is, ``self`` ends before ``other`` ends (or at the same value). Args: other: object to compare with @@ -376,14 +337,8 @@ def is_over_or_left(self, other) -> bool: MEOS Functions: overleft_span_span, overleft_span_spanset """ - from .span import Span - from .spanset import SpanSet if isinstance(other, Set): return overleft_set_set(self._inner, other._inner) - elif isinstance(other, Span): - return overleft_span_span(set_span(self._inner), other._inner) - elif isinstance(other, SpanSet): - return overleft_span_spanset(set_span(self._inner), other._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -401,14 +356,8 @@ def is_over_or_right(self, other) -> bool: MEOS Functions: overright_span_span, overright_span_spanset """ - from .span import Span - from .spanset import SpanSet if isinstance(other, Set): return overright_set_set(self._inner, other._inner) - elif isinstance(other, Span): - return overright_span_span(set_span(self._inner), other._inner) - elif isinstance(other, SpanSet): - return overright_span_spanset(set_span(self._inner), other._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -426,14 +375,8 @@ def is_right(self, other) -> bool: MEOS Functions: right_set_set, right_span_span, right_span_spanset """ - from .span import Span - from .spanset import SpanSet if isinstance(other, Set): return right_set_set(self._inner, other._inner) - elif isinstance(other, Span): - return right_span_span(set_span(self._inner), other._inner) - elif isinstance(other, SpanSet): - return right_span_spanset(set_span(self._inner), other._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') diff --git a/pymeos/pymeos/collections/number/floatset.py b/pymeos/pymeos/collections/number/floatset.py index 906b1214..33d8b648 100644 --- a/pymeos/pymeos/collections/number/floatset.py +++ b/pymeos/pymeos/collections/number/floatset.py @@ -208,6 +208,27 @@ def contains(self, content: Union[FloatSet, float]) -> bool: else: return super().contains(content) + # ------------------------- Position Operations -------------------------------- + + def is_left(self, content: Union[FloatSet, float]) -> bool: + """ + Returns whether ``self`` is strictly to the left of ``other``. That is, + ``self`` ends before ``other`` starts. + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + left_set_set, left_floatset_float + """ + if isinstance(content, float): + return left_floatset_float(self._inner, content) + else: + return super().contains(content) + # ------------------------- Set Operations -------------------------------- @overload diff --git a/pymeos/pymeos/collections/number/intset.py b/pymeos/pymeos/collections/number/intset.py index 3b376d90..6937cdf4 100644 --- a/pymeos/pymeos/collections/number/intset.py +++ b/pymeos/pymeos/collections/number/intset.py @@ -200,11 +200,89 @@ def contains(self, content: Union[IntSet, int]) -> bool: MEOS Functions: contains_set_set, contains_intset_int """ - if isinstance(content, int): + if isinstance(content, int): return contains_intset_int(self._inner, content) else: return super().contains(content) + # ------------------------- Position Operations -------------------------------- + + def is_left(self, content: Union[IntSet, int]) -> bool: + """ + Returns whether ``self`` is strictly to the left of ``other``. That is, + ``self`` ends before ``other`` starts. + + Args: + content: object to compare with + + Returns: + True if left, False otherwise + + MEOS Functions: + left_set_set, left_intset_int + """ + if isinstance(content, int): + return left_intset_int(self._inner, content) + else: + return super().is_left(content) + + def is_over_or_left(self, content: Union[IntSet, int]) -> bool: + """ + Returns whether ``self`` is to the left of ``other`` allowing overlap. + That is, ``self`` ends before ``other`` ends (or at the same value). + + Args: + content: object to compare with + + Returns: + True if is over or left, False otherwise + + MEOS Functions: + overleft_set_set, overleft_intset_int + """ + if isinstance(content, int): + return overleft_intset_int(self._inner, content) + else: + return super().is_over_or_left(content) + + def is_right(self, content: Union[IntSet, int]) -> bool: + """ + Returns whether ``self`` is strictly to the right of ``other``. That is, + ``self`` ends after ``other`` starts. + + Args: + content: object to compare with + + Returns: + True if right, False otherwise + + MEOS Functions: + right_set_set, right_intset_int + """ + if isinstance(content, int): + return right_intset_int(self._inner, content) + else: + return super().is_right(content) + + def is_over_or_right(self, content: Union[IntSet, int]) -> bool: + """ + Returns whether ``self`` is to the right of ``other`` allowing overlap. + That is, ``self`` starts before ``other`` ends (or at the same value). + + Args: + content: object to compare with + + Returns: + True if is over or right, False otherwise + + MEOS Functions: + overright_set_set, overright_intset_int + """ + if isinstance(content, int): + return overright_intset_int(self._inner, content) + else: + return super().is_over_or_right(content) + # ------------------------- Set Operations -------------------------------- @overload diff --git a/pymeos/tests/collections/number/floatset_test.py b/pymeos/tests/collections/number/floatset_test.py new file mode 100644 index 00000000..f2639a8c --- /dev/null +++ b/pymeos/tests/collections/number/floatset_test.py @@ -0,0 +1,269 @@ +from copy import copy +from typing import List + +import pytest + +from pymeos import FloatSet, FloatSpan, FloatSpanSet, TFloatInst, TFloatSeq, TFloatSeqSet, \ + TBox, STBox +from tests.conftest import TestPyMEOS + + +class TestFloatSet(TestPyMEOS): + floatset = FloatSet('{1, 2, 3}') + + @staticmethod + def assert_intset_equality(floatset: FloatSet, values: List[int]): + assert floatset.num_elements() == len(values) + assert floatset.elements() == values + + +class TestFloatSetConstructors(TestFloatSet): + + def test_string_constructor(self): + self.assert_intset_equality(self.floatset, [1, 2, 3]) + + def test_list_constructor(self): + floatset = FloatSet(elements=[1, 2, 3]) + self.assert_intset_equality(floatset, [1, 2, 3]) + + def test_hexwkb_constructor(self): + floatset = FloatSet.from_hexwkb('0106000103000000000000000000F03F00000000000000400000000000000840') + self.assert_intset_equality(floatset, [1, 2, 3]) + + def test_from_as_constructor(self): + assert self.floatset == FloatSet(str(self.floatset)) + assert self.floatset == FloatSet.from_wkb(self.floatset.as_wkb()) + assert self.floatset == FloatSet.from_hexwkb(self.floatset.as_hexwkb()) + + def test_copy_constructor(self): + intset_copy = copy(self.floatset) + assert self.floatset == intset_copy + assert self.floatset is not intset_copy + + +class TestFloatSetOutputs(TestFloatSet): + + def test_str(self): + assert str(self.floatset) == '{1, 2, 3}' + + def test_repr(self): + assert repr(self.floatset) == 'FloatSet({1, 2, 3})' + + def test_as_hexwkb(self): + assert self.floatset.as_hexwkb() == '0106000103000000000000000000F03F00000000000000400000000000000840' + + +# class TestIntConversions(TestFloatSet): + + # def test_to_spanset(self): + # assert self.floatset.to_spanset() == FloatSpanSet( + # '{[1, 1], [2, 2], [3, 3]}') + + +class TestFloatSetAccessors(TestFloatSet): + + def test_to_span(self): + assert self.floatset.to_span() == FloatSpan('[1, 3]') + + def test_num_elements(self): + assert self.floatset.num_elements() == 3 + + def test_start_element(self): + assert self.floatset.start_element() == 1 + + def test_end_element(self): + assert self.floatset.end_element() == 3 + + def test_element_n(self): + assert self.floatset.element_n(1) == 2 + + def test_element_n_out_of_range(self): + with pytest.raises(IndexError): + self.floatset.element_n(3) + + def test_elements(self): + assert self.floatset.elements() == [1, 2, 3] + + def test_hash(self): + assert hash(self.floatset) == 2419122126 + + +class TestFloatSetTopologicalFunctions(TestFloatSet): + value = 5.0 + other = FloatSet('{5, 10}') + + @pytest.mark.parametrize( + 'arg, result', + [ + (other, False), + ], + ids=['other'] + ) + def test_is_contained_in(self, arg, result): + assert self.floatset.is_contained_in(arg) == result + + @pytest.mark.parametrize( + 'arg, result', + [ + (value, False), + (other, False), + ], + ids=['value', 'other'] + ) + def test_contains(self, arg, result): + assert self.floatset.contains(arg) == result + + @pytest.mark.parametrize( + 'arg, result', + [ + (other, False), + ], + ids=['other'] + ) + def test_overlaps(self, arg, result): + assert self.floatset.overlaps(arg) == result + +class TestFloatSetPositionFunctions(TestFloatSet): + value = 5.0 + other = FloatSet('{5, 10}') + + @pytest.mark.parametrize( + 'arg, result', + [ + (value, False), + (other, False), + ], + ids=['value', 'other'] + ) + def test_is_left(self, arg, result): + assert self.floatset.is_left(arg) == result + + @pytest.mark.parametrize( + 'arg, result', + [ + (value, False), + (other, False), + ], + ids=['value', 'other'] + ) + def test_is_over_or_left(self, arg, result): + assert self.floatset.is_over_or_left(arg) == result + + @pytest.mark.parametrize( + 'arg, result', + [ + (value, False), + (other, False), + ], + ids=['value', 'other'] + ) + def test_is_right(self, arg, result): + assert self.floatset.is_right(arg) == result + + @pytest.mark.parametrize( + 'arg, result', + [ + (value, False), + (other, False), + ], + ids=['value', 'other'] + ) + def test_is_over_or_right(self, arg, result): + assert self.floatset.is_over_or_right(arg) == result + + # @pytest.mark.parametrize( + # 'other', + # [intspan, # intspanset, + # value, floatset, instant, discrete_sequence, + # stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], + # ids=['intspan', # 'intspanset', + # 'value', 'floatset', 'instant', + # 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', + # 'sequence_set', 'tbox', 'stbox'] + # ) + # def test_distance(self, other): + # self.floatset.distance(other) + + +class TestFloatSetSetFunctions(TestFloatSet): + value = 5.0 + floatset = FloatSet('{1, 10}') + + @pytest.mark.parametrize( + 'other', + [value, floatset], + ids=['value', 'floatset'] + ) + def test_intersection(self, other): + self.floatset.intersection(other) + self.floatset * other + + @pytest.mark.parametrize( + 'other', + [value, floatset], + ids=['value', 'floatset'] + ) + def test_union(self, other): + self.floatset.union(other) + self.floatset + other + + @pytest.mark.parametrize( + 'other', + [value, floatset], + ids=['value', 'floatset'] + ) + def test_minus(self, other): + self.floatset.minus(other) + self.floatset - other + + +class TestFloatSetComparisons(TestFloatSet): + floatset = FloatSet('{1, 10}') + other = FloatSet('{2, 10}') + + def test_eq(self): + _ = self.floatset == self.other + + def test_ne(self): + _ = self.floatset != self.other + + def test_lt(self): + _ = self.floatset < self.other + + def test_le(self): + _ = self.floatset <= self.other + + def test_gt(self): + _ = self.floatset > self.other + + def test_ge(self): + _ = self.floatset >= self.other + + +# class TestFloatSetTransformationFunctions(TestFloatSet): + + # @pytest.mark.parametrize( + # 'delta,result', + # [(4, [5, 6, 8]), + # (-4, [-3, -1, 0]), + # ], + # ids=['positive delta', 'negative delta'] + # ) + # def test_shift(self, delta, result): + # shifted = self.floatset.shift(delta) + # self.assert_intset_equality(shifted, result) + + # @pytest.mark.parametrize( + # 'delta,result', + # [(6, [1, 4, 7])], + # ids=['positive'] + # ) + # def test_scale(self, delta, result): + # scaled = self.floatset.scale(delta) + # self.assert_intset_equality(scaled, result) + + # def test_shift_scale(self): + # shifted_scaled = self.floatset.shift_scale(4, 4) + # self.assert_intset_equality(shifted_scaled, [5, 7, 9]) + + diff --git a/pymeos/tests/collections/number/intset_test.py b/pymeos/tests/collections/number/intset_test.py index 3d9776b2..a713af0a 100644 --- a/pymeos/tests/collections/number/intset_test.py +++ b/pymeos/tests/collections/number/intset_test.py @@ -124,107 +124,53 @@ def test_contains(self, arg, result): def test_overlaps(self, arg, result): assert self.intset.overlaps(arg) == result -# class TestIntSetPositionFunctions(TestIntSet): - # value = 5 - # other = IntSet('{5, 10}') - - # @pytest.mark.parametrize( - # 'other', - # [intset, intspan, # intspanset, - # instant, discrete_sequence, stepwise_sequence, continuous_sequence, - # sequence_set, tbox, - # stbox], - # ids=['intset', 'intspan', # 'intspanset', - # 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_contained_in(self, other): - # self.intset.is_contained_in(other) - - # @pytest.mark.parametrize( - # 'other', - # [value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence], - # ids=['value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set'] - # ) - # def test_contains(self, other): - # self.intset.contains(other) - # _ = other in self.intset - - - # @pytest.mark.parametrize( - # 'other', - # [intspan, # intspanset, - # value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence, tbox, stbox], - # ids=['intspan', # 'intspanset', - # 'value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_overlaps(self, other): - # self.intset.overlaps(other) - - # @pytest.mark.parametrize( - # 'other', - # [intspan, # intspanset, - # value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence, tbox, stbox], - # ids=['intspan', # 'intspanset', - # 'value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_same(self, other): - # self.intset.is_same(other) +class TestIntSetPositionFunctions(TestIntSet): + value = 5 + other = IntSet('{5, 10}') - # @pytest.mark.parametrize( - # 'other', - # [intspan, # intspanset, - # value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence, tbox, stbox], - # ids=['intspan', # 'intspanset', - # 'value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_before(self, other): - # self.intset.is_before(other) + @pytest.mark.parametrize( + 'arg, result', + [ + (value, True), + (other, True), + ], + ids=['value', 'other'] + ) + def test_is_left(self, arg, result): + assert self.intset.is_left(arg) == result - # @pytest.mark.parametrize( - # 'other', - # [intspan, # intspanset, - # value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence, tbox, stbox], - # ids=['intspan', # 'intspanset', - # 'value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_over_or_before(self, other): - # self.intset.is_over_or_before(other) + @pytest.mark.parametrize( + 'arg, result', + [ + (value, True), + (other, True), + ], + ids=['value', 'other'] + ) + def test_is_over_or_left(self, arg, result): + assert self.intset.is_over_or_left(arg) == result - # @pytest.mark.parametrize( - # 'other', - # [intspan, # intspanset, - # value, intset, instant, discrete_sequence, stepwise_sequence, sequence_set, - # continuous_sequence, tbox, stbox], - # ids=['intspan', # 'intspanset', - # 'value', 'intset', 'instant', 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_after(self, other): - # self.intset.is_after(other) + @pytest.mark.parametrize( + 'arg, result', + [ + (value, False), + (other, False), + ], + ids=['value', 'other'] + ) + def test_is_right(self, arg, result): + assert self.intset.is_right(arg) == result - # @pytest.mark.parametrize( - # 'other', - # [intspan, # intspanset, - # value, intset, instant, discrete_sequence, - # stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - # ids=['intspan', # 'intspanset', - # 'value', 'intset', 'instant', - # 'discrete_sequence', 'stepwise_sequence', - # 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_is_over_or_after(self, other): - # self.intset.is_over_or_after(other) + @pytest.mark.parametrize( + 'arg, result', + [ + (value, True), + (other, False), + ], + ids=['value', 'other'] + ) + def test_is_over_or_right(self, arg, result): + assert self.intset.is_over_or_right(arg) == result # @pytest.mark.parametrize( # 'other', diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 3e3b6ab6..87b1a30b 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -383,55 +383,106 @@ 'overlaps_span_span', 'overlaps_spanset_span', 'overlaps_spanset_spanset', + 'after_period_timestamp', + 'after_periodset_timestamp', + 'after_timestamp_period', + 'after_timestamp_periodset', 'after_timestamp_timestampset', + 'after_timestampset_timestamp', + 'before_period_timestamp', 'before_periodset_timestamp', + 'before_timestamp_period', + 'before_timestamp_periodset', 'before_timestamp_timestampset', + 'before_timestampset_timestamp', + 'left_bigint_bigintset', + 'left_bigint_bigintspan', + 'left_bigintset_bigint', + 'left_bigintspan_bigint', + 'left_float_floatset', 'left_float_floatspan', + 'left_floatset_float', 'left_floatspan_float', + 'left_int_intset', 'left_int_intspan', + 'left_intset_int', 'left_intspan_int', 'left_set_set', 'left_span_span', 'left_span_spanset', 'left_spanset_span', 'left_spanset_spanset', + 'left_text_textset', + 'left_textset_text', 'overafter_period_timestamp', 'overafter_periodset_timestamp', 'overafter_timestamp_period', 'overafter_timestamp_periodset', 'overafter_timestamp_timestampset', + 'overafter_timestampset_timestamp', 'overbefore_period_timestamp', 'overbefore_periodset_timestamp', 'overbefore_timestamp_period', 'overbefore_timestamp_periodset', 'overbefore_timestamp_timestampset', + 'overbefore_timestampset_timestamp', + 'overleft_bigint_bigintset', + 'overleft_bigint_bigintspan', + 'overleft_bigintset_bigint', + 'overleft_bigintspan_bigint', + 'overleft_float_floatset', 'overleft_float_floatspan', + 'overleft_floatset_float', 'overleft_floatspan_float', + 'overleft_int_intset', 'overleft_int_intspan', + 'overleft_intset_int', 'overleft_intspan_int', 'overleft_set_set', 'overleft_span_span', 'overleft_span_spanset', 'overleft_spanset_span', 'overleft_spanset_spanset', + 'overleft_text_textset', + 'overleft_textset_text', + 'overright_bigint_bigintset', + 'overright_bigint_bigintspan', + 'overright_bigintset_bigint', + 'overright_bigintspan_bigint', + 'overright_float_floatset', 'overright_float_floatspan', + 'overright_floatset_float', 'overright_floatspan_float', + 'overright_int_intset', 'overright_int_intspan', + 'overright_intset_int', 'overright_intspan_int', 'overright_set_set', 'overright_span_span', 'overright_span_spanset', 'overright_spanset_span', 'overright_spanset_spanset', + 'overright_text_textset', + 'overright_textset_text', + 'right_bigint_bigintset', + 'right_bigint_bigintspan', + 'right_bigintset_bigint', + 'right_bigintspan_bigint', + 'right_float_floatset', 'right_float_floatspan', + 'right_floatset_float', 'right_floatspan_float', + 'right_int_intset', 'right_int_intspan', + 'right_intset_int', 'right_intspan_int', 'right_set_set', 'right_span_span', 'right_span_spanset', 'right_spanset_span', 'right_spanset_spanset', + 'right_text_textset', + 'right_textset_text', 'distance_bigintset_bigint', 'distance_bigintspan_bigint', 'distance_bigintspanset_bigint', diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 9dbad14a..c9f1834c 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -1121,55 +1121,106 @@ extern bool overlaps_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern bool after_period_timestamp(const Span *s, TimestampTz t); +extern bool after_periodset_timestamp(const SpanSet *ss, TimestampTz t); +extern bool after_timestamp_period(TimestampTz t, const Span *s); +extern bool after_timestamp_periodset(TimestampTz t, const SpanSet *ss); extern bool after_timestamp_timestampset(TimestampTz t, const Set *ts); -extern bool before_periodset_timestamp(const SpanSet *ps, TimestampTz t); +extern bool after_timestampset_timestamp(const Set *s, TimestampTz t); +extern bool before_period_timestamp(const Span *s, TimestampTz t); +extern bool before_periodset_timestamp(const SpanSet *ss, TimestampTz t); +extern bool before_timestamp_period(TimestampTz t, const Span *s); +extern bool before_timestamp_periodset(TimestampTz t, const SpanSet *ss); extern bool before_timestamp_timestampset(TimestampTz t, const Set *ts); +extern bool before_timestampset_timestamp(const Set *s, TimestampTz t); +extern bool left_bigint_bigintset(int64 i, const Set *s); +extern bool left_bigint_bigintspan(int64 i, const Span *s); +extern bool left_bigintset_bigint(const Set *s, int64 i); +extern bool left_bigintspan_bigint(const Span *s, int64 i); +extern bool left_float_floatset(double d, const Set *s); extern bool left_float_floatspan(double d, const Span *s); +extern bool left_floatset_float(const Set *s, double d); extern bool left_floatspan_float(const Span *s, double d); +extern bool left_int_intset(int i, const Set *s); extern bool left_int_intspan(int i, const Span *s); +extern bool left_intset_int(const Set *s, int i); extern bool left_intspan_int(const Span *s, int i); extern bool left_set_set(const Set *s1, const Set *s2); extern bool left_span_span(const Span *s1, const Span *s2); extern bool left_span_spanset(const Span *s, const SpanSet *ss); extern bool left_spanset_span(const SpanSet *ss, const Span *s); extern bool left_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); -extern bool overafter_period_timestamp(const Span *p, TimestampTz t); -extern bool overafter_periodset_timestamp(const SpanSet *ps, TimestampTz t); -extern bool overafter_timestamp_period(TimestampTz t, const Span *p); -extern bool overafter_timestamp_periodset(TimestampTz t, const SpanSet *ps); +extern bool left_text_textset(text *txt, const Set *s); +extern bool left_textset_text(const Set *s, text *txt); +extern bool overafter_period_timestamp(const Span *s, TimestampTz t); +extern bool overafter_periodset_timestamp(const SpanSet *ss, TimestampTz t); +extern bool overafter_timestamp_period(TimestampTz t, const Span *s); +extern bool overafter_timestamp_periodset(TimestampTz t, const SpanSet *ss); extern bool overafter_timestamp_timestampset(TimestampTz t, const Set *ts); -extern bool overbefore_period_timestamp(const Span *p, TimestampTz t); -extern bool overbefore_periodset_timestamp(const SpanSet *ps, TimestampTz t); -extern bool overbefore_timestamp_period(TimestampTz t, const Span *p); -extern bool overbefore_timestamp_periodset(TimestampTz t, const SpanSet *ps); +extern bool overafter_timestampset_timestamp(const Set *s, TimestampTz t); +extern bool overbefore_period_timestamp(const Span *s, TimestampTz t); +extern bool overbefore_periodset_timestamp(const SpanSet *ss, TimestampTz t); +extern bool overbefore_timestamp_period(TimestampTz t, const Span *s); +extern bool overbefore_timestamp_periodset(TimestampTz t, const SpanSet *ss); extern bool overbefore_timestamp_timestampset(TimestampTz t, const Set *ts); +extern bool overbefore_timestampset_timestamp(const Set *s, TimestampTz t); +extern bool overleft_bigint_bigintset(int64 i, const Set *s); +extern bool overleft_bigint_bigintspan(int64 i, const Span *s); +extern bool overleft_bigintset_bigint(const Set *s, int64 i); +extern bool overleft_bigintspan_bigint(const Span *s, int64 i); +extern bool overleft_float_floatset(double d, const Set *s); extern bool overleft_float_floatspan(double d, const Span *s); +extern bool overleft_floatset_float(const Set *s, double d); extern bool overleft_floatspan_float(const Span *s, double d); +extern bool overleft_int_intset(int i, const Set *s); extern bool overleft_int_intspan(int i, const Span *s); +extern bool overleft_intset_int(const Set *s, int i); extern bool overleft_intspan_int(const Span *s, int i); extern bool overleft_set_set(const Set *s1, const Set *s2); extern bool overleft_span_span(const Span *s1, const Span *s2); extern bool overleft_span_spanset(const Span *s, const SpanSet *ss); extern bool overleft_spanset_span(const SpanSet *ss, const Span *s); extern bool overleft_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern bool overleft_text_textset(text *txt, const Set *s); +extern bool overleft_textset_text(const Set *s, text *txt); +extern bool overright_bigint_bigintset(int64 i, const Set *s); +extern bool overright_bigint_bigintspan(int64 i, const Span *s); +extern bool overright_bigintset_bigint(const Set *s, int64 i); +extern bool overright_bigintspan_bigint(const Span *s, int64 i); +extern bool overright_float_floatset(double d, const Set *s); extern bool overright_float_floatspan(double d, const Span *s); +extern bool overright_floatset_float(const Set *s, double d); extern bool overright_floatspan_float(const Span *s, double d); +extern bool overright_int_intset(int i, const Set *s); extern bool overright_int_intspan(int i, const Span *s); +extern bool overright_intset_int(const Set *s, int i); extern bool overright_intspan_int(const Span *s, int i); extern bool overright_set_set(const Set *s1, const Set *s2); extern bool overright_span_span(const Span *s1, const Span *s2); extern bool overright_span_spanset(const Span *s, const SpanSet *ss); extern bool overright_spanset_span(const SpanSet *ss, const Span *s); extern bool overright_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern bool overright_text_textset(text *txt, const Set *s); +extern bool overright_textset_text(const Set *s, text *txt); +extern bool right_bigint_bigintset(int64 i, const Set *s); +extern bool right_bigint_bigintspan(int64 i, const Span *s); +extern bool right_bigintset_bigint(const Set *s, int64 i); +extern bool right_bigintspan_bigint(const Span *s, int64 i); +extern bool right_float_floatset(double d, const Set *s); extern bool right_float_floatspan(double d, const Span *s); +extern bool right_floatset_float(const Set *s, double d); extern bool right_floatspan_float(const Span *s, double d); +extern bool right_int_intset(int i, const Set *s); extern bool right_int_intspan(int i, const Span *s); +extern bool right_intset_int(const Set *s, int i); extern bool right_intspan_int(const Span *s, int i); extern bool right_set_set(const Set *s1, const Set *s2); extern bool right_span_span(const Span *s1, const Span *s2); extern bool right_span_spanset(const Span *s, const SpanSet *ss); extern bool right_spanset_span(const SpanSet *ss, const Span *s); extern bool right_spanset_spanset(const SpanSet *ss1, const SpanSet *ss2); +extern bool right_text_textset(text *txt, const Set *s); +extern bool right_textset_text(const Set *s, text *txt); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index a4ecab0e..8ba808f9 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -2768,6 +2768,38 @@ def overlaps_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> return result if result != _ffi.NULL else None +def after_period_timestamp(s: 'const Span *', t: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.after_period_timestamp(s_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def after_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.after_periodset_timestamp(ss_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def after_timestamp_period(t: int, s: 'const Span *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + s_converted = _ffi.cast('const Span *', s) + result = _lib.after_timestamp_period(t_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def after_timestamp_periodset(t: int, ss: 'const SpanSet *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.after_timestamp_periodset(t_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def after_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) ts_converted = _ffi.cast('const Set *', ts) @@ -2776,10 +2808,42 @@ def after_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': return result if result != _ffi.NULL else None -def before_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) +def after_timestampset_timestamp(s: 'const Set *', t: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.before_periodset_timestamp(ps_converted, t_converted) + result = _lib.after_timestampset_timestamp(s_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def before_period_timestamp(s: 'const Span *', t: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.before_period_timestamp(s_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def before_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.before_periodset_timestamp(ss_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def before_timestamp_period(t: int, s: 'const Span *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + s_converted = _ffi.cast('const Span *', s) + result = _lib.before_timestamp_period(t_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def before_timestamp_periodset(t: int, ss: 'const SpanSet *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.before_timestamp_periodset(t_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None @@ -2792,6 +2856,53 @@ def before_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': return result if result != _ffi.NULL else None +def before_timestampset_timestamp(s: 'const Set *', t: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.before_timestampset_timestamp(s_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def left_bigint_bigintset(i: int, s: 'const Set *') -> 'bool': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Set *', s) + result = _lib.left_bigint_bigintset(i_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def left_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Span *', s) + result = _lib.left_bigint_bigintspan(i_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def left_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.left_bigintset_bigint(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def left_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.left_bigintspan_bigint(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def left_float_floatset(d: float, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.left_float_floatset(d, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + def left_float_floatspan(d: float, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.left_float_floatspan(d, s_converted) @@ -2799,6 +2910,13 @@ def left_float_floatspan(d: float, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def left_floatset_float(s: 'const Set *', d: float) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.left_floatset_float(s_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + def left_floatspan_float(s: 'const Span *', d: float) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.left_floatspan_float(s_converted, d) @@ -2806,6 +2924,13 @@ def left_floatspan_float(s: 'const Span *', d: float) -> 'bool': return result if result != _ffi.NULL else None +def left_int_intset(i: int, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.left_int_intset(i, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + def left_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.left_int_intspan(i, s_converted) @@ -2813,6 +2938,13 @@ def left_int_intspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def left_intset_int(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.left_intset_int(s_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + def left_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.left_intspan_int(s_converted, i) @@ -2860,34 +2992,50 @@ def left_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'boo return result if result != _ffi.NULL else None -def overafter_period_timestamp(p: 'const Span *', t: int) -> 'bool': - p_converted = _ffi.cast('const Span *', p) +def left_text_textset(txt: str, s: 'const Set *') -> 'bool': + txt_converted = cstring2text(txt) + s_converted = _ffi.cast('const Set *', s) + result = _lib.left_text_textset(txt_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def left_textset_text(s: 'const Set *', txt: str) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.left_textset_text(s_converted, txt_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overafter_period_timestamp(s: 'const Span *', t: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overafter_period_timestamp(p_converted, t_converted) + result = _lib.overafter_period_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) +def overafter_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overafter_periodset_timestamp(ps_converted, t_converted) + result = _lib.overafter_periodset_timestamp(ss_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_timestamp_period(t: int, p: 'const Span *') -> 'bool': +def overafter_timestamp_period(t: int, s: 'const Span *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) - p_converted = _ffi.cast('const Span *', p) - result = _lib.overafter_timestamp_period(t_converted, p_converted) + s_converted = _ffi.cast('const Span *', s) + result = _lib.overafter_timestamp_period(t_converted, s_converted) _check_error() return result if result != _ffi.NULL else None -def overafter_timestamp_periodset(t: int, ps: 'const SpanSet *') -> 'bool': +def overafter_timestamp_periodset(t: int, ss: 'const SpanSet *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.overafter_timestamp_periodset(t_converted, ps_converted) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overafter_timestamp_periodset(t_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None @@ -2900,34 +3048,42 @@ def overafter_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': return result if result != _ffi.NULL else None -def overbefore_period_timestamp(p: 'const Span *', t: int) -> 'bool': - p_converted = _ffi.cast('const Span *', p) +def overafter_timestampset_timestamp(s: 'const Set *', t: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overbefore_period_timestamp(p_converted, t_converted) + result = _lib.overafter_timestampset_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overbefore_periodset_timestamp(ps: 'const SpanSet *', t: int) -> 'bool': - ps_converted = _ffi.cast('const SpanSet *', ps) +def overbefore_period_timestamp(s: 'const Span *', t: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) t_converted = _ffi.cast('TimestampTz', t) - result = _lib.overbefore_periodset_timestamp(ps_converted, t_converted) + result = _lib.overbefore_period_timestamp(s_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overbefore_timestamp_period(t: int, p: 'const Span *') -> 'bool': +def overbefore_periodset_timestamp(ss: 'const SpanSet *', t: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) t_converted = _ffi.cast('TimestampTz', t) - p_converted = _ffi.cast('const Span *', p) - result = _lib.overbefore_timestamp_period(t_converted, p_converted) + result = _lib.overbefore_periodset_timestamp(ss_converted, t_converted) _check_error() return result if result != _ffi.NULL else None -def overbefore_timestamp_periodset(t: int, ps: 'const SpanSet *') -> 'bool': +def overbefore_timestamp_period(t: int, s: 'const Span *') -> 'bool': t_converted = _ffi.cast('TimestampTz', t) - ps_converted = _ffi.cast('const SpanSet *', ps) - result = _lib.overbefore_timestamp_periodset(t_converted, ps_converted) + s_converted = _ffi.cast('const Span *', s) + result = _lib.overbefore_timestamp_period(t_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overbefore_timestamp_periodset(t: int, ss: 'const SpanSet *') -> 'bool': + t_converted = _ffi.cast('TimestampTz', t) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overbefore_timestamp_periodset(t_converted, ss_converted) _check_error() return result if result != _ffi.NULL else None @@ -2940,6 +3096,53 @@ def overbefore_timestamp_timestampset(t: int, ts: 'const Set *') -> 'bool': return result if result != _ffi.NULL else None +def overbefore_timestampset_timestamp(s: 'const Set *', t: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + t_converted = _ffi.cast('TimestampTz', t) + result = _lib.overbefore_timestampset_timestamp(s_converted, t_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overleft_bigint_bigintset(i: int, s: 'const Set *') -> 'bool': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Set *', s) + result = _lib.overleft_bigint_bigintset(i_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overleft_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Span *', s) + result = _lib.overleft_bigint_bigintspan(i_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overleft_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.overleft_bigintset_bigint(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overleft_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.overleft_bigintspan_bigint(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overleft_float_floatset(d: float, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.overleft_float_floatset(d, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overleft_float_floatspan(d: float, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.overleft_float_floatspan(d, s_converted) @@ -2947,6 +3150,13 @@ def overleft_float_floatspan(d: float, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def overleft_floatset_float(s: 'const Set *', d: float) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.overleft_floatset_float(s_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + def overleft_floatspan_float(s: 'const Span *', d: float) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.overleft_floatspan_float(s_converted, d) @@ -2954,6 +3164,13 @@ def overleft_floatspan_float(s: 'const Span *', d: float) -> 'bool': return result if result != _ffi.NULL else None +def overleft_int_intset(i: int, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.overleft_int_intset(i, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overleft_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.overleft_int_intspan(i, s_converted) @@ -2961,6 +3178,13 @@ def overleft_int_intspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def overleft_intset_int(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.overleft_intset_int(s_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + def overleft_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.overleft_intspan_int(s_converted, i) @@ -3008,6 +3232,61 @@ def overleft_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> return result if result != _ffi.NULL else None +def overleft_text_textset(txt: str, s: 'const Set *') -> 'bool': + txt_converted = cstring2text(txt) + s_converted = _ffi.cast('const Set *', s) + result = _lib.overleft_text_textset(txt_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overleft_textset_text(s: 'const Set *', txt: str) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.overleft_textset_text(s_converted, txt_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overright_bigint_bigintset(i: int, s: 'const Set *') -> 'bool': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Set *', s) + result = _lib.overright_bigint_bigintset(i_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overright_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Span *', s) + result = _lib.overright_bigint_bigintspan(i_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overright_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.overright_bigintset_bigint(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overright_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.overright_bigintspan_bigint(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overright_float_floatset(d: float, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.overright_float_floatset(d, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overright_float_floatspan(d: float, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.overright_float_floatspan(d, s_converted) @@ -3015,6 +3294,13 @@ def overright_float_floatspan(d: float, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def overright_floatset_float(s: 'const Set *', d: float) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.overright_floatset_float(s_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + def overright_floatspan_float(s: 'const Span *', d: float) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.overright_floatspan_float(s_converted, d) @@ -3022,6 +3308,13 @@ def overright_floatspan_float(s: 'const Span *', d: float) -> 'bool': return result if result != _ffi.NULL else None +def overright_int_intset(i: int, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.overright_int_intset(i, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overright_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.overright_int_intspan(i, s_converted) @@ -3029,6 +3322,13 @@ def overright_int_intspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def overright_intset_int(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.overright_intset_int(s_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + def overright_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.overright_intspan_int(s_converted, i) @@ -3076,6 +3376,61 @@ def overright_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> return result if result != _ffi.NULL else None +def overright_text_textset(txt: str, s: 'const Set *') -> 'bool': + txt_converted = cstring2text(txt) + s_converted = _ffi.cast('const Set *', s) + result = _lib.overright_text_textset(txt_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def overright_textset_text(s: 'const Set *', txt: str) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.overright_textset_text(s_converted, txt_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def right_bigint_bigintset(i: int, s: 'const Set *') -> 'bool': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Set *', s) + result = _lib.right_bigint_bigintset(i_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def right_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': + i_converted = _ffi.cast('int64', i) + s_converted = _ffi.cast('const Span *', s) + result = _lib.right_bigint_bigintspan(i_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def right_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.right_bigintset_bigint(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def right_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': + s_converted = _ffi.cast('const Span *', s) + i_converted = _ffi.cast('int64', i) + result = _lib.right_bigintspan_bigint(s_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def right_float_floatset(d: float, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.right_float_floatset(d, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + def right_float_floatspan(d: float, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.right_float_floatspan(d, s_converted) @@ -3083,6 +3438,13 @@ def right_float_floatspan(d: float, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def right_floatset_float(s: 'const Set *', d: float) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.right_floatset_float(s_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + def right_floatspan_float(s: 'const Span *', d: float) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.right_floatspan_float(s_converted, d) @@ -3090,6 +3452,13 @@ def right_floatspan_float(s: 'const Span *', d: float) -> 'bool': return result if result != _ffi.NULL else None +def right_int_intset(i: int, s: 'const Set *') -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.right_int_intset(i, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + def right_int_intspan(i: int, s: 'const Span *') -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.right_int_intspan(i, s_converted) @@ -3097,6 +3466,13 @@ def right_int_intspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def right_intset_int(s: 'const Set *', i: int) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + result = _lib.right_intset_int(s_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + def right_intspan_int(s: 'const Span *', i: int) -> 'bool': s_converted = _ffi.cast('const Span *', s) result = _lib.right_intspan_int(s_converted, i) @@ -3144,6 +3520,22 @@ def right_spanset_spanset(ss1: 'const SpanSet *', ss2: 'const SpanSet *') -> 'bo return result if result != _ffi.NULL else None +def right_text_textset(txt: str, s: 'const Set *') -> 'bool': + txt_converted = cstring2text(txt) + s_converted = _ffi.cast('const Set *', s) + result = _lib.right_text_textset(txt_converted, s_converted) + _check_error() + return result if result != _ffi.NULL else None + + +def right_textset_text(s: 'const Set *', txt: str) -> 'bool': + s_converted = _ffi.cast('const Set *', s) + txt_converted = cstring2text(txt) + result = _lib.right_textset_text(s_converted, txt_converted) + _check_error() + return result if result != _ffi.NULL else None + + def distance_bigintset_bigint(s: 'const Set *', i: int) -> 'double': s_converted = _ffi.cast('const Set *', s) i_converted = _ffi.cast('int64', i) From 247de43616234f5b14af5c3df5e3f0cee11e47c9 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Fri, 29 Sep 2023 14:38:19 +0200 Subject: [PATCH 059/101] Improve tests --- pymeos/pymeos/collections/number/floatset.py | 60 ++++++++++++++++++- pymeos/pymeos/collections/number/intset.py | 1 + .../tests/collections/number/floatset_test.py | 33 +++++----- .../tests/collections/number/intset_test.py | 27 ++++----- 4 files changed, 87 insertions(+), 34 deletions(-) diff --git a/pymeos/pymeos/collections/number/floatset.py b/pymeos/pymeos/collections/number/floatset.py index 33d8b648..488a33e7 100644 --- a/pymeos/pymeos/collections/number/floatset.py +++ b/pymeos/pymeos/collections/number/floatset.py @@ -5,6 +5,7 @@ from pymeos_cffi import floatset_in, floatset_make, floatset_out, floatset_start_value, \ floatset_end_value, \ floatset_value_n, floatset_values, contains_floatset_float, intersection_floatset_float, intersection_set_set, \ + left_floatset_float, overleft_floatset_float, right_floatset_float, overright_floatset_float, \ minus_floatset_float, \ minus_set_set, union_set_set, union_floatset_float, floatset_shift_scale, minus_float_floatset, \ distance_floatset_float @@ -227,7 +228,64 @@ def is_left(self, content: Union[FloatSet, float]) -> bool: if isinstance(content, float): return left_floatset_float(self._inner, content) else: - return super().contains(content) + return super().is_left(content) + + def is_over_or_left(self, content: Union[FloatSet, float]) -> bool: + """ + Returns whether ``self`` is to the left of ``other`` allowing overlap. + That is, ``self`` ends before ``other`` ends (or at the same value). + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + overleft_set_set, overleft_floatset_float + """ + if isinstance(content, float): + return overleft_floatset_float(self._inner, content) + else: + return super().is_over_or_left(content) + + def is_right(self, content: Union[FloatSet, float]) -> bool: + """ + Returns whether ``self`` is strictly to the left of ``other``. That is, + ``self`` starts before ``other`` ends. + + Args: + content: object to compare with + + Returns: + True if is rigth, False otherwise + + MEOS Functions: + right_set_set, right_floatset_float + """ + if isinstance(content, float): + return right_floatset_float(self._inner, content) + else: + return super().is_right(content) + + def is_over_or_right(self, content: Union[FloatSet, float]) -> bool: + """ + Returns whether ``self`` is to the right of ``other`` allowing overlap. + That is, ``self`` ends after ``other`` ends (or at the same value). + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + overright_set_set, overright_floatset_float + """ + if isinstance(content, float): + return overright_floatset_float(self._inner, content) + else: + return super().is_over_or_right(content) # ------------------------- Set Operations -------------------------------- diff --git a/pymeos/pymeos/collections/number/intset.py b/pymeos/pymeos/collections/number/intset.py index 6937cdf4..0aa7fa2f 100644 --- a/pymeos/pymeos/collections/number/intset.py +++ b/pymeos/pymeos/collections/number/intset.py @@ -4,6 +4,7 @@ from pymeos_cffi import intset_in, intset_make, intset_out, intset_start_value, intset_end_value, \ intset_value_n, intset_values, contains_intset_int, intersection_intset_int, intersection_set_set, minus_intset_int, \ + left_intset_int, overleft_intset_int, right_intset_int, overright_intset_int, \ minus_set_set, union_set_set, union_intset_int, intset_shift_scale, minus_int_intset, distance_intset_int from .intspan import IntSpan diff --git a/pymeos/tests/collections/number/floatset_test.py b/pymeos/tests/collections/number/floatset_test.py index f2639a8c..ba05c43c 100644 --- a/pymeos/tests/collections/number/floatset_test.py +++ b/pymeos/tests/collections/number/floatset_test.py @@ -3,8 +3,7 @@ import pytest -from pymeos import FloatSet, FloatSpan, FloatSpanSet, TFloatInst, TFloatSeq, TFloatSeqSet, \ - TBox, STBox +from pymeos import FloatSet, FloatSpan, FloatSpanSet from tests.conftest import TestPyMEOS @@ -130,8 +129,8 @@ class TestFloatSetPositionFunctions(TestFloatSet): @pytest.mark.parametrize( 'arg, result', [ - (value, False), - (other, False), + (value, True), + (other, True), ], ids=['value', 'other'] ) @@ -141,8 +140,8 @@ def test_is_left(self, arg, result): @pytest.mark.parametrize( 'arg, result', [ - (value, False), - (other, False), + (value, True), + (other, True), ], ids=['value', 'other'] ) @@ -171,18 +170,16 @@ def test_is_right(self, arg, result): def test_is_over_or_right(self, arg, result): assert self.floatset.is_over_or_right(arg) == result - # @pytest.mark.parametrize( - # 'other', - # [intspan, # intspanset, - # value, floatset, instant, discrete_sequence, - # stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - # ids=['intspan', # 'intspanset', - # 'value', 'floatset', 'instant', - # 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', - # 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_distance(self, other): - # self.floatset.distance(other) + @pytest.mark.parametrize( + 'arg, result', + [ + (value, 2.0), + (other, 2.0), + ], + ids=['value', 'other'] + ) + def test_distance(self, arg, result): + assert self.floatset.distance(arg) == result class TestFloatSetSetFunctions(TestFloatSet): diff --git a/pymeos/tests/collections/number/intset_test.py b/pymeos/tests/collections/number/intset_test.py index a713af0a..1464917c 100644 --- a/pymeos/tests/collections/number/intset_test.py +++ b/pymeos/tests/collections/number/intset_test.py @@ -3,8 +3,7 @@ import pytest -from pymeos import IntSet, IntSpan, IntSpanSet, TIntInst, TIntSeq, TIntSeqSet, \ - TBox, STBox +from pymeos import IntSet, IntSpan, IntSpanSet from tests.conftest import TestPyMEOS @@ -164,7 +163,7 @@ def test_is_right(self, arg, result): @pytest.mark.parametrize( 'arg, result', [ - (value, True), + (value, False), (other, False), ], ids=['value', 'other'] @@ -172,18 +171,16 @@ def test_is_right(self, arg, result): def test_is_over_or_right(self, arg, result): assert self.intset.is_over_or_right(arg) == result - # @pytest.mark.parametrize( - # 'other', - # [intspan, # intspanset, - # value, intset, instant, discrete_sequence, - # stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - # ids=['intspan', # 'intspanset', - # 'value', 'intset', 'instant', - # 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', - # 'sequence_set', 'tbox', 'stbox'] - # ) - # def test_distance(self, other): - # self.intset.distance(other) + @pytest.mark.parametrize( + 'arg, result', + [ + (value, 2), + (other, 2), + ], + ids=['value', 'other'] + ) + def test_distance(self, arg, result): + assert self.intset.distance(arg) == result class TestIntSetSetFunctions(TestIntSet): From 79ae69fa2d898e12876c31fbc0438d2ce40fd886 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Fri, 29 Sep 2023 18:17:14 +0200 Subject: [PATCH 060/101] Improve tests --- pymeos/pymeos/collections/base/span.py | 5 +- pymeos/pymeos/collections/number/intspan.py | 84 ++--- .../tests/collections/number/intspan_test.py | 305 ++++++++++++++++++ 3 files changed, 327 insertions(+), 67 deletions(-) create mode 100644 pymeos/tests/collections/number/intspan_test.py diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index 425a782c..47abf6ad 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -417,11 +417,8 @@ def is_over_or_right(self, other) -> bool: overright_span_span, overright_span_spanset, overafter_period_timestamp, overafter_period_timestampset, overafter_period_temporal """ - from .set import Set from .spanset import SpanSet - if isinstance(other, Set): - return overright_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return overright_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): return overright_span_spanset(self._inner, other._inner) diff --git a/pymeos/pymeos/collections/number/intspan.py b/pymeos/pymeos/collections/number/intspan.py index cee9806f..a699e2f0 100644 --- a/pymeos/pymeos/collections/number/intspan.py +++ b/pymeos/pymeos/collections/number/intspan.py @@ -12,8 +12,6 @@ from .. import Span if TYPE_CHECKING: - from ...boxes import TBox - from .intset import IntSet from .intspanset import IntSpanSet @@ -141,9 +139,10 @@ def scale(self, new_width: int) -> IntSpan: """ return self.shift_scale(None, new_width) - def shift_scale(self, delta: Optional[int], new_width: Optional[int]) -> IntSpan: + def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSpan: """ - Return a new ``IntSpan`` with the lower and upper bounds shifted by ``delta`` and scaled so that the width is ``new_width``. + Return a new ``IntSpan`` with the lower and upper bounds shifted by + ``delta`` and scaled so that the width is ``width``. Args: delta: The value to shift by @@ -155,11 +154,15 @@ def shift_scale(self, delta: Optional[int], new_width: Optional[int]) -> IntSpan MEOS Functions: intspan_shift_scale """ - return IntSpan(numspan_shift_scale(self._inner, delta, new_width, delta is not None, new_width is not None)) + d = delta if delta is not None else 0 + w = width if width is not None else 0 + modified = numspan_shift_scale(self._inner, d, w, delta is not None, + width is not None) + return IntSpan(_inner=modified) # ------------------------- Topological Operations -------------------------------- - def is_adjacent(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + def is_adjacent(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them contains it. @@ -175,30 +178,10 @@ def is_adjacent(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> b """ if isinstance(other, int): return adjacent_intspan_int(self._inner, other) - elif isinstance(other, TBox): - return self.is_adjacent(other.to_span()) else: return super().is_adjacent(other) - def is_contained_in(self, container: Union[TBox, IntSpan, IntSpanSet]) -> bool: - """ - Returns whether ``self`` is contained in ``container``. - - Args: - container: object to compare with - - Returns: - True if contained, False otherwise - - MEOS Functions: - contained_span_span, contained_span_spanset, contained_intset_int - """ - if isinstance(container, TBox): - return self.is_contained_in(container.to_span()) - else: - return super().is_contained_in(container) - - def contains(self, content: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + def contains(self, content: Union[int, IntSpan, IntSpanSet]) -> bool: """ Returns whether ``self`` contains ``content``. @@ -213,12 +196,10 @@ def contains(self, content: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bo """ if isinstance(content, int): return contains_intspan_int(self._inner, content) - elif isinstance(content, TBox): - return self.contains(content.to_span()) else: return super().contains(content) - def overlaps(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + def overlaps(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ Returns whether ``self`` overlaps ``other``. That is, both share at least an instant @@ -234,12 +215,10 @@ def overlaps(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool """ if isinstance(other, int): return overlaps_span_span(self._inner, int_to_intspan(other)) - elif isinstance(other, TBox): - return self.overlaps(other.to_span()) else: return super().overlaps(other) - def is_same(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + def is_same(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ Returns whether ``self`` and the bounding period of ``other`` is the same. @@ -254,13 +233,11 @@ def is_same(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: """ if isinstance(other, int): return span_eq(self._inner, int_to_intspan(other)) - elif isinstance(other, TBox): - return self.is_same(other.to_span()) else: return super().is_same(other) # ------------------------- Position Operations --------------------------- - def is_left(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + def is_left(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. @@ -275,12 +252,10 @@ def is_left(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: """ if isinstance(other, int): return left_intspan_int(self._inner, other) - elif isinstance(other, TBox): - return self.is_left(other.to_span()) else: return super().is_left(other) - def is_over_or_left(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + def is_over_or_left(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or at the same time). @@ -296,12 +271,10 @@ def is_over_or_left(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) """ if isinstance(other, int): return overleft_intspan_int(self._inner, other) - elif isinstance(other, TBox): - return self.is_over_or_left(other.to_span()) else: return super().is_over_or_left(other) - def is_right(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + def is_right(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. @@ -316,12 +289,10 @@ def is_right(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool """ if isinstance(other, int): return right_intspan_int(other, self._inner) - elif isinstance(other, TBox): - return self.is_right(other.to_span()) else: return super().is_right(other) - def is_over_or_right(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> bool: + def is_over_or_right(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts (or at the same time). @@ -336,14 +307,12 @@ def is_over_or_right(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) overright_span_span, overright_span_spanset, overright_intspan_int """ if isinstance(other, int): - return overright_intspan_int(other, self._inner) - elif isinstance(other, TBox): - return self.is_over_or_right(other.to_span()) + return overright_intspan_int(self._inner, other) else: return super().is_over_or_right(other) # ------------------------- Distance Operations --------------------------- - def distance(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> float: + def distance(self, other: Union[int, IntSpan, IntSpanSet]) -> float: """ Returns the distance between ``self`` and ``other``. @@ -358,8 +327,6 @@ def distance(self, other: Union[int, TBox, IntSet, IntSpan, IntSpanSet]) -> floa """ if isinstance(other, int): return distance_intset_int(self._inner, other) - elif isinstance(other, TBox): - return self.distance(other.to_span()) else: return super().distance(other) @@ -373,7 +340,7 @@ def intersection(self, other: IntSpan) -> Optional[IntSpan]: ... @overload - def intersection(self, other: Union[IntSet, IntSpanSet]) -> Optional[IntSpanSet]: + def intersection(self, other: Union[IntSpan, IntSpanSet]) -> Optional[IntSpanSet]: ... def intersection(self, other): @@ -389,12 +356,9 @@ def intersection(self, other): MEOS Functions: intersection_span_span, intersection_spanset_span, intersection_intset_int """ - from .intset import IntSet from .intspanset import IntSpanSet if isinstance(other, int): return intersection_intset_int(self._inner, other) - elif isinstance(other, IntSet): - return self.intersection(other.to_span()) elif isinstance(other, IntSpan): result = intersection_span_span(self._inner, other._inner) return IntSpan(_inner=result) if result is not None else None @@ -404,7 +368,7 @@ def intersection(self, other): else: super().intersection(other) - def minus(self, other: Union[int, IntSet, IntSpan, IntSpanSet]) -> IntSpanSet: + def minus(self, other: Union[int, IntSpan, IntSpanSet]) -> IntSpanSet: """ Returns the difference of ``self`` and ``other``. @@ -417,12 +381,9 @@ def minus(self, other: Union[int, IntSet, IntSpan, IntSpanSet]) -> IntSpanSet: MEOS Functions: minus_span_span, minus_spanset_span, minus_intset_int """ - from .intset import IntSet from .intspanset import IntSpanSet if isinstance(other, int): result = minus_intspan_int(self._inner, other) - elif isinstance(other, IntSet): - return self.minus(other.to_spanset()) elif isinstance(other, IntSpan): result = minus_span_span(self._inner, other._inner) elif isinstance(other, IntSpanSet): @@ -431,7 +392,7 @@ def minus(self, other: Union[int, IntSet, IntSpan, IntSpanSet]) -> IntSpanSet: super().minus(other) return IntSpanSet(_inner=result) if result is not None else None - def union(self, other: Union[int, IntSet, IntSpan, IntSpanSet]) -> IntSpanSet: + def union(self, other: Union[int, IntSpan, IntSpanSet]) -> IntSpanSet: """ Returns the union of ``self`` and ``other``. @@ -444,12 +405,9 @@ def union(self, other: Union[int, IntSet, IntSpan, IntSpanSet]) -> IntSpanSet: MEOS Functions: union_spanset_span, union_span_span, union_intset_int """ - from .intset import IntSet from .intspanset import IntSpanSet if isinstance(other, int): result = union_intspan_int(self._inner, other) - elif isinstance(other, IntSet): - return self.union(other.to_spanset()) elif isinstance(other, IntSpan): result = union_span_span(self._inner, other._inner) elif isinstance(other, IntSpanSet): diff --git a/pymeos/tests/collections/number/intspan_test.py b/pymeos/tests/collections/number/intspan_test.py new file mode 100644 index 00000000..9f3d5c65 --- /dev/null +++ b/pymeos/tests/collections/number/intspan_test.py @@ -0,0 +1,305 @@ +from copy import copy + +import pytest + +from pymeos import IntSpan, IntSpanSet, IntSet + +from tests.conftest import TestPyMEOS + + +class TestIntSpan(TestPyMEOS): + intspan = IntSpan('[7, 10)') + + @staticmethod + def assert_intspan_equality(intspan: IntSpan, + lower: int = None, + upper: int = None, + lower_inc: bool = None, + upper_inc: bool = None): + if lower is not None: + assert intspan.lower() == lower + if upper is not None: + assert intspan.upper() == upper + if lower_inc is not None: + assert intspan.lower_inc() == lower_inc + if upper_inc is not None: + assert intspan.upper_inc() == upper_inc + + +class TestIntSpanConstructors(TestIntSpan): + + @pytest.mark.parametrize( + 'source, params', + [ + ('(7, 10)', (8, 10, True, False)), + ('[7, 10]', (7, 11, True, True)), + ] + ) + def test_string_constructor(self, source, params): + intspan = IntSpan(source) + self.assert_intspan_equality(intspan, *params) + + @pytest.mark.parametrize( + 'input_lower,input_upper,lower,upper', + [ + ('7', '10', 7, 10), + (7, 10, 7, 10), + (7, '10', 7, 10), + ], + ids=['string', 'int', 'mixed'] + ) + def test_constructor_bounds(self, input_lower, input_upper, lower, upper): + intspan = IntSpan(lower=lower, upper=upper) + self.assert_intspan_equality(intspan, lower, upper) + + def test_constructor_bound_inclusivity_defaults(self): + intspan = IntSpan(lower='7', upper='10') + self.assert_intspan_equality(intspan, lower_inc=True, upper_inc=False) + + @pytest.mark.parametrize( + 'lower,upper', + [ + (True, True), + (True, False), + (False, True), + (False, False), + ] + ) + def test_constructor_bound_inclusivity(self, lower, upper): + intspan = IntSpan(lower='7', upper='10', lower_inc=lower, upper_inc=upper) + self.assert_intspan_equality(intspan, lower_inc=lower, upper_inc=upper) + + def test_hexwkb_constructor(self): + source = '010D0001070000000A000000' + intspan = IntSpan.from_hexwkb(source) + self.assert_intspan_equality(intspan, 7, 10, False, False) + + def test_from_as_constructor(self): + assert self.intspan == IntSpan(str(self.intspan)) + assert self.intspan == IntSpan.from_wkb(self.intspan.as_wkb()) + assert self.intspan == IntSpan.from_hexwkb(self.intspan.as_hexwkb()) + + def test_copy_constructor(self): + other = copy(self.intspan) + assert self.intspan == other + assert self.intspan is not other + + +class TestIntSpanOutputs(TestIntSpan): + + def test_str(self): + assert str(self.intspan) == '(7, 10)' + + def test_repr(self): + assert repr(self.intspan) == 'IntSpan((7, 10))' + + def test_hexwkb(self): + assert self.intspan.as_hexwkb() == '010D0001070000000A000000' + + +# class TestIntSpanConversions(TestIntSpan): + + # def test_to_periodset(self): + # intspanset = self.intspan.to_periodset() + # assert isinstance(intspanset, IntSpanSet) + # assert intspanset.num_periods() == 1 + # assert intspanset.start_period() == self.intspan + + +class TestIntSpanAccessors(TestIntSpan): + intspan2 = IntSpan('[8, 11]') + + def test_lower(self): + assert self.intspan.lower() == 7 + assert self.intspan2.lower() == 8 + + def test_upper(self): + assert self.intspan.upper() == 10 + assert self.intspan2.upper() == 12 + + def test_lower_inc(self): + assert not self.intspan.lower_inc() + assert self.intspan2.lower_inc() + + def test_upper_inc(self): + assert not self.intspan.upper_inc() + assert self.intspan2.upper_inc() + + def test_width(self): + assert self.intspan.width() == 2 + assert self.intspan2.width() == 3 + + def test_hash(self): + assert hash(self.intspan) == 1519224342 + + +class TestIntSpanTransformations(TestIntSpan): + + @pytest.mark.parametrize( + 'delta,result', + [(4, (11, 15, True, False)), + (-4, (3, 7, True, False)), + ], + ids=['positive delta', 'negative delta'] + ) + def test_shift(self, delta, result): + shifted = self.intspan.shift(delta) + self.assert_intspan_equality(shifted, *result) + + @pytest.mark.parametrize( + 'delta,result', + [(4, (7, 11, True, False)), + ], + ids=['positive'] + ) + def test_scale(self, delta, result): + scaled = self.intspan.scale(delta) + self.assert_intspan_equality(scaled, *result) + + def test_shift_scale(self): + shifted_scaled = self.intspan.shift_scale(4, 2) + self.assert_intspan_equality(shifted_scaled, 11, 13, False, False) + + +class TestIntSpanTopologicalPositionFunctions(TestIntSpan): + value = 5 + intspan = IntSpan('(1, 20)') + # intspanset = IntSpanSet('{(1, 20), (31, 41)}') + + @pytest.mark.parametrize( + 'other', + [value, intspan], # intspanset], + ids=['value', 'intspan'] #, 'intspanset'] + ) + def test_is_adjacent(self, other): + self.intspan.is_adjacent(other) + + @pytest.mark.parametrize( + 'other', + [intspan], #intspanset], + ids=['intspan'] #, 'intspanset'] + ) + def test_is_contained_in(self, other): + self.intspan.is_contained_in(other) + + @pytest.mark.parametrize( + 'other', + [value, intspan], # intspanset], + ids=['value', 'intspan'] #, 'intspanset'] + ) + def test_contains(self, other): + self.intspan.contains(other) + _ = other in self.intspan + + @pytest.mark.parametrize( + 'other', + [value, intspan], # intspanset], + ids=['value', 'intspan'] #, 'intspanset'] + ) + def test_overlaps(self, other): + self.intspan.overlaps(other) + + @pytest.mark.parametrize( + 'other', + [value, intspan], # intspanset], + ids=['value', 'intspan'] #, 'intspanset'] + ) + def test_is_same(self, other): + self.intspan.is_same(other) + + @pytest.mark.parametrize( + 'other', + [value, intspan], # intspanset], + ids=['value', 'intspan'] #, 'intspanset'] + ) + def test_is_left(self, other): + self.intspan.is_left(other) + + @pytest.mark.parametrize( + 'other', + [value, intspan], # intspanset], + ids=['value', 'intspan'] #, 'intspanset'] + ) + def test_is_over_or_left(self, other): + self.intspan.is_over_or_left(other) + + @pytest.mark.parametrize( + 'other', + [value, intspan], # intspanset], + ids=['value', 'intspan'] #, 'intspanset'] + ) + def test_is_right(self, other): + self.intspan.is_right(other) + + @pytest.mark.parametrize( + 'other', + [value, intspan], # intspanset], + ids=['value', 'intspan'] #, 'intspanset'] + ) + def test_is_over_or_right(self, other): + self.intspan.is_over_or_right(other) + + @pytest.mark.parametrize( + 'other', + [value, intspan], # intspanset], + ids=['value', 'intspan'] #, 'intspanset'] + ) + def test_distance(self, other): + self.intspan.distance(other) + + +class TestIntSpanSetFunctions(TestIntSpan): + value = 1 + intspan = IntSpan('(1, 20)') + # intspanset = IntSpanSet('{(1, 20), (31, 41)}') + + @pytest.mark.parametrize( + 'other', + [value, intspan], # intspanset], + ids=['value', 'intspan'] #, 'intspanset'] + ) + def test_intersection(self, other): + self.intspan.intersection(other) + self.intspan * other + + # @pytest.mark.parametrize( + # 'other', + # [value, intspan, intspanset], + # ids=['value', 'intspan', 'intspanset'] + # ) + # def test_union(self, other): + # self.intspan.union(other) + # self.intspan + other + + # @pytest.mark.parametrize( + # 'other', + # [value, intspan, intspanset], + # ids=['value', 'intspan', 'intspanset'] + # ) + # def test_minus(self, other): + # self.intspan.minus(other) + # self.intspan - other + + +class TestIntSpanComparisons(TestIntSpan): + intspan = IntSpan('(1, 20)') + other = IntSpan('[5, 10)') + + def test_eq(self): + _ = self.intspan == self.other + + def test_ne(self): + _ = self.intspan != self.other + + def test_lt(self): + _ = self.intspan < self.other + + def test_le(self): + _ = self.intspan <= self.other + + def test_gt(self): + _ = self.intspan > self.other + + def test_ge(self): + _ = self.intspan >= self.other + From fdd4f01a0c668901dc96a259b6e7e9f3097050ca Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sat, 30 Sep 2023 11:24:55 +0200 Subject: [PATCH 061/101] Improve tests --- pymeos/pymeos/collections/base/set.py | 12 +- pymeos/pymeos/collections/base/span.py | 88 +++++------- pymeos/pymeos/collections/number/floatset.py | 14 +- pymeos/pymeos/collections/number/intset.py | 25 ++-- pymeos/pymeos/collections/number/intspan.py | 21 +-- pymeos/pymeos/collections/time/period.py | 88 +++++++----- pymeos/pymeos/collections/time/periodset.py | 132 +++++++++++------- .../pymeos/collections/time/timestampset.py | 118 ++++++++++------ .../tests/collections/number/intspan_test.py | 26 ++-- pymeos/tests/collections/time/period_test.py | 50 ++++--- .../tests/collections/time/periodset_test.py | 52 ++++--- 11 files changed, 349 insertions(+), 277 deletions(-) diff --git a/pymeos/pymeos/collections/base/set.py b/pymeos/pymeos/collections/base/set.py index 10f1c75f..7ed3eaf7 100644 --- a/pymeos/pymeos/collections/base/set.py +++ b/pymeos/pymeos/collections/base/set.py @@ -244,7 +244,8 @@ def is_contained_in(self, container) -> bool: True if contained, False otherwise MEOS Functions: - contained_span_span, contained_span_spanset, contained_set_set, contained_spanset_spanset + contained_span_span, contained_span_spanset, contained_set_set, + contained_spanset_spanset """ if isinstance(container, Set): return contained_set_set(self._inner, container._inner) @@ -296,7 +297,7 @@ def overlaps(self, other) -> bool: True if overlaps, False otherwise MEOS Functions: - overlaps_set_set, overlaps_span_span, overlaps_spanset_spanset + overlaps_set_set, overlaps_span_span, overlaps_spanset_spanset """ if isinstance(other, Set): return overlaps_set_set(self._inner, other._inner) @@ -354,7 +355,7 @@ def is_over_or_right(self, other) -> bool: True if overlapping or to the right, False otherwise MEOS Functions: - overright_span_span, overright_span_spanset + overright_span_span, overright_span_spanset """ if isinstance(other, Set): return overright_set_set(self._inner, other._inner) @@ -373,7 +374,7 @@ def is_right(self, other) -> bool: True if right, False otherwise MEOS Functions: - right_set_set, right_span_span, right_span_spanset + right_set_set, right_span_span, right_span_spanset """ if isinstance(other, Set): return right_set_set(self._inner, other._inner) @@ -418,7 +419,8 @@ def intersection(self, other): A :class:`Collection` instance. The actual class depends on ``other``. MEOS Functions: - intersection_set_set, intersection_spanset_span, intersection_spanset_spanset + intersection_set_set, intersection_spanset_span, + intersection_spanset_spanset """ raise TypeError(f'Operation not supported with type {other.__class__}') diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index 47abf6ad..853f2c9d 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -10,7 +10,6 @@ from .collection import Collection if TYPE_CHECKING: - from .set import Set from .spanset import SpanSet T = TypeVar('T') @@ -45,7 +44,8 @@ def __init__(self, string: Optional[str] = None, *, else: lower_converted = self.__class__._parse_value_function(lower) upper_converted = self.__class__._parse_value_function(upper) - self._inner = self.__class__._make_function(lower_converted, upper_converted, lower_inc, upper_inc) + self._inner = self.__class__._make_function(lower_converted, + upper_converted, lower_inc, upper_inc) def __copy__(self: Self) -> Self: """ @@ -131,7 +131,8 @@ def as_hexwkb(self) -> str: Returns the WKB representation of ``self`` in hex-encoded ASCII. Returns: - A :class:`str` object with the WKB representation of ``self`` in hex-encoded ASCII. + A :class:`str` object with the WKB representation of ``self`` in + hex-encoded ASCII. MEOS Functions: span_as_hexwkb @@ -172,7 +173,8 @@ def lower_inc(self) -> bool: Returns whether the lower bound of the period is inclusive or not Returns: - True if the lower bound of the period is inclusive and False otherwise + True if the lower bound of the period is inclusive and False + otherwise MEOS Functions: span_lower_inc @@ -184,7 +186,8 @@ def upper_inc(self) -> bool: Returns whether the upper bound of the period is inclusive or not Returns: - True if the upper bound of the period is inclusive and False otherwise + True if the upper bound of the period is inclusive and False + otherwise MEOS Functions: span_upper_inc @@ -219,8 +222,8 @@ def __hash__(self) -> int: @abstractmethod def is_adjacent(self: Self, other) -> bool: """ - Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them - contains it. + Returns whether ``self`` is adjacent to ``other``. That is, they share + a bound but only one of them contains it. Args: other: object to compare with @@ -231,11 +234,8 @@ def is_adjacent(self: Self, other) -> bool: MEOS Functions: adjacent_span_span, adjacent_span_spanset, """ - from .set import Set from .spanset import SpanSet - if isinstance(other, Set): - return adjacent_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return adjacent_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): return adjacent_spanset_span(other._inner, self._inner) @@ -277,11 +277,8 @@ def contains(self, content) -> bool: contains_span_span, contains_span_spanset, contains_period_timestamp, contains_period_timestampset, contains_period_temporal """ - from .set import Set from .spanset import SpanSet - if isinstance(content, Set): - return contains_span_span(self._inner, set_span(content._inner)) - elif isinstance(content, Span): + if isinstance(content, Span): return contains_span_span(self._inner, content._inner) elif isinstance(content, SpanSet): return contains_span_spanset(self._inner, content._inner) @@ -306,7 +303,8 @@ def __contains__(self, item): def overlaps(self, other) -> bool: """ - Returns whether ``self`` overlaps ``other``. That is, both share at least an element. + Returns whether ``self`` overlaps ``other``. That is, both share at + least an element. Args: other: temporal object to compare with @@ -317,11 +315,8 @@ def overlaps(self, other) -> bool: MEOS Functions: overlaps_span_span, overlaps_span_spanset """ - from .set import Set from .spanset import SpanSet - if isinstance(other, Set): - return overlaps_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return overlaps_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): return overlaps_spanset_span(other._inner, self._inner) @@ -330,7 +325,8 @@ def overlaps(self, other) -> bool: def is_same(self, other) -> bool: """ - Returns whether ``self`` and the bounding period of ``other`` is the same. + Returns whether ``self`` and the bounding period of ``other`` is the + same. Args: other: temporal object to compare with @@ -341,11 +337,8 @@ def is_same(self, other) -> bool: MEOS Functions: same_period_temporal """ - from .set import Set from .spanset import SpanSet - if isinstance(other, Set): - return span_eq(self._inner, set_span(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return span_eq(self._inner, other._inner) elif isinstance(other, SpanSet): return span_eq(self._inner, spanset_span(other._inner)) @@ -355,7 +348,8 @@ def is_same(self, other) -> bool: # ------------------------- Position Operations --------------------------- def is_left(self, other) -> bool: """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + Returns whether ``self`` is strictly before ``other``. That is, + ``self`` ends before ``other`` starts. Args: other: temporal object to compare with @@ -366,11 +360,8 @@ def is_left(self, other) -> bool: MEOS Functions: left_span_span, left_span_spanset """ - from .set import Set from .spanset import SpanSet - if isinstance(other, Set): - return left_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return left_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): return left_span_spanset(self._inner, other._inner) @@ -379,8 +370,8 @@ def is_left(self, other) -> bool: def is_over_or_left(self, other) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). + Returns whether ``self`` is before ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same time). Args: other: temporal object to compare with @@ -391,11 +382,8 @@ def is_over_or_left(self, other) -> bool: MEOS Functions: overleft_span_span, overleft_span_spanset """ - from .set import Set from .spanset import SpanSet - if isinstance(other, Set): - return overleft_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return overleft_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): return overleft_span_spanset(self._inner, other._inner) @@ -404,8 +392,8 @@ def is_over_or_left(self, other) -> bool: def is_over_or_right(self, other) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is after ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same time). Args: other: temporal object to compare with @@ -427,7 +415,8 @@ def is_over_or_right(self, other) -> bool: def is_right(self, other) -> bool: """ - Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. + Returns whether ``self`` is strictly after ``other``. That is, ``self`` + starts after ``other`` ends. Args: other: temporal object to compare with @@ -438,11 +427,8 @@ def is_right(self, other) -> bool: MEOS Functions: right_span_span, right_span_spanset """ - from .set import Set from .spanset import SpanSet - if isinstance(other, Set): - return right_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return right_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): return right_span_spanset(self._inner, other._inner) @@ -463,11 +449,8 @@ def distance(self, other) -> float: MEOS Functions: distance_span_span, distance_spanset_span """ - from .set import Set from .spanset import SpanSet - if isinstance(other, Set): - return distance_span_span(self._inner, set_span(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return distance_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): return distance_spanset_span(other._inner, self._inner) @@ -487,7 +470,8 @@ def intersection(self, other): A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_span_span, intersection_spanset_span, intersection_period_timestamp + intersection_span_span, intersection_spanset_span, + intersection_period_timestamp """ raise TypeError(f'Operation not supported with type {other.__class__}') @@ -502,7 +486,8 @@ def __mul__(self, other): A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_span_span, intersection_spanset_span, intersection_period_timestamp + intersection_span_span, intersection_spanset_span, + intersection_period_timestamp """ return self.intersection(other) @@ -545,11 +530,8 @@ def union(self, other): MEOS Functions: union_period_timestamp, union_spanset_span, union_span_span """ - from .set import Set from .spanset import SpanSet - if isinstance(other, Set): - return union_spanset_span(set_to_spanset(other._inner), self._inner) - elif isinstance(other, Span): + if isinstance(other, Span): return union_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): return union_spanset_span(other._inner, self._inner) diff --git a/pymeos/pymeos/collections/number/floatset.py b/pymeos/pymeos/collections/number/floatset.py index 488a33e7..562cf618 100644 --- a/pymeos/pymeos/collections/number/floatset.py +++ b/pymeos/pymeos/collections/number/floatset.py @@ -2,13 +2,13 @@ from typing import List, Union, overload, Optional -from pymeos_cffi import floatset_in, floatset_make, floatset_out, floatset_start_value, \ - floatset_end_value, \ - floatset_value_n, floatset_values, contains_floatset_float, intersection_floatset_float, intersection_set_set, \ - left_floatset_float, overleft_floatset_float, right_floatset_float, overright_floatset_float, \ - minus_floatset_float, \ - minus_set_set, union_set_set, union_floatset_float, floatset_shift_scale, minus_float_floatset, \ - distance_floatset_float +from pymeos_cffi import floatset_in, floatset_make, floatset_out, \ + floatset_start_value, floatset_end_value, floatset_value_n, \ + floatset_values, contains_floatset_float, intersection_floatset_float, \ + intersection_set_set, left_floatset_float, overleft_floatset_float, \ + right_floatset_float, overright_floatset_float, minus_floatset_float, \ + minus_set_set, union_set_set, union_floatset_float, floatset_shift_scale, \ + minus_float_floatset, distance_floatset_float from .floatspan import FloatSpan from .floatspanset import FloatSpanSet diff --git a/pymeos/pymeos/collections/number/intset.py b/pymeos/pymeos/collections/number/intset.py index 0aa7fa2f..669e26cb 100644 --- a/pymeos/pymeos/collections/number/intset.py +++ b/pymeos/pymeos/collections/number/intset.py @@ -2,10 +2,12 @@ from typing import List, Union, overload, Optional -from pymeos_cffi import intset_in, intset_make, intset_out, intset_start_value, intset_end_value, \ - intset_value_n, intset_values, contains_intset_int, intersection_intset_int, intersection_set_set, minus_intset_int, \ +from pymeos_cffi import intset_in, intset_make, intset_out, intset_start_value, \ + intset_end_value, intset_value_n, intset_values, contains_intset_int, \ + intersection_intset_int, intersection_set_set, minus_intset_int, \ left_intset_int, overleft_intset_int, right_intset_int, overright_intset_int, \ - minus_set_set, union_set_set, union_intset_int, intset_shift_scale, minus_int_intset, distance_intset_int + minus_set_set, union_set_set, union_intset_int, intset_shift_scale, \ + minus_int_intset, distance_intset_int from .intspan import IntSpan from .intspanset import IntSpanSet @@ -152,13 +154,13 @@ def shift(self, delta: int) -> IntSet: """ return self.shift_scale(delta, None) - def scale(self, new_width: int) -> IntSet: + def scale(self, width: int) -> IntSet: """ Returns a new ``IntSet`` instance with all elements scaled to so that the encompassing - span has width ``new_width``. + span has width ``width``. Args: - new_width: The new width. + width: The new width. Returns: A new :class:`IntSet` instance @@ -166,16 +168,16 @@ def scale(self, new_width: int) -> IntSet: MEOS Functions: intset_shift_scale """ - return self.shift_scale(None, new_width) + return self.shift_scale(None, width) - def shift_scale(self, delta: Optional[int], new_width: Optional[int]) -> IntSet: + def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSet: """ Returns a new ``IntSet`` instance with all elements shifted by ``delta`` and scaled to so that the - encompassing span has width ``new_width``. + encompassing span has width ``width``. Args: delta: The value to shift by. - new_width: The new width. + width: The new width. Returns: A new :class:`IntSet` instance @@ -184,7 +186,8 @@ def shift_scale(self, delta: Optional[int], new_width: Optional[int]) -> IntSet: intset_shift_scale """ return IntSet( - _inner=intset_shift_scale(self._inner, delta, new_width, delta is not None, new_width is not None)) + _inner=intset_shift_scale(self._inner, delta, width, + delta is not None, width is not None)) # ------------------------- Topological Operations -------------------------------- diff --git a/pymeos/pymeos/collections/number/intspan.py b/pymeos/pymeos/collections/number/intspan.py index a699e2f0..8ba538ee 100644 --- a/pymeos/pymeos/collections/number/intspan.py +++ b/pymeos/pymeos/collections/number/intspan.py @@ -3,11 +3,14 @@ from typing import Union, overload, Optional, TYPE_CHECKING from pymeos_cffi import intersection_intset_int, distance_intset_int, \ - intspan_in, intspan_lower, intspan_upper, numspan_shift_scale, contains_intspan_int, adjacent_intspan_int, \ - int_to_intspan, overlaps_span_span, span_eq, left_intspan_int, overleft_intspan_int, \ - right_intspan_int, overright_intspan_int, intersection_span_span, intersection_spanset_span, minus_intspan_int, \ - minus_span_span, minus_spanset_span, union_intspan_int, \ - union_span_span, union_spanset_span, intspan_out, intspan_make + intspan_in, intspan_lower, intspan_upper, numspan_shift_scale, \ + contains_intspan_int, adjacent_intspan_int, span_width, \ + int_to_intspan, overlaps_span_span, span_eq, left_intspan_int,\ + overleft_intspan_int, right_intspan_int, overright_intspan_int, \ + intersection_intspan_int, intersection_span_span, intersection_spanset_span, \ + minus_intspan_int, minus_span_span, minus_spanset_span, union_intspan_int, \ + union_span_span, union_spanset_span, intspan_out, intspan_make, \ + distance_intspan_int from .. import Span @@ -106,7 +109,7 @@ def width(self) -> float: MEOS Functions: span_width """ - return self.width() + return span_width(self._inner) # ------------------------- Transformations ------------------------------- def shift(self, delta: int) -> IntSpan: @@ -288,7 +291,7 @@ def is_right(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: right_span_span, right_span_spanset, right_intspan_int """ if isinstance(other, int): - return right_intspan_int(other, self._inner) + return right_intspan_int(self._inner, other) else: return super().is_right(other) @@ -326,7 +329,7 @@ def distance(self, other: Union[int, IntSpan, IntSpanSet]) -> float: distance_span_span, distance_span_spanset, distance_intset_int, """ if isinstance(other, int): - return distance_intset_int(self._inner, other) + return distance_intspan_int(self._inner, other) else: return super().distance(other) @@ -358,7 +361,7 @@ def intersection(self, other): """ from .intspanset import IntSpanSet if isinstance(other, int): - return intersection_intset_int(self._inner, other) + return intersection_intspan_int(self._inner, other) elif isinstance(other, IntSpan): result = intersection_span_span(self._inner, other._inner) return IntSpan(_inner=result) if result is not None else None diff --git a/pymeos/pymeos/collections/time/period.py b/pymeos/pymeos/collections/time/period.py index 500b208e..3a96b16b 100644 --- a/pymeos/pymeos/collections/time/period.py +++ b/pymeos/pymeos/collections/time/period.py @@ -27,9 +27,10 @@ class Period(Span[datetime], TimeCollection): >>> Period('(2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01)') - Another possibility is to provide the ``lower`` and ``upper`` named parameters (of type str or datetime), and - optionally indicate whether the bounds are inclusive or exclusive (by default, the lower bound is inclusive and the - upper is exclusive): + Another possibility is to provide the ``lower`` and ``upper`` named + parameters (of type str or datetime), and optionally indicate whether the + bounds are inclusive or exclusive (by default, the lower bound is inclusive + and the upper is exclusive): >>> Period(lower='2019-09-08 00:00:00+01', upper='2019-09-10 00:00:00+01') >>> Period(lower='2019-09-08 00:00:00+01', upper='2019-09-10 00:00:00+01', lower_inc=False, upper_inc=True) @@ -117,7 +118,8 @@ def duration(self) -> timedelta: Returns the duration of the period. Returns: - A :class:`datetime.timedelta` instance representing the duration of the period + A :class:`datetime.timedelta` instance representing the duration of + the period MEOS Functions: period_duration @@ -165,7 +167,8 @@ def scale(self, duration: timedelta) -> Period: >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' Args: - duration: :class:`datetime.timedelta` instance representing the duration of the new period + duration: :class:`datetime.timedelta` instance representing the + duration of the new period Returns: A new :class:`Period` instance @@ -175,9 +178,11 @@ def scale(self, duration: timedelta) -> Period: """ return self.shift_scale(duration=duration) - def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> Period: + def shift_scale(self, shift: Optional[timedelta] = None, + duration: Optional[timedelta] = None) -> Period: """ - Returns a new period that starts at ``self`` shifted by ``shift`` and has duration ``duration`` + Returns a new period that starts at ``self`` shifted by ``shift`` and + has duration ``duration`` Examples: >>> Period('[2000-01-01, 2000-01-10]').shift_scale(shift=timedelta(days=2), duration=timedelta(days=4)) @@ -185,7 +190,8 @@ def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[time Args: shift: :class:`datetime.timedelta` instance to shift - duration: :class:`datetime.timedelta` instance representing the duration of the new period + duration: :class:`datetime.timedelta` instance representing the + duration of the new period Returns: A new :class:`Period` instance @@ -204,8 +210,8 @@ def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[time # ------------------------- Topological Operations ------------------------ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them - contains it. + Returns whether ``self`` is temporally adjacent to ``other``. That is, + they share a bound but only one of them contains it. Examples: >>> Period('[2012-01-01, 2012-01-02)').is_adjacent(Period('[2012-01-02, 2012-01-03]')) @@ -228,7 +234,8 @@ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return adjacent_period_timestamp(self._inner, datetime_to_timestamptz(other)) + return adjacent_period_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, Temporal): return adjacent_span_span(self._inner, temporal_to_period(other._inner)) elif isinstance(other, get_args(Box)): @@ -291,7 +298,8 @@ def contains(self, content: Union[Time, Box, Temporal]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(content, datetime): - return contains_period_timestamp(self._inner, datetime_to_timestamptz(content)) + return contains_period_timestamp(self._inner, + datetime_to_timestamptz(content)) elif isinstance(content, Temporal): return self.contains(content.period()) elif isinstance(content, get_args(Box)): @@ -301,7 +309,8 @@ def contains(self, content: Union[Time, Box, Temporal]) -> bool: def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant + Returns whether ``self`` temporally overlaps ``other``. That is, both + share at least an instant Examples: >>> Period('[2012-01-01, 2012-01-02]').overlaps(Period('[2012-01-02, 2012-01-03]')) @@ -318,13 +327,14 @@ def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: True if overlaps, False otherwise MEOS Functions: - overlaps_span_span, overlaps_span_spanset, overlaps_period_timestampset, - overlaps_period_temporal + overlaps_span_span, overlaps_span_spanset, + overlaps_period_timestampset, overlaps_period_temporal """ from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overlaps_span_span(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) + return overlaps_span_span(self._inner, + timestamp_to_period(datetime_to_timestamptz(other))) elif isinstance(other, Temporal): return self.overlaps(other.period()) elif isinstance(other, get_args(Box)): @@ -352,14 +362,16 @@ def is_same(self, other: Union[Time, Box, Temporal]) -> bool: elif isinstance(other, get_args(Box)): return self.is_same(other.to_period()) elif isinstance(other, datetime): - return span_eq(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) + return span_eq(self._inner, + timestamp_to_period(datetime_to_timestamptz(other))) else: return super().is_same(other) # ------------------------- Position Operations --------------------------- def is_left(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + Returns whether ``self`` is strictly before ``other``. That is, + ``self`` ends before ``other`` starts. Examples: >>> Period('[2012-01-01, 2012-01-02)').is_left(Period('[2012-01-02, 2012-01-03]')) @@ -382,7 +394,8 @@ def is_left(self, other: Union[Time, Box, Temporal]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overafter_timestamp_period(datetime_to_timestamptz(other), self._inner) + return overafter_timestamp_period(datetime_to_timestamptz(other), + self._inner) elif isinstance(other, Temporal): return self.is_left(other.period()) elif isinstance(other, get_args(Box)): @@ -392,8 +405,8 @@ def is_left(self, other: Union[Time, Box, Temporal]) -> bool: def is_over_or_left(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). + Returns whether ``self`` is before ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same time). Examples: >>> Period('[2012-01-01, 2012-01-02)').is_over_or_left(Period('[2012-01-02, 2012-01-03]')) @@ -416,7 +429,8 @@ def is_over_or_left(self, other: Union[Time, Box, Temporal]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overbefore_period_timestamp(self._inner, datetime_to_timestamptz(other)) + return overbefore_period_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, Temporal): return self.is_over_or_left(other.period()) elif isinstance(other, get_args(Box)): @@ -426,7 +440,8 @@ def is_over_or_left(self, other: Union[Time, Box, Temporal]) -> bool: def is_right(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. + Returns whether ``self`` is strictly after ``other``. That is, ``self`` + starts after ``other`` ends. Examples: >>> Period('[2012-01-02, 2012-01-03]').is_right(Period('[2012-01-01, 2012-01-02)')) @@ -449,7 +464,8 @@ def is_right(self, other: Union[Time, Box, Temporal]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overbefore_timestamp_period(datetime_to_timestamptz(other), self._inner) + return overbefore_timestamp_period(datetime_to_timestamptz(other), + self._inner) elif isinstance(other, Temporal): return self.is_right(other.period()) elif isinstance(other, get_args(Box)): @@ -459,8 +475,8 @@ def is_right(self, other: Union[Time, Box, Temporal]) -> bool: def is_over_or_right(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is after ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same time). Examples: >>> Period('[2012-01-02, 2012-01-03]').is_over_or_right(Period('[2012-01-01, 2012-01-02)')) @@ -483,7 +499,8 @@ def is_over_or_right(self, other: Union[Time, Box, Temporal]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overafter_period_timestamp(self._inner, datetime_to_timestamptz(other)) + return overafter_period_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, Temporal): return self.is_over_or_right(other.period()) elif isinstance(other, get_args(Box)): @@ -508,7 +525,8 @@ def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return timedelta(seconds=distance_period_timestamp(self._inner, datetime_to_timestamptz(other))) + return timedelta(seconds=distance_period_timestamp(self._inner, + datetime_to_timestamptz(other))) elif isinstance(other, Temporal): return self.distance(other.period()) elif isinstance(other, get_args(Box)): @@ -540,12 +558,14 @@ def intersection(self, other: Time) -> Optional[Time]: A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_span_span, intersection_spanset_span, intersection_period_timestamp + intersection_span_span, intersection_spanset_span, + intersection_period_timestamp """ from .periodset import PeriodSet from .timestampset import TimestampSet if isinstance(other, datetime): - result = intersection_period_timestamp(self._inner, datetime_to_timestamptz(other)) + result = intersection_period_timestamp(self._inner, + datetime_to_timestamptz(other)) return timestamptz_to_datetime(result) if result is not None else None elif isinstance(other, TimestampSet): return self.intersection(other.to_periodset()) @@ -574,7 +594,8 @@ def minus(self, other: Time) -> PeriodSet: from .periodset import PeriodSet from .timestampset import TimestampSet if isinstance(other, datetime): - result = minus_period_timestamp(self._inner, datetime_to_timestamptz(other)) + result = minus_period_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): return self.minus(other.to_periodset()) elif isinstance(other, Period): @@ -596,12 +617,13 @@ def union(self, other: Time) -> PeriodSet: A :class:`PeriodSet` instance. MEOS Functions: - union_period_timestamp, union_spanset_span, union_span_span + union_period_timestamp, union_spanset_span, union_span_span """ from .periodset import PeriodSet from .timestampset import TimestampSet if isinstance(other, datetime): - result = union_period_timestamp(self._inner, datetime_to_timestamptz(other)) + result = union_period_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): result = super().union(other) elif isinstance(other, Period): diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py index efd8aa9c..54de27db 100644 --- a/pymeos/pymeos/collections/time/periodset.py +++ b/pymeos/pymeos/collections/time/periodset.py @@ -94,10 +94,12 @@ def duration(self, ignore_gaps: Optional[bool] = False) -> timedelta: the upper bound of the last period. Parameters: - ignore_gaps: Whether to take into account potential time gaps in the periodset. + ignore_gaps: Whether to take into account potential time gaps in + the periodset. Returns: - A :class:`datetime.timedelta` instance representing the duration of the periodset + A :class:`datetime.timedelta` instance representing the duration of + the periodset MEOS Functions: periodset_duration @@ -334,7 +336,8 @@ def periods(self) -> List[Period]: # ------------------------- Transformations ------------------------------- def shift(self, delta: timedelta) -> PeriodSet: """ - Returns a new periodset that is the result of shifting ``self`` by ``delta`` + Returns a new periodset that is the result of shifting ``self`` by + ``delta`` Examples: >>> Period('[2000-01-01, 2000-01-10]').shift(timedelta(days=2)) @@ -353,14 +356,16 @@ def shift(self, delta: timedelta) -> PeriodSet: def scale(self, duration: timedelta) -> PeriodSet: """ - Returns a new periodset that starts as ``self`` but has duration ``duration`` + Returns a new periodset that starts as ``self`` but has duration + ``duration`` Examples: >>> Period('[2000-01-01, 2000-01-10]').scale(timedelta(days=2)) >>> 'Period([2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01])' Args: - duration: :class:`datetime.timedelta` instance representing the duration of the new period + duration: :class:`datetime.timedelta` instance representing the + duration of the new period Returns: A new :class:`PeriodSet` instance @@ -370,9 +375,11 @@ def scale(self, duration: timedelta) -> PeriodSet: """ return self.shift_scale(duration=duration) - def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> PeriodSet: + def shift_scale(self, shift: Optional[timedelta] = None, + duration: Optional[timedelta] = None) -> PeriodSet: """ - Returns a new periodset that starts at ``self`` shifted by ``shift`` and has duration ``duration`` + Returns a new periodset that starts at ``self`` shifted by ``shift`` + and has duration ``duration`` Examples: >>> Period('[2000-01-01, 2000-01-10]').shift_scale(shift=timedelta(days=2), duration=timedelta(days=4)) @@ -380,7 +387,8 @@ def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[time Args: shift: :class:`datetime.timedelta` instance to shift - duration: :class:`datetime.timedelta` instance representing the duration of the new period + duration: :class:`datetime.timedelta` instance representing the + duration of the new period Returns: A new :class:`PeriodSet` instance @@ -399,8 +407,8 @@ def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[time # ------------------------- Topological Operations ------------------------ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them - contains it. + Returns whether ``self`` is temporally adjacent to ``other``. That is, + they share a bound but only one of them contains it. Examples: >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_adjacent(PeriodSet('{[2012-01-02, 2012-01-03]}')) @@ -417,13 +425,14 @@ def is_adjacent(self, other: Union[Time, Box, Temporal]) -> bool: True if adjacent, False otherwise MEOS Functions: - adjacent_spanset_span, adjacent_spanset_spanset, adjacent_periodset_timestamp, - adjacent_periodset_timestampset + adjacent_spanset_span, adjacent_spanset_spanset, + adjacent_periodset_timestamp, adjacent_periodset_timestampset """ from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return adjacent_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + return adjacent_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, Temporal): return self.is_adjacent(other.period()) elif isinstance(other, get_args(Box)): @@ -450,12 +459,14 @@ def is_contained_in(self, container: Union[Time, Box, Temporal]) -> bool: True if contained, False otherwise MEOS Functions: - contained_spanset_span, contained_spanset_spanset, contained_periodset_temporal + contained_spanset_span, contained_spanset_spanset, + contained_periodset_temporal """ from ...temporal import Temporal from ...boxes import Box if isinstance(container, datetime): - return contained_spanset_span(self._inner, timestamp_to_period(datetime_to_timestamptz(container))) + return contained_spanset_span(self._inner, + timestamp_to_period(datetime_to_timestamptz(container))) elif isinstance(container, Temporal): return self.is_contained_in(container.period()) elif isinstance(container, get_args(Box)): @@ -482,12 +493,14 @@ def contains(self, content: Union[Time, Box, Temporal]) -> bool: True if contains, False otherwise MEOS Functions: - contains_spanset_span, contains_spanset_spanset, contains_periodset_timestamp + contains_spanset_span, contains_spanset_spanset, + contains_periodset_timestamp """ from ...temporal import Temporal from ...boxes import Box if isinstance(content, datetime): - return contains_periodset_timestamp(self._inner, datetime_to_timestamptz(content)) + return contains_periodset_timestamp(self._inner, + datetime_to_timestamptz(content)) elif isinstance(content, Temporal): return self.contains(content.period()) elif isinstance(content, get_args(Box)): @@ -514,14 +527,16 @@ def __contains__(self, item): True if contains, False otherwise MEOS Functions: - contains_spanset_span, contains_spanset_spanset, contains_periodset_timestamp, - contains_periodset_timestampset, contains_periodset_temporal + contains_spanset_span, contains_spanset_spanset, + contains_periodset_timestamp, contains_periodset_timestampset, + contains_periodset_temporal """ return self.contains(item) def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant + Returns whether ``self`` temporally overlaps ``other``. That is, both + share at least an instant Examples: >>> PeriodSet('{[2012-01-01, 2012-01-02]}').overlaps(PeriodSet('{[2012-01-02, 2012-01-03]}')) @@ -543,7 +558,8 @@ def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overlaps_spanset_span(self._inner, timestamp_to_period(datetime_to_timestamptz(other))) + return overlaps_spanset_span(self._inner, + timestamp_to_period(datetime_to_timestamptz(other))) elif isinstance(other, Temporal): return self.overlaps(other.period()) elif isinstance(other, get_args(Box)): @@ -553,7 +569,8 @@ def overlaps(self, other: Union[Time, Box, Temporal]) -> bool: def is_same(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether the bounding period of `self` is the same as the bounding period of `other`. + Returns whether the bounding period of `self` is the same as the + bounding period of `other`. Args: other: A time or temporal object to compare to `self`. @@ -569,7 +586,8 @@ def is_same(self, other: Union[Time, Box, Temporal]) -> bool: # ------------------------- Position Operations --------------------------- def is_left(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + Returns whether ``self`` is strictly before ``other``. That is, + ``self`` ends before ``other`` starts. Examples: >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_left(PeriodSet('{[2012-01-02, 2012-01-03]}')) @@ -591,7 +609,8 @@ def is_left(self, other: Union[Time, Box, Temporal]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return before_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + return before_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, Temporal): return self.is_left(other.period()) elif isinstance(other, get_args(Box)): @@ -601,8 +620,8 @@ def is_left(self, other: Union[Time, Box, Temporal]) -> bool: def is_over_or_left(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). + Returns whether ``self`` is before ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same time). Examples: >>> PeriodSet('{[2012-01-01, 2012-01-02)}').is_over_or_left(PeriodSet('{[2012-01-02, 2012-01-03]}')) @@ -619,13 +638,15 @@ def is_over_or_left(self, other: Union[Time, Box, Temporal]) -> bool: True if before, False otherwise MEOS Functions: - overleft_spanset_span, overleft_spanset_spanset, overbefore_periodset_timestamp, - overbefore_periodset_timestampset, overbefore_periodset_temporal + overleft_spanset_span, overleft_spanset_spanset, + overbefore_periodset_timestamp, overbefore_periodset_timestampset, + overbefore_periodset_temporal """ from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overbefore_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + return overbefore_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, Temporal): return self.is_over_or_left(other.period()) elif isinstance(other, get_args(Box)): @@ -635,8 +656,8 @@ def is_over_or_left(self, other: Union[Time, Box, Temporal]) -> bool: def is_over_or_right(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is after ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same time). Examples: >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_over_or_right(PeriodSet('{[2012-01-01, 2012-01-02)}')) @@ -653,13 +674,14 @@ def is_over_or_right(self, other: Union[Time, Box, Temporal]) -> bool: True if overlapping or after, False otherwise MEOS Functions: - overright_spanset_span, overright_spanset_spanset, overafter_periodset_timestamp, - overafter_periodset_timestampset, + overright_spanset_span, overright_spanset_spanset, + overafter_periodset_timestamp, overafter_periodset_timestampset, """ from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overafter_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + return overafter_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, Temporal): return self.is_over_or_right(other.period()) elif isinstance(other, get_args(Box)): @@ -669,7 +691,8 @@ def is_over_or_right(self, other: Union[Time, Box, Temporal]) -> bool: def is_right(self, other: Union[Time, Box, Temporal]) -> bool: """ - Returns whether ``self`` is strictly after ``other``.That is, ``self`` starts after ``other`` ends. + Returns whether ``self`` is strictly after ``other``.That is, ``self`` + starts after ``other`` ends. Examples: >>> PeriodSet('{[2012-01-02, 2012-01-03]}').is_right(PeriodSet('{[2012-01-01, 2012-01-02)}')) @@ -686,12 +709,14 @@ def is_right(self, other: Union[Time, Box, Temporal]) -> bool: True if after, False otherwise MEOS Functions: - right_spanset_span, right_spanset_spanset, overbefore_timestamp_periodset + right_spanset_span, right_spanset_spanset, + overbefore_timestamp_periodset """ from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overbefore_timestamp_periodset(datetime_to_timestamptz(other), self._inner) + return overbefore_timestamp_periodset(datetime_to_timestamptz(other), + self._inner) elif isinstance(other, Temporal): return self.is_right(other.period()) elif isinstance(other, get_args(Box)): @@ -711,13 +736,14 @@ def distance(self, other: Union[Time, Box, Temporal]) -> timedelta: A :class:`datetime.timedelta` instance MEOS Functions: - distance_periodset_period, distance_periodset_periodset, distance_periodset_timestamp, - distance_periodset_timestampset + distance_periodset_period, distance_periodset_periodset, + distance_periodset_timestamp, distance_periodset_timestampset """ from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return timedelta(seconds=distance_periodset_timestamp(self._inner, datetime_to_timestamptz(other))) + return timedelta(seconds=distance_periodset_timestamp(self._inner, + datetime_to_timestamptz(other))) if isinstance(other, Temporal): return self.distance(other.period()) elif isinstance(other, get_args(Box)): @@ -753,12 +779,14 @@ def intersection(self, other: Time) -> Union[PeriodSet, datetime, TimestampSet]: A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_periodset_timestamp, intersection_spanset_spanset, intersection_spanset_span + intersection_periodset_timestamp, intersection_spanset_spanset, + intersection_spanset_span """ from .period import Period from .timestampset import TimestampSet if isinstance(other, datetime): - result = intersection_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + result = intersection_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) return timestamptz_to_datetime(result) if result is not None else None elif isinstance(other, TimestampSet): result = super().intersection(other) @@ -783,7 +811,8 @@ def __mul__(self, other): A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_periodset_timestamp, intersection_spanset_spanset, intersection_spanset_span + intersection_periodset_timestamp, intersection_spanset_spanset, + intersection_spanset_span """ return self.intersection(other) @@ -798,10 +827,11 @@ def minus(self, other: Time) -> PeriodSet: A :class:`PeriodSet` instance. MEOS Functions: - minus_spanset_span, minus_spanset_spanset, minus_periodset_timestamp + minus_spanset_span, minus_spanset_spanset, minus_periodset_timestamp """ if isinstance(other, datetime): - result = minus_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + result = minus_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) else: result = super().minus(other) return PeriodSet(_inner=result) if result is not None else None @@ -817,7 +847,8 @@ def __sub__(self, other): A :class:`PeriodSet` instance. MEOS Functions: - minus_spanset_span, minus_spanset_spanset, minus_periodset_timestamp + minus_spanset_span, minus_spanset_spanset, + minus_periodset_timestamp """ return self.minus(other) @@ -832,10 +863,12 @@ def union(self, other: Time) -> PeriodSet: A :class:`PeriodSet` instance. MEOS Functions: - union_periodset_timestamp, union_spanset_spanset, union_spanset_span + union_periodset_timestamp, union_spanset_spanset, + union_spanset_span """ if isinstance(other, datetime): - result = union_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) + result = union_periodset_timestamp(self._inner, + datetime_to_timestamptz(other)) else: result = super().union(other) return PeriodSet(_inner=result) if result is not None else None @@ -851,7 +884,8 @@ def __add__(self, other): A :class:`PeriodSet` instance. MEOS Functions: - union_periodset_timestamp, union_spanset_spanset, union_spanset_span + union_periodset_timestamp, union_spanset_spanset, + union_spanset_span """ return self.union(other) diff --git a/pymeos/pymeos/collections/time/timestampset.py b/pymeos/pymeos/collections/time/timestampset.py index 64a11f43..7f3b6b8b 100644 --- a/pymeos/pymeos/collections/time/timestampset.py +++ b/pymeos/pymeos/collections/time/timestampset.py @@ -21,8 +21,8 @@ class TimestampSet(Set[datetime], TimeCollection): """ Class for representing lists of distinct timestamp values. - ``TimestampSet`` objects can be created with a single argument of type string - as in MobilityDB. + ``TimestampSet`` objects can be created with a single argument of type + string as in MobilityDB. >>> TimestampSet(string='{2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01, 2019-09-11 00:00:00+01}') @@ -61,7 +61,8 @@ def __str__(self): # ------------------------- Conversions ----------------------------------- def to_spanset(self) -> PeriodSet: """ - Returns a PeriodSet that contains a Period for each Timestamp in ``self``. + Returns a PeriodSet that contains a Period for each Timestamp in + ``self``. Returns: A new :class:`PeriodSet` instance @@ -74,7 +75,8 @@ def to_spanset(self) -> PeriodSet: def to_periodset(self) -> PeriodSet: """ - Returns a PeriodSet that contains a Period for each Timestamp in ``self``. + Returns a PeriodSet that contains a Period for each Timestamp in + ``self``. Returns: A new :class:`PeriodSet` instance @@ -112,11 +114,12 @@ def to_period(self) -> Period: # ------------------------- Accessors ------------------------------------- def duration(self) -> timedelta: """ - Returns the duration of the time ignoring gaps, i.e. the duration from the - first timestamp to the last one. + Returns the duration of the time ignoring gaps, i.e. the duration from + the first timestamp to the last one. Returns: - A :class:`datetime.timedelta` instance representing the duration of the period + A :class:`datetime.timedelta` instance representing the duration of + the period MEOS Functions: period_duration @@ -172,7 +175,8 @@ def elements(self) -> List[datetime]: # ------------------------- Transformations ------------------------------- def shift(self, delta: timedelta) -> TimestampSet: """ - Returns a new TimestampSet that is the result of shifting ``self`` by ``delta`` + Returns a new TimestampSet that is the result of shifting ``self`` by + ``delta`` Examples: >>> TimestampSet('{2000-01-01, 2000-01-10}').shift(timedelta(days=2)) @@ -191,14 +195,16 @@ def shift(self, delta: timedelta) -> TimestampSet: def scale(self, duration: timedelta) -> TimestampSet: """ - Returns a new TimestampSet that with the scaled so that the span of ``self`` is ``duration``. + Returns a new TimestampSet that with the scaled so that the span of + ``self`` is ``duration``. Examples: >>> TimestampSet('{2000-01-01, 2000-01-10}').scale(timedelta(days=2)) >>> 'TimestampSet({2000-01-01 00:00:00+01, 2000-01-03 00:00:00+01})' Args: - duration: :class:`datetime.timedelta` instance representing the span of the new set + duration: :class:`datetime.timedelta` instance representing the + span of the new set Returns: A new :class:`PeriodSet` instance @@ -208,9 +214,11 @@ def scale(self, duration: timedelta) -> TimestampSet: """ return self.shift_scale(duration=duration) - def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[timedelta] = None) -> TimestampSet: + def shift_scale(self, shift: Optional[timedelta] = None, + duration: Optional[timedelta] = None) -> TimestampSet: """ - Returns a new TimestampSet that is the result of shifting and scaling ``self``. + Returns a new TimestampSet that is the result of shifting and scaling + ``self``. Examples: >>> TimestampSet('{2000-01-01, 2000-01-10}').shift_scale(shift=timedelta(days=2), duration=timedelta(days=4)) @@ -218,7 +226,8 @@ def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[time Args: shift: :class:`datetime.timedelta` instance to shift - duration: :class:`datetime.timedelta` instance representing the span of the new set + duration: :class:`datetime.timedelta` instance representing the + span of the new set Returns: A new :class:`PeriodSet` instance @@ -237,8 +246,8 @@ def shift_scale(self, shift: Optional[timedelta] = None, duration: Optional[time # ------------------------- Topological Operations ------------------------ def is_adjacent(self, other: Union[Period, PeriodSet, Temporal, Box]) -> bool: """ - Returns whether ``self`` is temporally adjacent to ``other``. That is, they share a bound but only one of them - contains it. + Returns whether ``self`` is temporally adjacent to ``other``. That is, + they share a bound but only one of them contains it. Examples: >>> TimestampSet('{2012-01-01, 2012-01-02}').is_adjacent(Period('[2012-01-02, 2012-01-03]')) @@ -255,7 +264,7 @@ def is_adjacent(self, other: Union[Period, PeriodSet, Temporal, Box]) -> bool: True if adjacent, False otherwise MEOS Functions: - adjacent_span_span, adjacent_spanset_span + adjacent_span_span, adjacent_spanset_span """ from ...temporal import Temporal from ...boxes import Box @@ -266,7 +275,8 @@ def is_adjacent(self, other: Union[Period, PeriodSet, Temporal, Box]) -> bool: else: super().is_adjacent(other) - def is_contained_in(self, container: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) -> bool: + def is_contained_in(self, container: Union[Period, PeriodSet, TimestampSet, + Temporal, Box]) -> bool: """ Returns whether ``self`` is temporally contained in ``container``. @@ -285,7 +295,8 @@ def is_contained_in(self, container: Union[Period, PeriodSet, TimestampSet, Temp True if contained, False otherwise MEOS Functions: - contained_span_span, contained_span_spanset, contained_set_set, contained_spanset_spanset + contained_span_span, contained_span_spanset, contained_set_set, + contained_spanset_spanset """ from ...temporal import Temporal from ...boxes import Box @@ -315,11 +326,13 @@ def contains(self, content: Union[datetime, TimestampSet, Temporal]) -> bool: True if contains, False otherwise MEOS Functions: - contains_timestampset_timestamp, contains_set_set, contains_spanset_spanset + contains_timestampset_timestamp, contains_set_set, + contains_spanset_spanset """ from ...temporal import Temporal if isinstance(content, datetime): - return contains_timestampset_timestamp(self._inner, datetime_to_timestamptz(content)) + return contains_timestampset_timestamp(self._inner, + datetime_to_timestamptz(content)) elif isinstance(content, Temporal): return self.to_spanset().contains(content) else: @@ -344,13 +357,16 @@ def __contains__(self, item): True if contains, False otherwise MEOS Functions: - contains_timestampset_timestamp, contains_set_set, contains_spanset_spanset + contains_timestampset_timestamp, contains_set_set, + contains_spanset_spanset """ return self.contains(item) - def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) -> bool: + def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Temporal, + Box]) -> bool: """ - Returns whether ``self`` temporally overlaps ``other``. That is, both share at least an instant + Returns whether ``self`` temporally overlaps ``other``. That is, both + share at least an instant Examples: >>> TimestampSet('{2012-01-01, 2012-01-02}').overlaps(TimestampSet('{2012-01-02, 2012-01-03}')) @@ -367,12 +383,13 @@ def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) True if overlaps, False otherwise MEOS Functions: - overlaps_set_set, overlaps_span_span, overlaps_spanset_spanset + overlaps_set_set, overlaps_span_span, overlaps_spanset_spanset """ from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return contains_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) + return contains_timestampset_timestamp(self._inner, + datetime_to_timestamptz(other)) elif isinstance(other, Temporal): return self.to_spanset().overlaps(other) elif isinstance(other, Box): @@ -382,7 +399,8 @@ def overlaps(self, other: Union[Period, PeriodSet, TimestampSet, Temporal, Box]) def is_same(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether the bounding period of `self` is the same as the bounding period of `other`. + Returns whether the bounding period of `self` is the same as the + bounding period of `other`. Args: other: A time or temporal object to compare to `self`. @@ -398,7 +416,8 @@ def is_same(self, other: Union[Time, Temporal, Box]) -> bool: # ------------------------- Position Operations --------------------------- def is_left(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + Returns whether ``self`` is strictly before ``other``. That is, + ``self`` ends before ``other`` starts. Examples: >>> TimestampSet('{2012-01-01, 2012-01-02}').is_left(TimestampSet('{2012-01-03}')) @@ -420,7 +439,8 @@ def is_left(self, other: Union[Time, Temporal, Box]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return after_timestamp_timestampset(datetime_to_timestamptz(other), self._inner) + return after_timestamp_timestampset(datetime_to_timestamptz(other), + self._inner) elif isinstance(other, Temporal): return self.to_period().is_left(other) elif isinstance(other, get_args(Box)): @@ -430,8 +450,8 @@ def is_left(self, other: Union[Time, Temporal, Box]) -> bool: def is_over_or_left(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). + Returns whether ``self`` is before ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same time). Examples: >>> TimestampSet('{2012-01-01, 2012-01-02}').is_over_or_left(Period('[2012-01-02, 2012-01-03]')) @@ -453,7 +473,8 @@ def is_over_or_left(self, other: Union[Time, Temporal, Box]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overafter_timestamp_timestampset(datetime_to_timestamptz(other), self._inner) + return overafter_timestamp_timestampset(datetime_to_timestamptz(other), + self._inner) elif isinstance(other, Temporal): return self.to_period().is_over_or_left(other) elif isinstance(other, get_args(Box)): @@ -463,8 +484,8 @@ def is_over_or_left(self, other: Union[Time, Temporal, Box]) -> bool: def is_over_or_right(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is after ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same time). Examples: >>> TimestampSet('{2012-01-02, 2012-01-03}').is_over_or_right(Period('[2012-01-01, 2012-01-02)')) @@ -486,7 +507,8 @@ def is_over_or_right(self, other: Union[Time, Temporal, Box]) -> bool: from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return overbefore_timestamp_timestampset(datetime_to_timestamptz(other), self._inner) + return overbefore_timestamp_timestampset(datetime_to_timestamptz(other), + self._inner) elif isinstance(other, Temporal): return self.to_period().is_over_or_right(other) elif isinstance(other, get_args(Box)): @@ -496,8 +518,8 @@ def is_over_or_right(self, other: Union[Time, Temporal, Box]) -> bool: def is_right(self, other: Union[Time, Temporal, Box]) -> bool: """ - Returns whether ``self`` is strictly after ``other``. That is, the first timestamp in ``self`` - is after ``other``. + Returns whether ``self`` is strictly after ``other``. That is, the + first timestamp in ``self`` is after ``other``. Examples: >>> TimestampSet('{2012-01-02, 2012-01-03}').is_right(Period('[2012-01-01, 2012-01-02)')) @@ -514,12 +536,14 @@ def is_right(self, other: Union[Time, Temporal, Box]) -> bool: True if after, False otherwise MEOS Functions: - overbefore_timestamp_timestampset, right_set_set, right_span_span, right_span_spanset + overbefore_timestamp_timestampset, right_set_set, right_span_span, + right_span_spanset """ from ...temporal import Temporal from ...boxes import Box if isinstance(other, datetime): - return before_timestamp_timestampset(datetime_to_timestamptz(other), self._inner) + return before_timestamp_timestampset(datetime_to_timestamptz(other), + self._inner) elif isinstance(other, Temporal): return self.to_period().is_right(other) elif isinstance(other, get_args(Box)): @@ -539,7 +563,8 @@ def distance(self, other: Union[Time, Temporal, Box]) -> timedelta: A :class:`datetime.timedelta` instance MEOS Functions: - distance_timestampset_timestamp, distance_set_set, distance_span_span, distance_spanset_span + distance_timestampset_timestamp, distance_set_set, + distance_span_span, distance_spanset_span """ from ...temporal import Temporal from ...boxes import Box @@ -576,12 +601,14 @@ def intersection(self, other: Union[Time, Temporal]) -> Optional[Time]: A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_set_set, intersection_spanset_span, intersection_spanset_spanset + intersection_set_set, intersection_spanset_span, + intersection_spanset_spanset """ from .period import Period from .periodset import PeriodSet if isinstance(other, datetime): - result = intersection_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) + result = intersection_timestampset_timestamp(self._inner, + datetime_to_timestamptz(other)) return timestamptz_to_datetime(result) if result is not None else None elif isinstance(other, TimestampSet): result = intersection_set_set(self._inner, other._inner) @@ -616,12 +643,14 @@ def minus(self, other: Union[Time, Temporal, Box]) -> Optional[Time]: A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - minus_timestampset_timestamp, minus_set_set, minus_spanset_span, minus_spanset_spanset + minus_timestampset_timestamp, minus_set_set, minus_spanset_span, + minus_spanset_spanset """ from .period import Period from .periodset import PeriodSet if isinstance(other, datetime): - result = minus_timestampset_timestamp(self._inner, datetime_to_timestamptz(other)) + result = minus_timestampset_timestamp(self._inner, + datetime_to_timestamptz(other)) return TimestampSet(_inner=result) if result is not None else None elif isinstance(other, TimestampSet): result = minus_set_set(self._inner, other._inner) @@ -674,7 +703,8 @@ def union(self, other: Union[Time, Temporal, Box]) -> Union[PeriodSet, Timestamp A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - union_timestampset_timestamp, union_set_set, union_spanset_span, union_spanset_spanset + union_timestampset_timestamp, union_set_set, union_spanset_span, + union_spanset_spanset """ from .period import Period from .periodset import PeriodSet diff --git a/pymeos/tests/collections/number/intspan_test.py b/pymeos/tests/collections/number/intspan_test.py index 9f3d5c65..6c528d98 100644 --- a/pymeos/tests/collections/number/intspan_test.py +++ b/pymeos/tests/collections/number/intspan_test.py @@ -32,7 +32,7 @@ class TestIntSpanConstructors(TestIntSpan): 'source, params', [ ('(7, 10)', (8, 10, True, False)), - ('[7, 10]', (7, 11, True, True)), + ('[7, 10]', (7, 11, True, False)), ] ) def test_string_constructor(self, source, params): @@ -67,12 +67,12 @@ def test_constructor_bound_inclusivity_defaults(self): ) def test_constructor_bound_inclusivity(self, lower, upper): intspan = IntSpan(lower='7', upper='10', lower_inc=lower, upper_inc=upper) - self.assert_intspan_equality(intspan, lower_inc=lower, upper_inc=upper) + self.assert_intspan_equality(intspan, lower_inc=True, upper_inc=False) def test_hexwkb_constructor(self): source = '010D0001070000000A000000' intspan = IntSpan.from_hexwkb(source) - self.assert_intspan_equality(intspan, 7, 10, False, False) + self.assert_intspan_equality(intspan, 7, 10, True, False) def test_from_as_constructor(self): assert self.intspan == IntSpan(str(self.intspan)) @@ -88,10 +88,10 @@ def test_copy_constructor(self): class TestIntSpanOutputs(TestIntSpan): def test_str(self): - assert str(self.intspan) == '(7, 10)' + assert str(self.intspan) == '[7, 10)' def test_repr(self): - assert repr(self.intspan) == 'IntSpan((7, 10))' + assert repr(self.intspan) == 'IntSpan([7, 10))' def test_hexwkb(self): assert self.intspan.as_hexwkb() == '010D0001070000000A000000' @@ -118,16 +118,16 @@ def test_upper(self): assert self.intspan2.upper() == 12 def test_lower_inc(self): - assert not self.intspan.lower_inc() + assert self.intspan.lower_inc() assert self.intspan2.lower_inc() def test_upper_inc(self): assert not self.intspan.upper_inc() - assert self.intspan2.upper_inc() + assert not self.intspan2.upper_inc() def test_width(self): - assert self.intspan.width() == 2 - assert self.intspan2.width() == 3 + assert self.intspan.width() == 3 + assert self.intspan2.width() == 4 def test_hash(self): assert hash(self.intspan) == 1519224342 @@ -137,8 +137,8 @@ class TestIntSpanTransformations(TestIntSpan): @pytest.mark.parametrize( 'delta,result', - [(4, (11, 15, True, False)), - (-4, (3, 7, True, False)), + [(4, (11, 14, True, False)), + (-4, (3, 6, True, False)), ], ids=['positive delta', 'negative delta'] ) @@ -148,7 +148,7 @@ def test_shift(self, delta, result): @pytest.mark.parametrize( 'delta,result', - [(4, (7, 11, True, False)), + [(4, (7, 12, True, False)), ], ids=['positive'] ) @@ -158,7 +158,7 @@ def test_scale(self, delta, result): def test_shift_scale(self): shifted_scaled = self.intspan.shift_scale(4, 2) - self.assert_intspan_equality(shifted_scaled, 11, 13, False, False) + self.assert_intspan_equality(shifted_scaled, 11, 14, True, False) class TestIntSpanTopologicalPositionFunctions(TestIntSpan): diff --git a/pymeos/tests/collections/time/period_test.py b/pymeos/tests/collections/time/period_test.py index 992bd673..5c7525a4 100644 --- a/pymeos/tests/collections/time/period_test.py +++ b/pymeos/tests/collections/time/period_test.py @@ -188,7 +188,6 @@ class TestPeriodTopologicalPositionFunctions(TestPeriod): periodset = PeriodSet( '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') timestamp = datetime(year=2020, month=1, day=1) - timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') instant = TFloatInst('1.0@2020-01-01') discrete_sequence = TFloatSeq('{1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31}') stepwise_sequence = TFloatSeq('Interp=Step;(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') @@ -200,9 +199,9 @@ class TestPeriodTopologicalPositionFunctions(TestPeriod): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_adjacent(self, other): @@ -220,9 +219,9 @@ def test_is_contained_in(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_contains(self, other): @@ -231,9 +230,9 @@ def test_contains(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_overlaps(self, other): @@ -241,9 +240,9 @@ def test_overlaps(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_same(self, other): @@ -251,9 +250,9 @@ def test_is_same(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_before(self, other): @@ -262,9 +261,9 @@ def test_is_before(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_over_or_before(self, other): @@ -273,9 +272,9 @@ def test_is_over_or_before(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_after(self, other): @@ -284,9 +283,9 @@ def test_is_after(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_over_or_after(self, other): @@ -295,9 +294,9 @@ def test_is_over_or_after(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_distance(self, other): @@ -309,12 +308,11 @@ class TestPeriodSetFunctions(TestPeriod): periodset = PeriodSet( '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') timestamp = datetime(year=2020, month=1, day=1) - timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset], - ids=['period', 'periodset', 'timestamp', 'timestampset'] + [period, periodset, timestamp], + ids=['period', 'periodset', 'timestamp'] ) def test_intersection(self, other): self.period.intersection(other) @@ -322,8 +320,8 @@ def test_intersection(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset], - ids=['period', 'periodset', 'timestamp', 'timestampset'] + [period, periodset, timestamp], + ids=['period', 'periodset', 'timestamp'] ) def test_union(self, other): self.period.union(other) @@ -331,8 +329,8 @@ def test_union(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset], - ids=['period', 'periodset', 'timestamp', 'timestampset'] + [period, periodset, timestamp], + ids=['period', 'periodset', 'timestamp'] ) def test_minus(self, other): self.period.minus(other) diff --git a/pymeos/tests/collections/time/periodset_test.py b/pymeos/tests/collections/time/periodset_test.py index c4d09008..dc02f5a6 100644 --- a/pymeos/tests/collections/time/periodset_test.py +++ b/pymeos/tests/collections/time/periodset_test.py @@ -4,7 +4,7 @@ import pytest -from pymeos import Period, PeriodSet, TimestampSet, TFloatInst, TFloatSeq, STBox, TFloatSeqSet, TBox +from pymeos import Period, PeriodSet, TFloatInst, TFloatSeq, STBox, TFloatSeqSet, TBox from tests.conftest import TestPyMEOS @@ -169,7 +169,6 @@ class TestPeriodSetTopologicalPositionFunctions(TestPeriodSet): periodset = PeriodSet( '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') timestamp = datetime(year=2020, month=1, day=1) - timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') instant = TFloatInst('1.0@2020-01-01') discrete_sequence = TFloatSeq('{1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31}') stepwise_sequence = TFloatSeq('Interp=Step;(1.0@2020-01-01, 3.0@2020-01-10, 10.0@2020-01-20, 0.0@2020-01-31]') @@ -181,9 +180,9 @@ class TestPeriodSetTopologicalPositionFunctions(TestPeriodSet): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_adjacent(self, other): @@ -201,9 +200,9 @@ def test_is_contained_in(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_contains(self, other): @@ -212,9 +211,9 @@ def test_contains(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_overlaps(self, other): @@ -222,9 +221,9 @@ def test_overlaps(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_same(self, other): @@ -232,9 +231,9 @@ def test_is_same(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_before(self, other): @@ -242,9 +241,9 @@ def test_is_before(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_over_or_before(self, other): @@ -252,9 +251,9 @@ def test_is_over_or_before(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_after(self, other): @@ -262,9 +261,9 @@ def test_is_after(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_is_over_or_after(self, other): @@ -272,9 +271,9 @@ def test_is_over_or_after(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset, instant, discrete_sequence, stepwise_sequence, sequence_set, + [period, periodset, timestamp, instant, discrete_sequence, stepwise_sequence, sequence_set, continuous_sequence, tbox, stbox], - ids=['period', 'periodset', 'timestamp', 'timestampset', 'instant', 'discrete_sequence', 'stepwise_sequence', + ids=['period', 'periodset', 'timestamp', 'instant', 'discrete_sequence', 'stepwise_sequence', 'continuous_sequence', 'sequence_set', 'tbox', 'stbox'] ) def test_distance(self, other): @@ -286,12 +285,11 @@ class TestPeriodSetSetFunctions(TestPeriodSet): periodset = PeriodSet( '{(2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0), (2021-01-01 00:00:00+0, 2021-01-31 00:00:00+0)}') timestamp = datetime(year=2020, month=1, day=1) - timestampset = TimestampSet('{2020-01-01 00:00:00+0, 2020-01-31 00:00:00+0}') @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset], - ids=['period', 'periodset', 'timestamp', 'timestampset'] + [period, periodset, timestamp], + ids=['period', 'periodset', 'timestamp'] ) def test_intersection(self, other): self.periodset.intersection(other) @@ -299,8 +297,8 @@ def test_intersection(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset], - ids=['period', 'periodset', 'timestamp', 'timestampset'] + [period, periodset, timestamp], + ids=['period', 'periodset', 'timestamp'] ) def test_union(self, other): self.periodset.union(other) @@ -308,8 +306,8 @@ def test_union(self, other): @pytest.mark.parametrize( 'other', - [period, periodset, timestamp, timestampset], - ids=['period', 'periodset', 'timestamp', 'timestampset'] + [period, periodset, timestamp], + ids=['period', 'periodset', 'timestamp'] ) def test_minus(self, other): self.periodset.minus(other) From 6f533cced93814d1d8b5719791c459aa36726143 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sat, 30 Sep 2023 14:00:28 +0200 Subject: [PATCH 062/101] Improve tests --- pymeos/pymeos/collections/number/floatspan.py | 224 +++++++----------- pymeos/pymeos/collections/number/intspan.py | 44 +--- .../tests/collections/number/intspan_test.py | 6 +- pymeos_cffi/pymeos_cffi/__init__.py | 9 +- pymeos_cffi/pymeos_cffi/builder/meos.h | 9 +- pymeos_cffi/pymeos_cffi/functions.py | 61 +++-- 6 files changed, 155 insertions(+), 198 deletions(-) diff --git a/pymeos/pymeos/collections/number/floatspan.py b/pymeos/pymeos/collections/number/floatspan.py index 95b583ac..ad151e51 100644 --- a/pymeos/pymeos/collections/number/floatspan.py +++ b/pymeos/pymeos/collections/number/floatspan.py @@ -2,19 +2,20 @@ from typing import Union, overload, Optional, TYPE_CHECKING -from pymeos_cffi import intersection_floatset_float, distance_floatset_float, \ - floatspan_in, floatspan_lower, floatspan_upper, numspan_shift_scale, contains_floatspan_float, \ - adjacent_floatspan_float, \ - float_to_floatspan, overlaps_span_span, span_eq, left_floatspan_float, overleft_floatspan_float, \ - right_floatspan_float, overright_floatspan_float, intersection_span_span, intersection_spanset_span, \ - minus_floatspan_float, minus_span_span, minus_spanset_span, union_floatspan_float, \ - union_span_span, union_spanset_span, floatspan_out, floatspan_make +from pymeos_cffi import intersection_floatspan_float, distance_floatspan_float, \ + floatspan_in, floatspan_lower, floatspan_upper, floatspan_shift_scale, \ + contains_floatspan_float, adjacent_floatspan_float, \ + float_to_floatspan, span_eq, \ + left_floatspan_float, overleft_floatspan_float, \ + right_floatspan_float, overright_floatspan_float, \ + intersection_span_span, intersection_spanset_span, \ + minus_floatspan_float, minus_span_span, minus_spanset_span, \ + union_floatspan_float, union_span_span, union_spanset_span, \ + floatspan_out, floatspan_make, span_width from .. import Span if TYPE_CHECKING: - from ...boxes import TBox - from .floatset import FloatSet from .floatspanset import FloatSpanSet @@ -28,9 +29,10 @@ class FloatSpan(Span[float]): >>> FloatSpan('(2.5, 5.21]') - Another possibility is to provide the ``lower`` and ``upper`` named parameters (of type str or float), and - optionally indicate whether the bounds are inclusive or exclusive (by default, the lower bound is inclusive and the - upper is exclusive): + Another possibility is to provide the ``lower`` and ``upper`` named + parameters (of type str or float), and optionally indicate whether the + bounds are inclusive or exclusive (by default, the lower bound is inclusive + and the upper is exclusive): >>> FloatSpan(lower=2.0, upper=5.8) >>> FloatSpan(lower=2.0, upper=5.8, lower_inc=False, upper_inc=True) @@ -109,12 +111,13 @@ def width(self) -> float: MEOS Functions: span_width """ - return self.width() + return span_width(self._inner) # ------------------------- Transformations ------------------------------- def shift(self, delta: float) -> FloatSpan: """ - Return a new ``FloatSpan`` with the lower and upper bounds shifted by ``delta``. + Return a new ``FloatSpan`` with the lower and upper bounds shifted by + ``delta``. Args: delta: The value to shift by @@ -127,12 +130,13 @@ def shift(self, delta: float) -> FloatSpan: """ return self.shift_scale(delta, None) - def scale(self, new_width: float) -> FloatSpan: + def scale(self, width: float) -> FloatSpan: """ - Return a new ``FloatSpan`` with the lower and upper bounds scaled so that the width is ``new_width``. + Return a new ``FloatSpan`` with the lower and upper bounds scaled so + that the width is ``width``. Args: - new_width: The new width + width: The new width Returns: A new ``FloatSpan`` instance @@ -140,15 +144,16 @@ def scale(self, new_width: float) -> FloatSpan: MEOS Functions: floatspan_shift_scale """ - return self.shift_scale(None, new_width) + return self.shift_scale(None, width) - def shift_scale(self, delta: Optional[float], new_width: Optional[float]) -> FloatSpan: + def shift_scale(self, delta: Optional[float], width: Optional[float]) -> FloatSpan: """ - Return a new ``FloatSpan`` with the lower and upper bounds shifted by ``delta`` and scaled so that the width is ``new_width``. + Return a new ``FloatSpan`` with the lower and upper bounds shifted by + ``delta`` and scaled so that the width is ``width``. Args: delta: The value to shift by - new_width: The new width + width: The new width Returns: A new ``FloatSpan`` instance @@ -156,14 +161,18 @@ def shift_scale(self, delta: Optional[float], new_width: Optional[float]) -> Flo MEOS Functions: floatspan_shift_scale """ - return FloatSpan(numspan_shift_scale(self._inner, delta, new_width, delta is not None, new_width is not None)) + d = delta if delta is not None else 0 + w = width if width is not None else 0 + modified = floatspan_shift_scale(self._inner, d, w, delta is not None, + width is not None) + return FloatSpan(_inner=modified) # ------------------------- Topological Operations -------------------------------- - def is_adjacent(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + def is_adjacent(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> bool: """ - Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them - contains it. + Returns whether ``self`` is adjacent to ``other``. That is, they share + a bound but only one of them contains it. Args: other: object to compare with @@ -172,34 +181,14 @@ def is_adjacent(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSe True if adjacent, False otherwise MEOS Functions: - adjacent_span_span, adjacent_span_spanset, adjacent_floatset_float + adjacent_span_span, adjacent_span_spanset, adjacent_floatspan_float """ - if isinstance(other, float): - return adjacent_floatspan_float(self._inner, other) - elif isinstance(other, TBox): - return self.is_adjacent(other.to_span()) + if isinstance(other, int) or isinstance(other, float) : + return adjacent_floatspan_float(self._inner, float(other)) else: return super().is_adjacent(other) - def is_contained_in(self, container: Union[TBox, FloatSpan, FloatSpanSet]) -> bool: - """ - Returns whether ``self`` is contained in ``container``. - - Args: - container: object to compare with - - Returns: - True if contained, False otherwise - - MEOS Functions: - contained_span_span, contained_span_spanset, contained_floatset_float - """ - if isinstance(container, TBox): - return self.is_contained_in(container.to_span()) - else: - return super().is_contained_in(container) - - def contains(self, content: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + def contains(self, content: Union[int, float, FloatSpan, FloatSpanSet]) -> bool: """ Returns whether ``self`` contains ``content``. @@ -210,39 +199,17 @@ def contains(self, content: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet True if contains, False otherwise MEOS Functions: - contains_set_set, contains_floatset_float + contains_set_set, contains_floatspan_float """ - if isinstance(content, float): - return contains_floatspan_float(self._inner, content) - elif isinstance(content, TBox): - return self.contains(content.to_span()) + if isinstance(content, int) or isinstance(content, float) : + return contains_floatspan_float(self._inner, float(content)) else: return super().contains(content) - def overlaps(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: - """ - Returns whether ``self`` overlaps ``other``. That is, both share at least an instant - - Args: - other: object to compare with - - Returns: - True if overlaps, False otherwise - - MEOS Functions: - overlaps_span_span, overlaps_span_spanset, overlaps_period_timestampset, - overlaps_period_temporal - """ - if isinstance(other, float): - return overlaps_span_span(self._inner, float_to_floatspan(other)) - elif isinstance(other, TBox): - return self.overlaps(other.to_span()) - else: - return super().overlaps(other) - - def is_same(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + def is_same(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> bool: """ - Returns whether ``self`` and the bounding period of ``other`` is the same. + Returns whether ``self`` and the bounding period of ``other`` is the + same. Args: other: object to compare with @@ -253,17 +220,16 @@ def is_same(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) MEOS Functions: same_period_temporal """ - if isinstance(other, float): - return span_eq(self._inner, float_to_floatspan(other)) - elif isinstance(other, TBox): - return self.is_same(other.to_span()) + if isinstance(other, int) or isinstance(other, float) : + return span_eq(self._inner, float_to_floatspan(float(other))) else: return super().is_same(other) # ------------------------- Position Operations --------------------------- - def is_left(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + def is_left(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> bool: """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + Returns whether ``self`` is strictly before ``other``. That is, + ``self`` ends before ``other`` starts. Args: other: object to compare with @@ -274,17 +240,15 @@ def is_left(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) MEOS Functions: left_span_span, left_span_spanset, left_floatspan_float """ - if isinstance(other, float): - return left_floatspan_float(self._inner, other) - elif isinstance(other, TBox): - return self.is_left(other.to_span()) + if isinstance(other, int) or isinstance(other, float) : + return left_floatspan_float(self._inner, float(other)) else: return super().is_left(other) - def is_over_or_left(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + def is_over_or_left(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). + Returns whether ``self`` is before ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same value). Args: other: object to compare with @@ -295,16 +259,15 @@ def is_over_or_left(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSp MEOS Functions: overleft_span_span, overleft_span_spanset, overleft_floatspan_float """ - if isinstance(other, float): - return overleft_floatspan_float(self._inner, other) - elif isinstance(other, TBox): - return self.is_over_or_left(other.to_span()) + if isinstance(other, int) or isinstance(other, float) : + return overleft_floatspan_float(self._inner, float(other)) else: return super().is_over_or_left(other) - def is_right(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + def is_right(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> bool: """ - Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. + Returns whether ``self`` is strictly after ``other``. That is, ``self`` + starts after ``other`` ends. Args: other: object to compare with @@ -315,17 +278,15 @@ def is_right(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) MEOS Functions: right_span_span, right_span_spanset, right_floatspan_float """ - if isinstance(other, float): - return right_floatspan_float(other, self._inner) - elif isinstance(other, TBox): - return self.is_right(other.to_span()) + if isinstance(other, int) or isinstance(other, float) : + return right_floatspan_float(self._inner, float(other)) else: return super().is_right(other) - def is_over_or_right(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> bool: + def is_over_or_right(self, other: Union[float, FloatSpan, FloatSpanSet]) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is after ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same value). Args: other: object to compare with @@ -336,15 +297,13 @@ def is_over_or_right(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatS MEOS Functions: overright_span_span, overright_span_spanset, overright_floatspan_float """ - if isinstance(other, float): - return overright_floatspan_float(other, self._inner) - elif isinstance(other, TBox): - return self.is_over_or_right(other.to_span()) + if isinstance(other, int) or isinstance(other, float) : + return overright_floatspan_float(self._inner, float(other)) else: return super().is_over_or_right(other) # ------------------------- Distance Operations --------------------------- - def distance(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) -> float: + def distance(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> float: """ Returns the distance between ``self`` and ``other``. @@ -352,21 +311,19 @@ def distance(self, other: Union[float, TBox, FloatSet, FloatSpan, FloatSpanSet]) other: object to compare with Returns: - A :class:`datetime.timedelta` instance + A float value MEOS Functions: - distance_span_span, distance_span_spanset, distance_floatset_float, + distance_span_span, distance_span_spanset, distance_floatspan_float, """ - if isinstance(other, float): - return distance_floatset_float(self._inner, other) - elif isinstance(other, TBox): - return self.distance(other.to_span()) + if isinstance(other, int) or isinstance(other, float) : + return distance_floatspan_float(self._inner, float(other)) else: return super().distance(other) # ------------------------- Set Operations -------------------------------- @overload - def intersection(self, other: float) -> Optional[float]: + def intersection(self, other: Union[int, float]) -> Optional[float]: ... @overload @@ -374,7 +331,7 @@ def intersection(self, other: FloatSpan) -> Optional[FloatSpan]: ... @overload - def intersection(self, other: Union[FloatSet, FloatSpanSet]) -> Optional[FloatSpanSet]: + def intersection(self, other: FloatSpanSet) -> Optional[FloatSpanSet]: ... def intersection(self, other): @@ -385,17 +342,16 @@ def intersection(self, other): other: object to intersect with Returns: - A :class:`Collection[float]` instance. The actual class depends on ``other``. + A :class:`Collection[float]` instance. The actual class depends on + ``other``. MEOS Functions: - intersection_span_span, intersection_spanset_span, intersection_floatset_float + intersection_span_span, intersection_spanset_span, + intersection_floatset_float """ - from .floatset import FloatSet from .floatspanset import FloatSpanSet - if isinstance(other, float): - return intersection_floatset_float(self._inner, other) - elif isinstance(other, FloatSet): - return self.intersection(other.to_span()) + if isinstance(other, int) or isinstance(other, float) : + return intersection_floatspan_float(self._inner, float(other)) elif isinstance(other, FloatSpan): result = intersection_span_span(self._inner, other._inner) return FloatSpan(_inner=result) if result is not None else None @@ -405,7 +361,7 @@ def intersection(self, other): else: super().intersection(other) - def minus(self, other: Union[float, FloatSet, FloatSpan, FloatSpanSet]) -> FloatSpanSet: + def minus(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> FloatSpanSet: """ Returns the difference of ``self`` and ``other``. @@ -416,14 +372,11 @@ def minus(self, other: Union[float, FloatSet, FloatSpan, FloatSpanSet]) -> Float A :class:`FloatSpanSet` instance. MEOS Functions: - minus_span_span, minus_spanset_span, minus_floatset_float + minus_span_span, minus_spanset_span, minus_floatspan_float """ - from .floatset import FloatSet from .floatspanset import FloatSpanSet - if isinstance(other, float): - result = minus_floatspan_float(self._inner, other) - elif isinstance(other, FloatSet): - return self.minus(other.to_spanset()) + if isinstance(other, int) or isinstance(other, float) : + result = minus_floatspan_float(self._inner, float(other)) elif isinstance(other, FloatSpan): result = minus_span_span(self._inner, other._inner) elif isinstance(other, FloatSpanSet): @@ -432,7 +385,7 @@ def minus(self, other: Union[float, FloatSet, FloatSpan, FloatSpanSet]) -> Float super().minus(other) return FloatSpanSet(_inner=result) if result is not None else None - def union(self, other: Union[float, FloatSet, FloatSpan, FloatSpanSet]) -> FloatSpanSet: + def union(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> FloatSpanSet: """ Returns the union of ``self`` and ``other``. @@ -443,14 +396,11 @@ def union(self, other: Union[float, FloatSet, FloatSpan, FloatSpanSet]) -> Float A :class:`PeriodSet` instance. MEOS Functions: - union_spanset_span, union_span_span, union_floatset_float + union_spanset_span, union_span_span, union_floatspan_float """ - from .floatset import FloatSet from .floatspanset import FloatSpanSet - if isinstance(other, float): - result = union_floatspan_float(self._inner, other) - elif isinstance(other, FloatSet): - return self.union(other.to_spanset()) + if isinstance(other, int) or isinstance(other, float) : + result = union_floatspan_float(self._inner, float(other)) elif isinstance(other, FloatSpan): result = union_span_span(self._inner, other._inner) elif isinstance(other, FloatSpanSet): diff --git a/pymeos/pymeos/collections/number/intspan.py b/pymeos/pymeos/collections/number/intspan.py index 8ba538ee..4b3ca12f 100644 --- a/pymeos/pymeos/collections/number/intspan.py +++ b/pymeos/pymeos/collections/number/intspan.py @@ -3,9 +3,9 @@ from typing import Union, overload, Optional, TYPE_CHECKING from pymeos_cffi import intersection_intset_int, distance_intset_int, \ - intspan_in, intspan_lower, intspan_upper, numspan_shift_scale, \ + intspan_in, intspan_lower, intspan_upper, intspan_shift_scale, \ contains_intspan_int, adjacent_intspan_int, span_width, \ - int_to_intspan, overlaps_span_span, span_eq, left_intspan_int,\ + int_to_intspan, span_eq, left_intspan_int,\ overleft_intspan_int, right_intspan_int, overright_intspan_int, \ intersection_intspan_int, intersection_span_span, intersection_spanset_span, \ minus_intspan_int, minus_span_span, minus_spanset_span, union_intspan_int, \ @@ -159,7 +159,7 @@ def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSpan: """ d = delta if delta is not None else 0 w = width if width is not None else 0 - modified = numspan_shift_scale(self._inner, d, w, delta is not None, + modified = intspan_shift_scale(self._inner, d, w, delta is not None, width is not None) return IntSpan(_inner=modified) @@ -167,8 +167,8 @@ def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSpan: def is_adjacent(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ - Returns whether ``self`` is adjacent to ``other``. That is, they share a bound but only one of them - contains it. + Returns whether ``self`` is adjacent to ``other``. That is, they share + a bound but only one of them contains it. Args: other: object to compare with @@ -202,28 +202,10 @@ def contains(self, content: Union[int, IntSpan, IntSpanSet]) -> bool: else: return super().contains(content) - def overlaps(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: - """ - Returns whether ``self`` overlaps ``other``. That is, both share at least an instant - - Args: - other: object to compare with - - Returns: - True if overlaps, False otherwise - - MEOS Functions: - overlaps_span_span, overlaps_span_spanset, overlaps_period_timestampset, - overlaps_period_temporal - """ - if isinstance(other, int): - return overlaps_span_span(self._inner, int_to_intspan(other)) - else: - return super().overlaps(other) - def is_same(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ - Returns whether ``self`` and the bounding period of ``other`` is the same. + Returns whether ``self`` and the bounding period of ``other`` is the + same. Args: other: object to compare with @@ -260,8 +242,8 @@ def is_left(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: def is_over_or_left(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or - at the same time). + Returns whether ``self`` is before ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same value). Args: other: object to compare with @@ -297,8 +279,8 @@ def is_right(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: def is_over_or_right(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts - (or at the same time). + Returns whether ``self`` is after ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same value). Args: other: object to compare with @@ -323,7 +305,7 @@ def distance(self, other: Union[int, IntSpan, IntSpanSet]) -> float: other: object to compare with Returns: - A :class:`datetime.timedelta` instance + A float value MEOS Functions: distance_span_span, distance_span_spanset, distance_intset_int, @@ -343,7 +325,7 @@ def intersection(self, other: IntSpan) -> Optional[IntSpan]: ... @overload - def intersection(self, other: Union[IntSpan, IntSpanSet]) -> Optional[IntSpanSet]: + def intersection(self, other: IntSpanSet) -> Optional[IntSpanSet]: ... def intersection(self, other): diff --git a/pymeos/tests/collections/number/intspan_test.py b/pymeos/tests/collections/number/intspan_test.py index 6c528d98..fd58b1e4 100644 --- a/pymeos/tests/collections/number/intspan_test.py +++ b/pymeos/tests/collections/number/intspan_test.py @@ -2,7 +2,7 @@ import pytest -from pymeos import IntSpan, IntSpanSet, IntSet +from pymeos import IntSpan, IntSpanSet from tests.conftest import TestPyMEOS @@ -193,8 +193,8 @@ def test_contains(self, other): @pytest.mark.parametrize( 'other', - [value, intspan], # intspanset], - ids=['value', 'intspan'] #, 'intspanset'] + [intspan], # intspanset], + ids=['intspan'] #, 'intspanset'] ) def test_overlaps(self, other): self.intspan.overlaps(other) diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 87b1a30b..299574f0 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -242,19 +242,22 @@ 'timestampset_timestamp_n', 'timestampset_values', 'bigintset_shift_scale', + 'bigintspan_shift_scale', + 'bigintspanset_shift_scale', 'floatset_round', 'floatset_shift_scale', 'floatspan_intspan', 'floatspan_round', + 'floatspan_shift_scale', 'floatspanset_intspanset', 'floatspanset_round', + 'floatspanset_shift_scale', 'geoset_round', 'intset_shift_scale', 'intspan_floatspan', + 'intspan_shift_scale', 'intspanset_floatspanset', - 'numset_shift_scale', - 'numspan_shift_scale', - 'numspanset_shift_scale', + 'intspanset_shift_scale', 'period_shift_scale', 'period_tprecision', 'periodset_shift_scale', diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index c9f1834c..1279bbb4 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -963,19 +963,22 @@ extern TimestampTz *timestampset_values(const Set *ts); extern Set *bigintset_shift_scale(const Set *s, int64 shift, int64 width, bool hasshift, bool haswidth); +extern Span *bigintspan_shift_scale(const Span *s, int64 shift, int64 width, bool hasshift, bool haswidth); +extern SpanSet *bigintspanset_shift_scale(const SpanSet *ss, int64 shift, int64 width, bool hasshift, bool haswidth); extern Set *floatset_round(const Set *s, int maxdd); extern Set *floatset_shift_scale(const Set *s, double shift, double width, bool hasshift, bool haswidth); extern Span *floatspan_intspan(const Span *s); extern Span *floatspan_round(const Span *s, int maxdd); +extern Span *floatspan_shift_scale(const Span *s, double shift, double width, bool hasshift, bool haswidth); extern SpanSet *floatspanset_intspanset(const SpanSet *ss); extern SpanSet *floatspanset_round(const SpanSet *ss, int maxdd); +extern SpanSet *floatspanset_shift_scale(const SpanSet *ss, double shift, double width, bool hasshift, bool haswidth); extern Set *geoset_round(const Set *s, int maxdd); extern Set *intset_shift_scale(const Set *s, int shift, int width, bool hasshift, bool haswidth); extern Span *intspan_floatspan(const Span *s); +extern Span *intspan_shift_scale(const Span *s, int shift, int width, bool hasshift, bool haswidth); extern SpanSet *intspanset_floatspanset(const SpanSet *ss); -extern Set *numset_shift_scale(const Set *s, Datum shift, Datum width, bool hasshift, bool haswidth); -extern Span *numspan_shift_scale(const Span *s, Datum shift, Datum width, bool hasshift, bool haswidth); -extern SpanSet *numspanset_shift_scale(const SpanSet *ss, Datum shift, Datum width, bool hasshift, bool haswidth); +extern SpanSet *intspanset_shift_scale(const SpanSet *ss, int shift, int width, bool hasshift, bool haswidth); extern Span *period_shift_scale(const Span *p, const Interval *shift, const Interval *duration); extern Span *period_tprecision(const Span *s, const Interval *duration, TimestampTz torigin); extern SpanSet *periodset_shift_scale(const SpanSet *ss, const Interval *shift, const Interval *duration); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 8ba808f9..4b48c618 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -1599,6 +1599,24 @@ def bigintset_shift_scale(s: 'const Set *', shift: int, width: int, hasshift: bo return result if result != _ffi.NULL else None +def bigintspan_shift_scale(s: 'const Span *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'Span *': + s_converted = _ffi.cast('const Span *', s) + shift_converted = _ffi.cast('int64', shift) + width_converted = _ffi.cast('int64', width) + result = _lib.bigintspan_shift_scale(s_converted, shift_converted, width_converted, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + +def bigintspanset_shift_scale(ss: 'const SpanSet *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + shift_converted = _ffi.cast('int64', shift) + width_converted = _ffi.cast('int64', width) + result = _lib.bigintspanset_shift_scale(ss_converted, shift_converted, width_converted, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + def floatset_round(s: 'const Set *', maxdd: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) result = _lib.floatset_round(s_converted, maxdd) @@ -1627,6 +1645,13 @@ def floatspan_round(s: 'const Span *', maxdd: int) -> 'Span *': return result if result != _ffi.NULL else None +def floatspan_shift_scale(s: 'const Span *', shift: float, width: float, hasshift: bool, haswidth: bool) -> 'Span *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.floatspan_shift_scale(s_converted, shift, width, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + def floatspanset_intspanset(ss: 'const SpanSet *') -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) result = _lib.floatspanset_intspanset(ss_converted) @@ -1641,6 +1666,13 @@ def floatspanset_round(ss: 'const SpanSet *', maxdd: int) -> 'SpanSet *': return result if result != _ffi.NULL else None +def floatspanset_shift_scale(ss: 'const SpanSet *', shift: float, width: float, hasshift: bool, haswidth: bool) -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.floatspanset_shift_scale(ss_converted, shift, width, hasshift, haswidth) + _check_error() + return result if result != _ffi.NULL else None + + def geoset_round(s: 'const Set *', maxdd: int) -> 'Set *': s_converted = _ffi.cast('const Set *', s) result = _lib.geoset_round(s_converted, maxdd) @@ -1662,36 +1694,23 @@ def intspan_floatspan(s: 'const Span *') -> 'Span *': return result if result != _ffi.NULL else None -def intspanset_floatspanset(ss: 'const SpanSet *') -> 'SpanSet *': - ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.intspanset_floatspanset(ss_converted) - _check_error() - return result if result != _ffi.NULL else None - - -def numset_shift_scale(s: 'const Set *', shift: 'Datum', width: 'Datum', hasshift: bool, haswidth: bool) -> 'Set *': - s_converted = _ffi.cast('const Set *', s) - shift_converted = _ffi.cast('Datum', shift) - width_converted = _ffi.cast('Datum', width) - result = _lib.numset_shift_scale(s_converted, shift_converted, width_converted, hasshift, haswidth) +def intspan_shift_scale(s: 'const Span *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'Span *': + s_converted = _ffi.cast('const Span *', s) + result = _lib.intspan_shift_scale(s_converted, shift, width, hasshift, haswidth) _check_error() return result if result != _ffi.NULL else None -def numspan_shift_scale(s: 'const Span *', shift: 'Datum', width: 'Datum', hasshift: bool, haswidth: bool) -> 'Span *': - s_converted = _ffi.cast('const Span *', s) - shift_converted = _ffi.cast('Datum', shift) - width_converted = _ffi.cast('Datum', width) - result = _lib.numspan_shift_scale(s_converted, shift_converted, width_converted, hasshift, haswidth) +def intspanset_floatspanset(ss: 'const SpanSet *') -> 'SpanSet *': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.intspanset_floatspanset(ss_converted) _check_error() return result if result != _ffi.NULL else None -def numspanset_shift_scale(ss: 'const SpanSet *', shift: 'Datum', width: 'Datum', hasshift: bool, haswidth: bool) -> 'SpanSet *': +def intspanset_shift_scale(ss: 'const SpanSet *', shift: int, width: int, hasshift: bool, haswidth: bool) -> 'SpanSet *': ss_converted = _ffi.cast('const SpanSet *', ss) - shift_converted = _ffi.cast('Datum', shift) - width_converted = _ffi.cast('Datum', width) - result = _lib.numspanset_shift_scale(ss_converted, shift_converted, width_converted, hasshift, haswidth) + result = _lib.intspanset_shift_scale(ss_converted, shift, width, hasshift, haswidth) _check_error() return result if result != _ffi.NULL else None From 4f4726fcca3aa2b0d7fdbdc8f8fbf6e7643ad079 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sat, 30 Sep 2023 15:33:39 +0200 Subject: [PATCH 063/101] Implement abstract methods of intspanset and floatspanset --- pymeos/pymeos/collections/base/span.py | 3 +- pymeos/pymeos/collections/base/spanset.py | 90 ++----- .../pymeos/collections/number/floatspanset.py | 244 +++++++++++++++++- .../pymeos/collections/number/intspanset.py | 244 +++++++++++++++++- pymeos/pymeos/collections/time/periodset.py | 87 +------ .../tests/collections/number/intspan_test.py | 94 +++---- .../tests/collections/time/periodset_test.py | 4 +- 7 files changed, 562 insertions(+), 204 deletions(-) diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index 853f2c9d..26f79149 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -35,7 +35,8 @@ def __init__(self, string: Optional[str] = None, *, upper_inc: Optional[bool] = False, _inner=None): super().__init__() - assert (_inner is not None) or ((string is not None) != (lower is not None and upper is not None)), \ + assert (_inner is not None) or ((string is not None) != \ + (lower is not None and upper is not None)), \ "Either string must be not None or both lower and upper must be not" if _inner is not None: self._inner = _inner diff --git a/pymeos/pymeos/collections/base/spanset.py b/pymeos/pymeos/collections/base/spanset.py index d6b5abc9..88ff0af0 100644 --- a/pymeos/pymeos/collections/base/spanset.py +++ b/pymeos/pymeos/collections/base/spanset.py @@ -9,7 +9,6 @@ from .collection import Collection if TYPE_CHECKING: - from .set import Set from .span import Span T = TypeVar('T') @@ -27,17 +26,17 @@ class SpanSet(Collection[T], ABC): _parse_value_function: Callable[[Union[str, T]], Any] = None # ------------------------- Constructors ---------------------------------- - def __init__(self, string: Optional[str] = None, *, period_list: Optional[List[Union[str, Span]]] = None, + def __init__(self, string: Optional[str] = None, *, span_list: Optional[List[Union[str, Span]]] = None, normalize: bool = True, _inner=None): super().__init__() - assert (_inner is not None) or ((string is not None) != (period_list is not None)), \ - "Either string must be not None or period_list must be not" + assert (_inner is not None) or ((string is not None) != (span_list is not None)), \ + "Either string must be not None or span_list must be not" if _inner is not None: self._inner = _inner elif string is not None: self._inner = self.__class__._parse_function(string) else: - periods = [self.__class__._parse_value_function(p) for p in period_list] + periods = [self.__class__._parse_value_function(p) for p in span_list] self._inner = spanset_make(periods, normalize) def __copy__(self: Self) -> Self: @@ -149,27 +148,6 @@ def to_span(self) -> Span: # ------------------------- Accessors ------------------------------------- - @abstractmethod - def start_element(self) -> T: - raise NotImplementedError() - - @abstractmethod - def end_element(self) -> T: - raise NotImplementedError() - - @abstractmethod - def element_n(self, n: int) -> T: - if n < 0 or n >= self.num_elements(): - raise IndexError(f'Index {n} out of bounds') - - @abstractmethod - def elements(self) -> List[T]: - raise NotImplementedError() - - @abstractmethod - def num_elements(self) -> int: - raise NotImplementedError() - def num_spans(self) -> int: """ Returns the number of spans in ``self``. @@ -258,11 +236,8 @@ def is_adjacent(self, other) -> bool: MEOS Functions: adjacent_spanset_span, adjacent_spanset_spanset """ - from .set import Set from .span import Span - if isinstance(other, Set): - return self.is_adjacent(other.to_spanset()) - elif isinstance(other, Span): + if isinstance(other, Span): return adjacent_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return adjacent_spanset_spanset(self._inner, other._inner) @@ -282,11 +257,8 @@ def is_contained_in(self, container) -> bool: MEOS Functions: contained_spanset_span, contained_spanset_spanset """ - from .set import Set from .span import Span - if isinstance(container, Set): - return self.is_contained_in(container.to_spanset()) - elif isinstance(container, Span): + if isinstance(container, Span): return contained_spanset_span(self._inner, container._inner) elif isinstance(container, SpanSet): return contained_spanset_spanset(self._inner, container._inner) @@ -306,11 +278,8 @@ def contains(self, content) -> bool: MEOS Functions: contains_spanset_span, contains_spanset_spanset, """ - from .set import Set from .span import Span - if isinstance(content, Set): - return contains_spanset_spanset(self._inner, set_to_spanset(content._inner)) - elif isinstance(content, Span): + if isinstance(content, Span): return contains_spanset_span(self._inner, content._inner) elif isinstance(content, SpanSet): return contains_spanset_spanset(self._inner, content._inner) @@ -345,11 +314,8 @@ def overlaps(self, other) -> bool: MEOS Functions: overlaps_spanset_span, overlaps_spanset_spanset """ - from .set import Set from .span import Span - if isinstance(other, Set): - return self.overlaps(other.to_spanset()) - elif isinstance(other, Span): + if isinstance(other, Span): return overlaps_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return overlaps_spanset_spanset(self._inner, other._inner) @@ -385,11 +351,8 @@ def is_left(self, other) -> bool: MEOS Functions: before_periodset_timestamp, left_spanset_span, left_spanset_spanset """ - from .set import Set from .span import Span - if isinstance(other, Set): - return self.is_left(other.to_spanset()) - elif isinstance(other, Span): + if isinstance(other, Span): return left_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return left_spanset_spanset(self._inner, other._inner) @@ -410,11 +373,8 @@ def is_over_or_left(self, other) -> bool: MEOS Functions: overleft_spanset_span, overleft_spanset_spanset """ - from .set import Set from .span import Span - if isinstance(other, Set): - return overleft_spanset_span(self._inner, set_span(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return overleft_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return overleft_spanset_spanset(self._inner, other._inner) @@ -435,11 +395,8 @@ def is_over_or_right(self, other) -> bool: MEOS Functions: overright_spanset_span, overright_spanset_spanset """ - from .set import Set from .span import Span - if isinstance(other, Set): - return self.is_over_or_right(other.to_spanset()) - elif isinstance(other, Span): + if isinstance(other, Span): return overright_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return overright_spanset_spanset(self._inner, other._inner) @@ -459,11 +416,8 @@ def is_right(self, other) -> bool: MEOS Functions: right_spanset_span, right_spanset_spanset """ - from .set import Set from .span import Span - if isinstance(other, Set): - return self.is_right(other.to_spanset()) - elif isinstance(other, Span): + if isinstance(other, Span): return right_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return right_spanset_spanset(self._inner, other._inner) @@ -483,11 +437,8 @@ def distance(self, other) -> float: MEOS Functions: """ - from .set import Set from .span import Span - if isinstance(other, Set): - return distance_spanset_spanset(self._inner, set_to_spanset(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return distance_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return distance_spanset_spanset(self._inner, other._inner) @@ -509,11 +460,8 @@ def intersection(self, other): MEOS Functions: intersection_spanset_spanset, intersection_spanset_span """ - from .set import Set from .span import Span - if isinstance(other, Set): - return intersection_spanset_spanset(self._inner, set_to_spanset(other._inner)) - elif isinstance(other, Span): + if isinstance(other, Span): return intersection_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return intersection_spanset_spanset(self._inner, other._inner) @@ -549,11 +497,8 @@ def minus(self, other): MEOS Functions: minus_spanset_span, minus_spanset_spanset """ - from .set import Set from .span import Span - if isinstance(other, Set): - return self.minus(other.to_spanset()) - elif isinstance(other, Span): + if isinstance(other, Span): return minus_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return minus_spanset_spanset(self._inner, other._inner) @@ -589,11 +534,8 @@ def union(self, other): MEOS Functions: union_periodset_timestamp, union_spanset_spanset, union_spanset_span """ - from .set import Set from .span import Span - if isinstance(other, Set): - return self.union(other.to_spanset()) - elif isinstance(other, Span): + if isinstance(other, Span): return union_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return union_spanset_spanset(self._inner, other._inner) diff --git a/pymeos/pymeos/collections/number/floatspanset.py b/pymeos/pymeos/collections/number/floatspanset.py index f9290d04..69a49a23 100644 --- a/pymeos/pymeos/collections/number/floatspanset.py +++ b/pymeos/pymeos/collections/number/floatspanset.py @@ -1,5 +1,247 @@ +from __future__ import annotations + +from typing import Union, overload, Optional, TYPE_CHECKING + +from pymeos_cffi import floatspanset_in, floatspanset_out + from pymeos.collections import SpanSet +if TYPE_CHECKING: + from .floatspan import FloatSpan class FloatSpanSet(SpanSet[float]): - pass + """ + Class for representing lists of disjoint intspans. + + ``FloatSpanSet`` objects can be created with a single argument of type string + as in MobilityDB. + + >>> FloatSpanSet(string='{[8, 10], [11, 1]}') + + Another possibility is to give a list specifying the composing + spans, which can be instances of ``str`` or ``FloatSpan``. The composing + spans must be given in increasing order. + + >>> FloatSpanSet(span_list=['[8, 10]', '[11, 12]']) + >>> FloatSpanSet(span_list=[FloatSpan('[8, 10]'), FloatSpan('[11, 12]')]) + + """ + + __slots__ = ['_inner'] + + _mobilitydb_name = 'floatspanset' + + _parse_function = floatspanset_in + _parse_value_function = lambda span: floatspanset_in(span)[0] if isinstance(spanset, str) else floatspanset_in._inner[0] + + + # ------------------------- Output ---------------------------------------- + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + floatspanset_out + """ + return floatspanset_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + + def to_span(self) -> FloatSpan: + """ + Returns a span that encompasses ``self``. + + Returns: + A new :class:`FloatSpan` instance + + MEOS Functions: + spanset_span + """ + from .floatspan import FloatSpan + return FloatSpan(_inner=super().to_span()) + + # ------------------------- Accessors ------------------------------------- + def start_span(self) -> FloatSpan: + """ + Returns the first span in ``self``. + Returns: + A :class:`FloatSpan` instance + + MEOS Functions: + spanset_start_span + """ + from .floatspan import FloatSpan + return FloatSpan(_inner=super().start_span()) + + def end_span(self) -> FloatSpan: + """ + Returns the last span in ``self``. + Returns: + A :class:`FloatSpan` instance + + MEOS Functions: + spanset_end_span + """ + from .floatspan import FloatSpan + return FloatSpan(_inner=super().end_span()) + + def span_n(self, n: int) -> FloatSpan: + """ + Returns the n-th span in ``self``. + Returns: + A :class:`FloatSpan` instance + + MEOS Functions: + spanset_span_n + """ + from .floatspan import FloatSpan + return FloatSpan(_inner=super().span_n(n)) + + def spans(self) -> List[FloatSpan]: + """ + Returns the list of spans in ``self``. + Returns: + A :class:`list[FloatSpan]` instance + + MEOS Functions: + spanset_spans + """ + from .floatspan import FloatSpan + ps = super().spans() + return [FloatSpan(_inner=ps[i]) for i in range(self.num_spans())] + + + # ------------------------- Set Operations -------------------------------- + @overload + def intersection(self, other: FloatSpan) -> FloatSpanSet: + ... + + @overload + def intersection(self, other: FloatSpanSet) -> FloatSpanSet: + ... + + @overload + def intersection(self, other: float) -> float: + ... + + def intersection(self, other: Union[float, FloatSpan, FloatSpanSet]) -> Union[float, FloatSpanSet]: + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: object to intersect with + + Returns: + An float or a :class:`FloatSpanSet` instance. The actual class depends + on ``other``. + + MEOS Functions: + intersection_floatspanset_int, intersection_spanset_spanset, + intersection_spanset_span + """ + from .floatspan import FloatSpan + if isinstance(other, float): + result = intersection_floatspanset_int(self._inner, float) + return timestamptz_to_int(result) if result is not None else None + elif isinstance(other, TimestampSet): + result = super().intersection(other) + return TimestampSet(_inner=result) if result is not None else None + elif isinstance(other, FloatSpan): + result = super().intersection(other) + return FloatSpanSet(_inner=result) if result is not None else None + elif isinstance(other, FloatSpanSet): + result = super().intersection(other) + return FloatSpanSet(_inner=result) if result is not None else None + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __mul__(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_floatspanset_int, intersection_spanset_spanset, + intersection_spanset_span + """ + return self.intersection(other) + + def minus(self, other: Time) -> FloatSpanSet: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: object to diff with + + Returns: + A :class:`FloatSpanSet` instance. + + MEOS Functions: + minus_spanset_span, minus_spanset_spanset, minus_floatspanset_int + """ + if isinstance(other, float): + result = minus_floatspanset_int(self._inner, float) + else: + result = super().minus(other) + return FloatSpanSet(_inner=result) if result is not None else None + + def __sub__(self, other): + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: object to diff with + + Returns: + A :class:`FloatSpanSet` instance. + + MEOS Functions: + minus_spanset_span, minus_spanset_spanset, + minus_floatspanset_int + """ + return self.minus(other) + + def union(self, other: Time) -> FloatSpanSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: object to merge with + + Returns: + A :class:`FloatSpanSet` instance. + + MEOS Functions: + union_floatspanset_int, union_spanset_spanset, + union_spanset_span + """ + if isinstance(other, float): + result = union_floatspanset_int(self._inner, float) + else: + result = super().union(other) + return FloatSpanSet(_inner=result) if result is not None else None + + def __add__(self, other): + """ + Returns the union of ``self`` and ``other``. + + Args: + other: object to merge with + + Returns: + A :class:`FloatSpanSet` instance. + + MEOS Functions: + union_floatspanset_int, union_spanset_spanset, + union_spanset_span + """ + return self.union(other) + diff --git a/pymeos/pymeos/collections/number/intspanset.py b/pymeos/pymeos/collections/number/intspanset.py index cbb63a84..30df324a 100644 --- a/pymeos/pymeos/collections/number/intspanset.py +++ b/pymeos/pymeos/collections/number/intspanset.py @@ -1,5 +1,247 @@ +from __future__ import annotations + +from typing import Union, overload, Optional, TYPE_CHECKING + +from pymeos_cffi import intspanset_in, intspanset_out + from pymeos.collections import SpanSet +if TYPE_CHECKING: + from .intspan import IntSpan class IntSpanSet(SpanSet[int]): - pass + """ + Class for representing lists of disjoint intspans. + + ``IntSpanSet`` objects can be created with a single argument of type string + as in MobilityDB. + + >>> IntSpanSet(string='{[8, 10], [11, 1]}') + + Another possibility is to give a list specifying the composing + spans, which can be instances of ``str`` or ``IntSpan``. The composing + spans must be given in increasing order. + + >>> IntSpanSet(span_list=['[8, 10]', '[11, 12]']) + >>> IntSpanSet(span_list=[IntSpan('[8, 10]'), IntSpan('[11, 12]')]) + + """ + + __slots__ = ['_inner'] + + _mobilitydb_name = 'intspanset' + + _parse_function = intspanset_in + _parse_value_function = lambda span: intspanset_in(span)[0] if isinstance(spanset, str) else intspanset_in._inner[0] + + + # ------------------------- Output ---------------------------------------- + def __str__(self): + """ + Return the string representation of the content of ``self``. + + Returns: + A new :class:`str` instance + + MEOS Functions: + intspanset_out + """ + return intspanset_out(self._inner) + + # ------------------------- Conversions ----------------------------------- + + def to_span(self) -> IntSpan: + """ + Returns a span that encompasses ``self``. + + Returns: + A new :class:`IntSpan` instance + + MEOS Functions: + spanset_span + """ + from .intspan import IntSpan + return IntSpan(_inner=super().to_span()) + + # ------------------------- Accessors ------------------------------------- + def start_span(self) -> IntSpan: + """ + Returns the first span in ``self``. + Returns: + A :class:`IntSpan` instance + + MEOS Functions: + spanset_start_span + """ + from .intspan import IntSpan + return IntSpan(_inner=super().start_span()) + + def end_span(self) -> IntSpan: + """ + Returns the last span in ``self``. + Returns: + A :class:`IntSpan` instance + + MEOS Functions: + spanset_end_span + """ + from .intspan import IntSpan + return IntSpan(_inner=super().end_span()) + + def span_n(self, n: int) -> IntSpan: + """ + Returns the n-th span in ``self``. + Returns: + A :class:`IntSpan` instance + + MEOS Functions: + spanset_span_n + """ + from .intspan import IntSpan + return IntSpan(_inner=super().span_n(n)) + + def spans(self) -> List[IntSpan]: + """ + Returns the list of spans in ``self``. + Returns: + A :class:`list[IntSpan]` instance + + MEOS Functions: + spanset_spans + """ + from .intspan import IntSpan + ps = super().spans() + return [IntSpan(_inner=ps[i]) for i in range(self.num_spans())] + + + # ------------------------- Set Operations -------------------------------- + @overload + def intersection(self, other: IntSpan) -> IntSpanSet: + ... + + @overload + def intersection(self, other: IntSpanSet) -> IntSpanSet: + ... + + @overload + def intersection(self, other: int) -> int: + ... + + def intersection(self, other: Union[int, IntSpan, IntSpanSet]) -> Union[int, IntSpanSet]: + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: object to intersect with + + Returns: + An int or a :class:`IntSpanSet` instance. The actual class depends + on ``other``. + + MEOS Functions: + intersection_intspanset_int, intersection_spanset_spanset, + intersection_spanset_span + """ + from .intspan import IntSpan + if isinstance(other, int): + result = intersection_intspanset_int(self._inner, int) + return timestamptz_to_int(result) if result is not None else None + elif isinstance(other, TimestampSet): + result = super().intersection(other) + return TimestampSet(_inner=result) if result is not None else None + elif isinstance(other, IntSpan): + result = super().intersection(other) + return IntSpanSet(_inner=result) if result is not None else None + elif isinstance(other, IntSpanSet): + result = super().intersection(other) + return IntSpanSet(_inner=result) if result is not None else None + else: + raise TypeError(f'Operation not supported with type {other.__class__}') + + def __mul__(self, other): + """ + Returns the intersection of ``self`` and ``other``. + + Args: + other: object to intersect with + + Returns: + A :class:`Time` instance. The actual class depends on ``other``. + + MEOS Functions: + intersection_intspanset_int, intersection_spanset_spanset, + intersection_spanset_span + """ + return self.intersection(other) + + def minus(self, other: Time) -> IntSpanSet: + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: object to diff with + + Returns: + A :class:`IntSpanSet` instance. + + MEOS Functions: + minus_spanset_span, minus_spanset_spanset, minus_intspanset_int + """ + if isinstance(other, int): + result = minus_intspanset_int(self._inner, int) + else: + result = super().minus(other) + return IntSpanSet(_inner=result) if result is not None else None + + def __sub__(self, other): + """ + Returns the difference of ``self`` and ``other``. + + Args: + other: object to diff with + + Returns: + A :class:`IntSpanSet` instance. + + MEOS Functions: + minus_spanset_span, minus_spanset_spanset, + minus_intspanset_int + """ + return self.minus(other) + + def union(self, other: Time) -> IntSpanSet: + """ + Returns the union of ``self`` and ``other``. + + Args: + other: object to merge with + + Returns: + A :class:`IntSpanSet` instance. + + MEOS Functions: + union_intspanset_int, union_spanset_spanset, + union_spanset_span + """ + if isinstance(other, int): + result = union_intspanset_int(self._inner, int) + else: + result = super().union(other) + return IntSpanSet(_inner=result) if result is not None else None + + def __add__(self, other): + """ + Returns the union of ``self`` and ``other``. + + Args: + other: object to merge with + + Returns: + A :class:`IntSpanSet` instance. + + MEOS Functions: + union_intspanset_int, union_spanset_spanset, + union_spanset_span + """ + return self.union(other) + diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py index 54de27db..0779d2e5 100644 --- a/pymeos/pymeos/collections/time/periodset.py +++ b/pymeos/pymeos/collections/time/periodset.py @@ -31,8 +31,8 @@ class PeriodSet(SpanSet[datetime], TimeCollection): periods, which can be instances of ``str`` or ``Period``. The composing periods must be given in increasing order. - >>> PeriodSet(period_list=['[2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01]', '[2019-09-11 00:00:00+01, 2019-09-12 00:00:00+01]']) - >>> PeriodSet(period_list=[Period('[2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01]'), Period('[2019-09-11 00:00:00+01, 2019-09-12 00:00:00+01]')]) + >>> PeriodSet(span_list=['[2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01]', '[2019-09-11 00:00:00+01, 2019-09-12 00:00:00+01]']) + >>> PeriodSet(span_list=[Period('[2019-09-08 00:00:00+01, 2019-09-10 00:00:00+01]'), Period('[2019-09-11 00:00:00+01, 2019-09-12 00:00:00+01]')]) """ @@ -106,17 +106,6 @@ def duration(self, ignore_gaps: Optional[bool] = False) -> timedelta: """ return interval_to_timedelta(periodset_duration(self._inner, ignore_gaps)) - def num_elements(self) -> int: - """ - Returns the number of timestamps in ``self``. - Returns: - An :class:`int` - - MEOS Functions: - periodset_num_timestamps - """ - return periodset_num_timestamps(self._inner) - def num_timestamps(self) -> int: """ Returns the number of timestamps in ``self``. @@ -126,18 +115,7 @@ def num_timestamps(self) -> int: MEOS Functions: periodset_num_timestamps """ - return self.num_elements() - - def start_element(self) -> datetime: - """ - Returns the first timestamp in ``self``. - Returns: - A :class:`datetime` instance - - MEOS Functions: - periodset_start_timestamp - """ - return timestamptz_to_datetime(periodset_start_timestamp(self._inner)) + return periodset_num_timestamps(self._inner) def start_timestamp(self) -> datetime: """ @@ -148,18 +126,7 @@ def start_timestamp(self) -> datetime: MEOS Functions: periodset_start_timestamp """ - return self.start_element() - - def end_element(self) -> datetime: - """ - Returns the last timestamp in ``self``. - Returns: - A :class:`datetime` instance - - MEOS Functions: - periodset_end_timestamp - """ - return timestamptz_to_datetime(periodset_end_timestamp(self._inner)) + return timestamptz_to_datetime(periodset_start_timestamp(self._inner)) def end_timestamp(self) -> datetime: """ @@ -170,19 +137,7 @@ def end_timestamp(self) -> datetime: MEOS Functions: periodset_end_timestamp """ - return self.end_element() - - def element_n(self, n: int) -> datetime: - """ - Returns the n-th timestamp in ``self``. - Returns: - A :class:`datetime` instance - - MEOS Functions: - periodset_timestamp_n - """ - super().element_n(n) - return timestamptz_to_datetime(periodset_timestamp_n(self._inner, n + 1)) + return timestamptz_to_datetime(periodset_end_timestamp(self._inner)) def timestamp_n(self, n: int) -> datetime: """ @@ -193,9 +148,11 @@ def timestamp_n(self, n: int) -> datetime: MEOS Functions: periodset_timestamp_n """ - return self.element_n(n) + if n < 0 or n >= self.num_timestamps(): + raise IndexError(f'Index {n} out of bounds') + return timestamptz_to_datetime(periodset_timestamp_n(self._inner, n + 1)) - def elements(self) -> List[datetime]: + def timestamps(self) -> List[datetime]: """ Returns the list of distinct timestamps in ``self``. Returns: @@ -207,28 +164,6 @@ def elements(self) -> List[datetime]: ts, count = periodset_timestamps(self._inner) return [timestamptz_to_datetime(ts[i]) for i in range(count)] - def timestamps(self) -> List[datetime]: - """ - Returns the list of distinct timestamps in ``self``. - Returns: - A :class:`list[datetime]` instance - - MEOS Functions: - periodset_timestamps - """ - return self.elements() - - def num_spans(self) -> int: - """ - Returns the number of periods in ``self``. - Returns: - An :class:`int` - - MEOS Functions: - spanset_num_spans - """ - return spanset_num_spans(self._inner) - def num_periods(self) -> int: """ Returns the number of periods in ``self``. @@ -764,10 +699,6 @@ def intersection(self, other: PeriodSet) -> PeriodSet: def intersection(self, other: datetime) -> datetime: ... - @overload - def intersection(self, other: TimestampSet) -> TimestampSet: - ... - def intersection(self, other: Time) -> Union[PeriodSet, datetime, TimestampSet]: """ Returns the temporal intersection of ``self`` and ``other``. diff --git a/pymeos/tests/collections/number/intspan_test.py b/pymeos/tests/collections/number/intspan_test.py index fd58b1e4..aa25e794 100644 --- a/pymeos/tests/collections/number/intspan_test.py +++ b/pymeos/tests/collections/number/intspan_test.py @@ -97,13 +97,13 @@ def test_hexwkb(self): assert self.intspan.as_hexwkb() == '010D0001070000000A000000' -# class TestIntSpanConversions(TestIntSpan): +class TestIntSpanConversions(TestIntSpan): - # def test_to_periodset(self): - # intspanset = self.intspan.to_periodset() - # assert isinstance(intspanset, IntSpanSet) - # assert intspanset.num_periods() == 1 - # assert intspanset.start_period() == self.intspan + def test_to_intspanset(self): + intspanset = self.intspan.to_spanset() + assert isinstance(intspanset, IntSpanSet) + assert intspanset.num_spans() == 1 + assert intspanset.start_span() == self.intspan class TestIntSpanAccessors(TestIntSpan): @@ -164,28 +164,28 @@ def test_shift_scale(self): class TestIntSpanTopologicalPositionFunctions(TestIntSpan): value = 5 intspan = IntSpan('(1, 20)') - # intspanset = IntSpanSet('{(1, 20), (31, 41)}') + intspanset = IntSpanSet('{(1, 20), (31, 41)}') @pytest.mark.parametrize( 'other', - [value, intspan], # intspanset], - ids=['value', 'intspan'] #, 'intspanset'] + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] ) def test_is_adjacent(self, other): self.intspan.is_adjacent(other) @pytest.mark.parametrize( 'other', - [intspan], #intspanset], - ids=['intspan'] #, 'intspanset'] + [intspan, intspanset], + ids=['intspan', 'intspanset'] ) def test_is_contained_in(self, other): self.intspan.is_contained_in(other) @pytest.mark.parametrize( 'other', - [value, intspan], # intspanset], - ids=['value', 'intspan'] #, 'intspanset'] + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] ) def test_contains(self, other): self.intspan.contains(other) @@ -193,56 +193,56 @@ def test_contains(self, other): @pytest.mark.parametrize( 'other', - [intspan], # intspanset], - ids=['intspan'] #, 'intspanset'] + [intspan, intspanset], + ids=['intspan', 'intspanset'] ) def test_overlaps(self, other): self.intspan.overlaps(other) @pytest.mark.parametrize( 'other', - [value, intspan], # intspanset], - ids=['value', 'intspan'] #, 'intspanset'] + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] ) def test_is_same(self, other): self.intspan.is_same(other) @pytest.mark.parametrize( 'other', - [value, intspan], # intspanset], - ids=['value', 'intspan'] #, 'intspanset'] + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] ) def test_is_left(self, other): self.intspan.is_left(other) @pytest.mark.parametrize( 'other', - [value, intspan], # intspanset], - ids=['value', 'intspan'] #, 'intspanset'] + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] ) def test_is_over_or_left(self, other): self.intspan.is_over_or_left(other) @pytest.mark.parametrize( 'other', - [value, intspan], # intspanset], - ids=['value', 'intspan'] #, 'intspanset'] + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] ) def test_is_right(self, other): self.intspan.is_right(other) @pytest.mark.parametrize( 'other', - [value, intspan], # intspanset], - ids=['value', 'intspan'] #, 'intspanset'] + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] ) def test_is_over_or_right(self, other): self.intspan.is_over_or_right(other) @pytest.mark.parametrize( 'other', - [value, intspan], # intspanset], - ids=['value', 'intspan'] #, 'intspanset'] + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] ) def test_distance(self, other): self.intspan.distance(other) @@ -251,34 +251,34 @@ def test_distance(self, other): class TestIntSpanSetFunctions(TestIntSpan): value = 1 intspan = IntSpan('(1, 20)') - # intspanset = IntSpanSet('{(1, 20), (31, 41)}') + intspanset = IntSpanSet('{(1, 20), (31, 41)}') @pytest.mark.parametrize( 'other', - [value, intspan], # intspanset], - ids=['value', 'intspan'] #, 'intspanset'] + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] ) def test_intersection(self, other): self.intspan.intersection(other) self.intspan * other - # @pytest.mark.parametrize( - # 'other', - # [value, intspan, intspanset], - # ids=['value', 'intspan', 'intspanset'] - # ) - # def test_union(self, other): - # self.intspan.union(other) - # self.intspan + other - - # @pytest.mark.parametrize( - # 'other', - # [value, intspan, intspanset], - # ids=['value', 'intspan', 'intspanset'] - # ) - # def test_minus(self, other): - # self.intspan.minus(other) - # self.intspan - other + @pytest.mark.parametrize( + 'other', + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] + ) + def test_union(self, other): + self.intspan.union(other) + self.intspan + other + + @pytest.mark.parametrize( + 'other', + [value, intspan, intspanset], + ids=['value', 'intspan', 'intspanset'] + ) + def test_minus(self, other): + self.intspan.minus(other) + self.intspan - other class TestIntSpanComparisons(TestIntSpan): diff --git a/pymeos/tests/collections/time/periodset_test.py b/pymeos/tests/collections/time/periodset_test.py index dc02f5a6..1383a8a1 100644 --- a/pymeos/tests/collections/time/periodset_test.py +++ b/pymeos/tests/collections/time/periodset_test.py @@ -25,8 +25,8 @@ def test_string_constructor(self): Period('[2019-09-03, 2019-09-04]') ]) - def test_period_list_constructor(self): - periodset = PeriodSet(period_list=[ + def test_span_list_constructor(self): + periodset = PeriodSet(span_list=[ Period('[2019-09-01, 2019-09-02]'), Period('[2019-09-03, 2019-09-04]') ]) From c9ae720c88689aaaa6b0f02bfe9252433857d861 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sat, 30 Sep 2023 17:10:32 +0200 Subject: [PATCH 064/101] Improve tests --- pymeos/pymeos/collections/base/spanset.py | 4 +- pymeos/pymeos/collections/number/intset.py | 5 +- pymeos/pymeos/collections/number/intspan.py | 11 +-- .../pymeos/collections/number/intspanset.py | 82 ++++++++++++++++++- pymeos_cffi/pymeos_cffi/builder/meos.h | 2 +- pymeos_cffi/pymeos_cffi/functions.py | 4 +- 6 files changed, 94 insertions(+), 14 deletions(-) diff --git a/pymeos/pymeos/collections/base/spanset.py b/pymeos/pymeos/collections/base/spanset.py index 88ff0af0..15985110 100644 --- a/pymeos/pymeos/collections/base/spanset.py +++ b/pymeos/pymeos/collections/base/spanset.py @@ -36,8 +36,8 @@ def __init__(self, string: Optional[str] = None, *, span_list: Optional[List[Uni elif string is not None: self._inner = self.__class__._parse_function(string) else: - periods = [self.__class__._parse_value_function(p) for p in span_list] - self._inner = spanset_make(periods, normalize) + spans = [self.__class__._parse_value_function(p) for p in span_list] + self._inner = spanset_make(spans, normalize) def __copy__(self: Self) -> Self: """ diff --git a/pymeos/pymeos/collections/number/intset.py b/pymeos/pymeos/collections/number/intset.py index 669e26cb..7a376c13 100644 --- a/pymeos/pymeos/collections/number/intset.py +++ b/pymeos/pymeos/collections/number/intset.py @@ -172,8 +172,9 @@ def scale(self, width: int) -> IntSet: def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSet: """ - Returns a new ``IntSet`` instance with all elements shifted by ``delta`` and scaled to so that the - encompassing span has width ``width``. + Returns a new ``IntSet`` instance with all elements shifted by + ``delta`` and scaled to so that the encompassing span has width + ``width``. Args: delta: The value to shift by. diff --git a/pymeos/pymeos/collections/number/intspan.py b/pymeos/pymeos/collections/number/intspan.py index 4b3ca12f..656b8e5c 100644 --- a/pymeos/pymeos/collections/number/intspan.py +++ b/pymeos/pymeos/collections/number/intspan.py @@ -127,12 +127,13 @@ def shift(self, delta: int) -> IntSpan: """ return self.shift_scale(delta, None) - def scale(self, new_width: int) -> IntSpan: + def scale(self, width: int) -> IntSpan: """ - Return a new ``IntSpan`` with the lower and upper bounds scaled so that the width is ``new_width``. + Return a new ``IntSpan`` with the lower and upper bounds scaled so that + the width is ``width``. Args: - new_width: The new width + width: The new width Returns: A new ``IntSpan`` instance @@ -140,7 +141,7 @@ def scale(self, new_width: int) -> IntSpan: MEOS Functions: intspan_shift_scale """ - return self.shift_scale(None, new_width) + return self.shift_scale(None, width) def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSpan: """ @@ -149,7 +150,7 @@ def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSpan: Args: delta: The value to shift by - new_width: The new width + width: The new width Returns: A new ``IntSpan`` instance diff --git a/pymeos/pymeos/collections/number/intspanset.py b/pymeos/pymeos/collections/number/intspanset.py index 30df324a..8beac104 100644 --- a/pymeos/pymeos/collections/number/intspanset.py +++ b/pymeos/pymeos/collections/number/intspanset.py @@ -2,7 +2,8 @@ from typing import Union, overload, Optional, TYPE_CHECKING -from pymeos_cffi import intspanset_in, intspanset_out +from pymeos_cffi import intspanset_in, intspanset_out, spanset_width, \ + intspanset_shift_scale from pymeos.collections import SpanSet @@ -32,7 +33,7 @@ class IntSpanSet(SpanSet[int]): _mobilitydb_name = 'intspanset' _parse_function = intspanset_in - _parse_value_function = lambda span: intspanset_in(span)[0] if isinstance(spanset, str) else intspanset_in._inner[0] + _parse_value_function = lambda span: intspanset_in(span)[0] if isinstance(span, str) else span._inner[0] # ------------------------- Output ---------------------------------------- @@ -64,6 +65,28 @@ def to_span(self) -> IntSpan: return IntSpan(_inner=super().to_span()) # ------------------------- Accessors ------------------------------------- + + def width(self, ignore_gaps: Optional[bool] = False) -> float: + """ + Returns the width of the spanset. By default, i.e., when the second + argument is False, the function takes into account the gaps within, + i.e., returns the sum of the widths of the spans within. + Otherwise, the function returns the width of the spanset ignoring + any gap, i.e., the width from the lower bound of the first span to + the upper bound of the last span. + + Parameters: + ignore_gaps: Whether to take into account potential gaps in + the spanset. + + Returns: + A `float` representing the duration of the spanset + + MEOS Functions: + spanset_width + """ + return spanset_width(self._inner, ignore_gaps) + def start_span(self) -> IntSpan: """ Returns the first span in ``self``. @@ -114,6 +137,61 @@ def spans(self) -> List[IntSpan]: return [IntSpan(_inner=ps[i]) for i in range(self.num_spans())] + # ------------------------- Transformations ------------------------------- + + def shift(self, delta: int) -> IntSpanSet: + """ + Return a new ``IntSpanSet`` with the lower and upper bounds shifted by + ``delta``. + + Args: + delta: The value to shift by + + Returns: + A new ``IntSpanSet`` instance + + MEOS Functions: + intspanset_shift_scale + """ + return self.shift_scale(delta, None) + + def scale(self, width: int) -> IntSpan: + """ + Return a new ``IntSpanSet`` with the lower and upper bounds scaled so + that the width is ``width``. + + Args: + width: The new width + + Returns: + A new ``IntSpanSet`` instance + + MEOS Functions: + intspanset_shift_scale + """ + return self.shift_scale(None, width) + + def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSpanSet: + """ + Return a new ``IntSpanSet`` with the lower and upper bounds shifted by + ``delta`` and scaled so that the width is ``width``. + + Args: + delta: The value to shift by + width: The new width + + Returns: + A new ``IntSpanSet`` instance + + MEOS Functions: + intspanset_shift_scale + """ + d = delta if delta is not None else 0 + w = width if width is not None else 0 + modified = intspanset_shift_scale(self._inner, d, w, delta is not None, + width is not None) + return IntSpanSet(_inner=modified) + # ------------------------- Set Operations -------------------------------- @overload def intersection(self, other: IntSpan) -> IntSpanSet: diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 1279bbb4..df75b8ee 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -947,7 +947,7 @@ extern Span *spanset_span_n(const SpanSet *ss, int i); extern const Span **spanset_spans(const SpanSet *ss); extern Span *spanset_start_span(const SpanSet *ss); extern bool spanset_upper_inc(const SpanSet *ss); -extern double spanset_width(const SpanSet *ss); +extern double spanset_width(const SpanSet *ss, bool boundspan); extern STBox *spatialset_stbox(const Set *s); extern text *textset_end_value(const Set *s); extern text *textset_start_value(const Set *s); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 4b48c618..b7136c65 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -1512,9 +1512,9 @@ def spanset_upper_inc(ss: 'const SpanSet *') -> 'bool': return result if result != _ffi.NULL else None -def spanset_width(ss: 'const SpanSet *') -> 'double': +def spanset_width(ss: 'const SpanSet *', boundspan: bool) -> 'double': ss_converted = _ffi.cast('const SpanSet *', ss) - result = _lib.spanset_width(ss_converted) + result = _lib.spanset_width(ss_converted, boundspan) _check_error() return result if result != _ffi.NULL else None From b7c2fefcc779209e095f1fd6d2e313379200c020 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sun, 1 Oct 2023 12:59:57 +0200 Subject: [PATCH 065/101] Improve tests --- pymeos/pymeos/collections/base/span.py | 12 +- pymeos/pymeos/collections/base/spanset.py | 9 +- pymeos/pymeos/collections/number/intspan.py | 39 ++-- .../pymeos/collections/number/intspanset.py | 186 ++++++++++++++++-- pymeos_cffi/pymeos_cffi/__init__.py | 24 +++ pymeos_cffi/pymeos_cffi/builder/meos.h | 24 +++ pymeos_cffi/pymeos_cffi/functions.py | 176 +++++++++++++++++ 7 files changed, 425 insertions(+), 45 deletions(-) diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index 26f79149..80d78a56 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -462,13 +462,13 @@ def distance(self, other) -> float: @abstractmethod def intersection(self, other): """ - Returns the temporal intersection of ``self`` and ``other``. + Returns the intersection of ``self`` and ``other``. Args: - other: temporal object to intersect with + other: object to intersect with Returns: - A :class:`Time` instance. The actual class depends on ``other``. + A collection instance. The actual class depends on ``other``. MEOS Functions: intersection_span_span, intersection_spanset_span, @@ -478,13 +478,13 @@ def intersection(self, other): def __mul__(self, other): """ - Returns the temporal intersection of ``self`` and ``other``. + Returns the intersection of ``self`` and ``other``. Args: - other: temporal object to intersect with + other: object to intersect with Returns: - A :class:`Time` instance. The actual class depends on ``other``. + A :class:`Span` instance. The actual class depends on ``other``. MEOS Functions: intersection_span_span, intersection_spanset_span, diff --git a/pymeos/pymeos/collections/base/spanset.py b/pymeos/pymeos/collections/base/spanset.py index 15985110..50651e0f 100644 --- a/pymeos/pymeos/collections/base/spanset.py +++ b/pymeos/pymeos/collections/base/spanset.py @@ -460,13 +460,7 @@ def intersection(self, other): MEOS Functions: intersection_spanset_spanset, intersection_spanset_span """ - from .span import Span - if isinstance(other, Span): - return intersection_spanset_span(self._inner, other._inner) - elif isinstance(other, SpanSet): - return intersection_spanset_spanset(self._inner, other._inner) - else: - raise TypeError(f'Operation not supported with type {other.__class__}') + raise TypeError(f'Operation not supported with type {other.__class__}') def __mul__(self, other): """ @@ -539,7 +533,6 @@ def union(self, other): return union_spanset_span(self._inner, other._inner) elif isinstance(other, SpanSet): return union_spanset_spanset(self._inner, other._inner) - else: raise TypeError(f'Operation not supported with type {other.__class__}') diff --git a/pymeos/pymeos/collections/number/intspan.py b/pymeos/pymeos/collections/number/intspan.py index 656b8e5c..679854c3 100644 --- a/pymeos/pymeos/collections/number/intspan.py +++ b/pymeos/pymeos/collections/number/intspan.py @@ -2,10 +2,9 @@ from typing import Union, overload, Optional, TYPE_CHECKING -from pymeos_cffi import intersection_intset_int, distance_intset_int, \ - intspan_in, intspan_lower, intspan_upper, intspan_shift_scale, \ - contains_intspan_int, adjacent_intspan_int, span_width, \ - int_to_intspan, span_eq, left_intspan_int,\ +from pymeos_cffi import intspan_in, intspan_lower, intspan_upper, \ + intspan_shift_scale, contains_intspan_int, adjacent_intspan_int, \ + span_width, int_to_intspan, span_eq, left_intspan_int,\ overleft_intspan_int, right_intspan_int, overright_intspan_int, \ intersection_intspan_int, intersection_span_span, intersection_spanset_span, \ minus_intspan_int, minus_span_span, minus_spanset_span, union_intspan_int, \ @@ -178,7 +177,7 @@ def is_adjacent(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: True if adjacent, False otherwise MEOS Functions: - adjacent_span_span, adjacent_span_spanset, adjacent_intset_int + adjacent_span_span, adjacent_span_spanset, adjacent_intspan_int """ if isinstance(other, int): return adjacent_intspan_int(self._inner, other) @@ -196,7 +195,7 @@ def contains(self, content: Union[int, IntSpan, IntSpanSet]) -> bool: True if contains, False otherwise MEOS Functions: - contains_set_set, contains_intset_int + contains_span_span, contains_intspan_int """ if isinstance(content, int): return contains_intspan_int(self._inner, content) @@ -225,13 +224,14 @@ def is_same(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: # ------------------------- Position Operations --------------------------- def is_left(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ - Returns whether ``self`` is strictly before ``other``. That is, ``self`` ends before ``other`` starts. + Returns whether ``self`` is strictly left ``other``. That is, + ``self`` ends before ``other`` starts. Args: other: object to compare with Returns: - True if before, False otherwise + True if left, False otherwise MEOS Functions: left_span_span, left_span_spanset, left_intspan_int @@ -243,14 +243,14 @@ def is_left(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: def is_over_or_left(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ - Returns whether ``self`` is before ``other`` allowing overlap. That is, + Returns whether ``self`` is left ``other`` allowing overlap. That is, ``self`` ends before ``other`` ends (or at the same value). Args: other: object to compare with Returns: - True if before, False otherwise + True if over or left, False otherwise MEOS Functions: overleft_span_span, overleft_span_spanset, overleft_intspan_int @@ -262,13 +262,14 @@ def is_over_or_left(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: def is_right(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ - Returns whether ``self`` is strictly after ``other``. That is, ``self`` starts after ``other`` ends. + Returns whether ``self`` is strictly right ``other``. That is, ``self`` + starts after ``other`` ends. Args: other: object to compare with Returns: - True if after, False otherwise + True if right, False otherwise MEOS Functions: right_span_span, right_span_spanset, right_intspan_int @@ -280,7 +281,7 @@ def is_right(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: def is_over_or_right(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: """ - Returns whether ``self`` is after ``other`` allowing overlap. That is, + Returns whether ``self`` is right ``other`` allowing overlap. That is, ``self`` starts after ``other`` starts (or at the same value). Args: @@ -309,7 +310,7 @@ def distance(self, other: Union[int, IntSpan, IntSpanSet]) -> float: A float value MEOS Functions: - distance_span_span, distance_span_spanset, distance_intset_int, + distance_span_span, distance_span_spanset, distance_intspan_int, """ if isinstance(other, int): return distance_intspan_int(self._inner, other) @@ -337,10 +338,12 @@ def intersection(self, other): other: object to intersect with Returns: - A :class:`Collection[int]` instance. The actual class depends on ``other``. + A :class:`Collection[int]` instance. The actual class depends on + ``other``. MEOS Functions: - intersection_span_span, intersection_spanset_span, intersection_intset_int + intersection_span_span, intersection_spanset_span, + intersection_intspan_int """ from .intspanset import IntSpanSet if isinstance(other, int): @@ -365,7 +368,7 @@ def minus(self, other: Union[int, IntSpan, IntSpanSet]) -> IntSpanSet: A :class:`IntSpanSet` instance. MEOS Functions: - minus_span_span, minus_spanset_span, minus_intset_int + minus_span_span, minus_spanset_span, minus_intspan_int """ from .intspanset import IntSpanSet if isinstance(other, int): @@ -389,7 +392,7 @@ def union(self, other: Union[int, IntSpan, IntSpanSet]) -> IntSpanSet: A :class:`PeriodSet` instance. MEOS Functions: - union_spanset_span, union_span_span, union_intset_int + union_spanset_span, union_span_span, union_intspan_int """ from .intspanset import IntSpanSet if isinstance(other, int): diff --git a/pymeos/pymeos/collections/number/intspanset.py b/pymeos/pymeos/collections/number/intspanset.py index 8beac104..ce59c928 100644 --- a/pymeos/pymeos/collections/number/intspanset.py +++ b/pymeos/pymeos/collections/number/intspanset.py @@ -3,7 +3,13 @@ from typing import Union, overload, Optional, TYPE_CHECKING from pymeos_cffi import intspanset_in, intspanset_out, spanset_width, \ - intspanset_shift_scale + intspanset_shift_scale, adjacent_intspanset_int, contains_intspanset_int, \ + spanset_eq, int_to_intspanset, left_intspanset_int, \ + overleft_intspanset_int, right_intspanset_int, overright_intspanset_int, \ + distance_intspanset_int, intersection_intspanset_int, \ + intersection_spanset_span, intersection_spanset_spanset, \ + union_intspanset_int, union_spanset_span, union_spanset_spanset, \ + minus_intspanset_int, minus_spanset_span, minus_spanset_spanset from pymeos.collections import SpanSet @@ -192,20 +198,178 @@ def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSpanSet: width is not None) return IntSpanSet(_inner=modified) + # ------------------------- Topological Operations -------------------------------- + + def is_adjacent(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is adjacent to ``other``. That is, they share + a bound but only one of them contains it. + + Args: + other: object to compare with + + Returns: + True if adjacent, False otherwise + + MEOS Functions: + adjacent_spanset_span, adjacent_spanset_spanset, + adjacent_intspanset_int + """ + if isinstance(other, int): + return adjacent_intspanset_int(self._inner, other) + else: + return super().is_adjacent(other) + + def contains(self, content: Union[int, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_spanset_span, contains_spanset_spanset, + contains_intspanset_int + """ + if isinstance(content, int): + return contains_intspanset_int(self._inner, content) + else: + return super().contains(content) + + def is_same(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` and the bounding period of ``other`` is the + same. + + Args: + other: object to compare with + + Returns: + True if equal, False otherwise + + MEOS Functions: + same_period_temporal + """ + if isinstance(other, int): + return spanset_eq(self._inner, int_to_intspanset(other)) + else: + return super().is_same(other) + + # ------------------------- Position Operations --------------------------- + def is_left(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is strictly left of ``other``. That is, + ``self`` ends before ``other`` starts. + + Args: + other: object to compare with + + Returns: + True if left, False otherwise + + MEOS Functions: + left_span_span, left_span_spanset, left_intspan_int + """ + if isinstance(other, int): + return left_intspanset_int(self._inner, other) + else: + return super().is_left(other) + + def is_over_or_left(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is left ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same value). + + Args: + other: object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overleft_span_span, overleft_span_spanset, overleft_intspan_int + """ + if isinstance(other, int): + return overleft_intspanset_int(self._inner, other) + else: + return super().is_over_or_left(other) + + def is_right(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is strictly right ``other``. That is, ``self`` + starts after ``other`` ends. + + Args: + other: object to compare with + + Returns: + True if right, False otherwise + + MEOS Functions: + right_span_span, right_span_spanset, right_intspan_int + """ + if isinstance(other, int): + return right_intspanset_int(self._inner, other) + else: + return super().is_right(other) + + def is_over_or_right(self, other: Union[int, IntSpan, IntSpanSet]) -> bool: + """ + Returns whether ``self`` is right ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same value). + + Args: + other: object to compare with + + Returns: + True if overlapping or after, False otherwise + + MEOS Functions: + overright_spanset_span, overright_spanset_spanset, + overright_intspanset_int + """ + if isinstance(other, int): + return overright_intspanset_int(self._inner, other) + else: + return super().is_over_or_right(other) + + # ------------------------- Distance Operations --------------------------- + def distance(self, other: Union[int, IntSpan, IntSpanSet]) -> float: + """ + Returns the distance between ``self`` and ``other``. + + Args: + other: object to compare with + + Returns: + A float value + + MEOS Functions: + distance_spanset_span, distance_spanset_spanset, + distance_intspanset_int + """ + if isinstance(other, int): + return distance_intspanset_int(self._inner, other) + else: + return super().distance(other) + # ------------------------- Set Operations -------------------------------- @overload - def intersection(self, other: IntSpan) -> IntSpanSet: + def intersection(self, other: int) -> Optional[int]: ... @overload - def intersection(self, other: IntSpanSet) -> IntSpanSet: + def intersection(self, other: IntSpan) -> Optional[IntSpanSet]: ... @overload - def intersection(self, other: int) -> int: + def intersection(self, other: IntSpanSet) -> Optional[IntSpanSet]: ... - def intersection(self, other: Union[int, IntSpan, IntSpanSet]) -> Union[int, IntSpanSet]: + def intersection(self, other): """ Returns the intersection of ``self`` and ``other``. @@ -222,19 +386,15 @@ def intersection(self, other: Union[int, IntSpan, IntSpanSet]) -> Union[int, Int """ from .intspan import IntSpan if isinstance(other, int): - result = intersection_intspanset_int(self._inner, int) - return timestamptz_to_int(result) if result is not None else None - elif isinstance(other, TimestampSet): - result = super().intersection(other) - return TimestampSet(_inner=result) if result is not None else None + return intersection_intspanset_int(self._inner, int) elif isinstance(other, IntSpan): - result = super().intersection(other) + result = intersection_spanset_span(self._inner, other._inner) return IntSpanSet(_inner=result) if result is not None else None elif isinstance(other, IntSpanSet): - result = super().intersection(other) + result = intersection_spanset_spanset(self._inner, other._inner) return IntSpanSet(_inner=result) if result is not None else None else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().intersection(other) def __mul__(self, other): """ diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 299574f0..88cc9c9a 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -400,16 +400,22 @@ 'before_timestampset_timestamp', 'left_bigint_bigintset', 'left_bigint_bigintspan', + 'left_bigint_bigintspanset', 'left_bigintset_bigint', 'left_bigintspan_bigint', + 'left_bigintspanset_bigint', 'left_float_floatset', 'left_float_floatspan', + 'left_float_floatspanset', 'left_floatset_float', 'left_floatspan_float', + 'left_floatspanset_float', 'left_int_intset', 'left_int_intspan', + 'left_int_intspanset', 'left_intset_int', 'left_intspan_int', + 'left_intspanset_int', 'left_set_set', 'left_span_span', 'left_span_spanset', @@ -431,16 +437,22 @@ 'overbefore_timestampset_timestamp', 'overleft_bigint_bigintset', 'overleft_bigint_bigintspan', + 'overleft_bigint_bigintspanset', 'overleft_bigintset_bigint', 'overleft_bigintspan_bigint', + 'overleft_bigintspanset_bigint', 'overleft_float_floatset', 'overleft_float_floatspan', + 'overleft_float_floatspanset', 'overleft_floatset_float', 'overleft_floatspan_float', + 'overleft_floatspanset_float', 'overleft_int_intset', 'overleft_int_intspan', + 'overleft_int_intspanset', 'overleft_intset_int', 'overleft_intspan_int', + 'overleft_intspanset_int', 'overleft_set_set', 'overleft_span_span', 'overleft_span_spanset', @@ -450,16 +462,22 @@ 'overleft_textset_text', 'overright_bigint_bigintset', 'overright_bigint_bigintspan', + 'overright_bigint_bigintspanset', 'overright_bigintset_bigint', 'overright_bigintspan_bigint', + 'overright_bigintspanset_bigint', 'overright_float_floatset', 'overright_float_floatspan', + 'overright_float_floatspanset', 'overright_floatset_float', 'overright_floatspan_float', + 'overright_floatspanset_float', 'overright_int_intset', 'overright_int_intspan', + 'overright_int_intspanset', 'overright_intset_int', 'overright_intspan_int', + 'overright_intspanset_int', 'overright_set_set', 'overright_span_span', 'overright_span_spanset', @@ -469,16 +487,22 @@ 'overright_textset_text', 'right_bigint_bigintset', 'right_bigint_bigintspan', + 'right_bigint_bigintspanset', 'right_bigintset_bigint', 'right_bigintspan_bigint', + 'right_bigintspanset_bigint', 'right_float_floatset', 'right_float_floatspan', + 'right_float_floatspanset', 'right_floatset_float', 'right_floatspan_float', + 'right_floatspanset_float', 'right_int_intset', 'right_int_intspan', + 'right_int_intspanset', 'right_intset_int', 'right_intspan_int', + 'right_intspanset_int', 'right_set_set', 'right_span_span', 'right_span_spanset', diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index df75b8ee..3ec71d54 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -1138,16 +1138,22 @@ extern bool before_timestamp_timestampset(TimestampTz t, const Set *ts); extern bool before_timestampset_timestamp(const Set *s, TimestampTz t); extern bool left_bigint_bigintset(int64 i, const Set *s); extern bool left_bigint_bigintspan(int64 i, const Span *s); +extern bool left_bigint_bigintspanset(int64 i, const SpanSet *ss); extern bool left_bigintset_bigint(const Set *s, int64 i); extern bool left_bigintspan_bigint(const Span *s, int64 i); +extern bool left_bigintspanset_bigint(const SpanSet *ss, int64 i); extern bool left_float_floatset(double d, const Set *s); extern bool left_float_floatspan(double d, const Span *s); +extern bool left_float_floatspanset(double d, const SpanSet *ss); extern bool left_floatset_float(const Set *s, double d); extern bool left_floatspan_float(const Span *s, double d); +extern bool left_floatspanset_float(const SpanSet *ss, double d); extern bool left_int_intset(int i, const Set *s); extern bool left_int_intspan(int i, const Span *s); +extern bool left_int_intspanset(int i, const SpanSet *ss); extern bool left_intset_int(const Set *s, int i); extern bool left_intspan_int(const Span *s, int i); +extern bool left_intspanset_int(const SpanSet *ss, int i); extern bool left_set_set(const Set *s1, const Set *s2); extern bool left_span_span(const Span *s1, const Span *s2); extern bool left_span_spanset(const Span *s, const SpanSet *ss); @@ -1169,16 +1175,22 @@ extern bool overbefore_timestamp_timestampset(TimestampTz t, const Set *ts); extern bool overbefore_timestampset_timestamp(const Set *s, TimestampTz t); extern bool overleft_bigint_bigintset(int64 i, const Set *s); extern bool overleft_bigint_bigintspan(int64 i, const Span *s); +extern bool overleft_bigint_bigintspanset(int64 i, const SpanSet *ss); extern bool overleft_bigintset_bigint(const Set *s, int64 i); extern bool overleft_bigintspan_bigint(const Span *s, int64 i); +extern bool overleft_bigintspanset_bigint(const SpanSet *ss, int64 i); extern bool overleft_float_floatset(double d, const Set *s); extern bool overleft_float_floatspan(double d, const Span *s); +extern bool overleft_float_floatspanset(double d, const SpanSet *ss); extern bool overleft_floatset_float(const Set *s, double d); extern bool overleft_floatspan_float(const Span *s, double d); +extern bool overleft_floatspanset_float(const SpanSet *ss, double d); extern bool overleft_int_intset(int i, const Set *s); extern bool overleft_int_intspan(int i, const Span *s); +extern bool overleft_int_intspanset(int i, const SpanSet *ss); extern bool overleft_intset_int(const Set *s, int i); extern bool overleft_intspan_int(const Span *s, int i); +extern bool overleft_intspanset_int(const SpanSet *ss, int i); extern bool overleft_set_set(const Set *s1, const Set *s2); extern bool overleft_span_span(const Span *s1, const Span *s2); extern bool overleft_span_spanset(const Span *s, const SpanSet *ss); @@ -1188,16 +1200,22 @@ extern bool overleft_text_textset(text *txt, const Set *s); extern bool overleft_textset_text(const Set *s, text *txt); extern bool overright_bigint_bigintset(int64 i, const Set *s); extern bool overright_bigint_bigintspan(int64 i, const Span *s); +extern bool overright_bigint_bigintspanset(int64 i, const SpanSet *ss); extern bool overright_bigintset_bigint(const Set *s, int64 i); extern bool overright_bigintspan_bigint(const Span *s, int64 i); +extern bool overright_bigintspanset_bigint(const SpanSet *ss, int64 i); extern bool overright_float_floatset(double d, const Set *s); extern bool overright_float_floatspan(double d, const Span *s); +extern bool overright_float_floatspanset(double d, const SpanSet *ss); extern bool overright_floatset_float(const Set *s, double d); extern bool overright_floatspan_float(const Span *s, double d); +extern bool overright_floatspanset_float(const SpanSet *ss, double d); extern bool overright_int_intset(int i, const Set *s); extern bool overright_int_intspan(int i, const Span *s); +extern bool overright_int_intspanset(int i, const SpanSet *ss); extern bool overright_intset_int(const Set *s, int i); extern bool overright_intspan_int(const Span *s, int i); +extern bool overright_intspanset_int(const SpanSet *ss, int i); extern bool overright_set_set(const Set *s1, const Set *s2); extern bool overright_span_span(const Span *s1, const Span *s2); extern bool overright_span_spanset(const Span *s, const SpanSet *ss); @@ -1207,16 +1225,22 @@ extern bool overright_text_textset(text *txt, const Set *s); extern bool overright_textset_text(const Set *s, text *txt); extern bool right_bigint_bigintset(int64 i, const Set *s); extern bool right_bigint_bigintspan(int64 i, const Span *s); +extern bool right_bigint_bigintspanset(int64 i, const SpanSet *ss); extern bool right_bigintset_bigint(const Set *s, int64 i); extern bool right_bigintspan_bigint(const Span *s, int64 i); +extern bool right_bigintspanset_bigint(const SpanSet *ss, int64 i); extern bool right_float_floatset(double d, const Set *s); extern bool right_float_floatspan(double d, const Span *s); +extern bool right_float_floatspanset(double d, const SpanSet *ss); extern bool right_floatset_float(const Set *s, double d); extern bool right_floatspan_float(const Span *s, double d); +extern bool right_floatspanset_float(const SpanSet *ss, double d); extern bool right_int_intset(int i, const Set *s); extern bool right_int_intspan(int i, const Span *s); +extern bool right_int_intspanset(int i, const SpanSet *ss); extern bool right_intset_int(const Set *s, int i); extern bool right_intspan_int(const Span *s, int i); +extern bool right_intspanset_int(const SpanSet *ss, int i); extern bool right_set_set(const Set *s1, const Set *s2); extern bool right_span_span(const Span *s1, const Span *s2); extern bool right_span_spanset(const Span *s, const SpanSet *ss); diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index b7136c65..2bdad684 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -2899,6 +2899,14 @@ def left_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def left_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'bool': + i_converted = _ffi.cast('int64', i) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.left_bigint_bigintspanset(i_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def left_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) i_converted = _ffi.cast('int64', i) @@ -2915,6 +2923,14 @@ def left_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': return result if result != _ffi.NULL else None +def left_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.left_bigintspanset_bigint(ss_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + def left_float_floatset(d: float, s: 'const Set *') -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.left_float_floatset(d, s_converted) @@ -2929,6 +2945,13 @@ def left_float_floatspan(d: float, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def left_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.left_float_floatspanset(d, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def left_floatset_float(s: 'const Set *', d: float) -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.left_floatset_float(s_converted, d) @@ -2943,6 +2966,13 @@ def left_floatspan_float(s: 'const Span *', d: float) -> 'bool': return result if result != _ffi.NULL else None +def left_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.left_floatspanset_float(ss_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + def left_int_intset(i: int, s: 'const Set *') -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.left_int_intset(i, s_converted) @@ -2957,6 +2987,13 @@ def left_int_intspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def left_int_intspanset(i: int, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.left_int_intspanset(i, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def left_intset_int(s: 'const Set *', i: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.left_intset_int(s_converted, i) @@ -2971,6 +3008,13 @@ def left_intspan_int(s: 'const Span *', i: int) -> 'bool': return result if result != _ffi.NULL else None +def left_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.left_intspanset_int(ss_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + def left_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) @@ -3139,6 +3183,14 @@ def overleft_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def overleft_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'bool': + i_converted = _ffi.cast('int64', i) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overleft_bigint_bigintspanset(i_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overleft_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) i_converted = _ffi.cast('int64', i) @@ -3155,6 +3207,14 @@ def overleft_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': return result if result != _ffi.NULL else None +def overleft_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.overleft_bigintspanset_bigint(ss_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overleft_float_floatset(d: float, s: 'const Set *') -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.overleft_float_floatset(d, s_converted) @@ -3169,6 +3229,13 @@ def overleft_float_floatspan(d: float, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def overleft_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overleft_float_floatspanset(d, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overleft_floatset_float(s: 'const Set *', d: float) -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.overleft_floatset_float(s_converted, d) @@ -3183,6 +3250,13 @@ def overleft_floatspan_float(s: 'const Span *', d: float) -> 'bool': return result if result != _ffi.NULL else None +def overleft_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overleft_floatspanset_float(ss_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + def overleft_int_intset(i: int, s: 'const Set *') -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.overleft_int_intset(i, s_converted) @@ -3197,6 +3271,13 @@ def overleft_int_intspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def overleft_int_intspanset(i: int, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overleft_int_intspanset(i, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overleft_intset_int(s: 'const Set *', i: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.overleft_intset_int(s_converted, i) @@ -3211,6 +3292,13 @@ def overleft_intspan_int(s: 'const Span *', i: int) -> 'bool': return result if result != _ffi.NULL else None +def overleft_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overleft_intspanset_int(ss_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + def overleft_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) @@ -3283,6 +3371,14 @@ def overright_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def overright_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'bool': + i_converted = _ffi.cast('int64', i) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overright_bigint_bigintspanset(i_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overright_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) i_converted = _ffi.cast('int64', i) @@ -3299,6 +3395,14 @@ def overright_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': return result if result != _ffi.NULL else None +def overright_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.overright_bigintspanset_bigint(ss_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overright_float_floatset(d: float, s: 'const Set *') -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.overright_float_floatset(d, s_converted) @@ -3313,6 +3417,13 @@ def overright_float_floatspan(d: float, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def overright_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overright_float_floatspanset(d, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overright_floatset_float(s: 'const Set *', d: float) -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.overright_floatset_float(s_converted, d) @@ -3327,6 +3438,13 @@ def overright_floatspan_float(s: 'const Span *', d: float) -> 'bool': return result if result != _ffi.NULL else None +def overright_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overright_floatspanset_float(ss_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + def overright_int_intset(i: int, s: 'const Set *') -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.overright_int_intset(i, s_converted) @@ -3341,6 +3459,13 @@ def overright_int_intspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def overright_int_intspanset(i: int, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overright_int_intspanset(i, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def overright_intset_int(s: 'const Set *', i: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.overright_intset_int(s_converted, i) @@ -3355,6 +3480,13 @@ def overright_intspan_int(s: 'const Span *', i: int) -> 'bool': return result if result != _ffi.NULL else None +def overright_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.overright_intspanset_int(ss_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + def overright_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) @@ -3427,6 +3559,14 @@ def right_bigint_bigintspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def right_bigint_bigintspanset(i: int, ss: 'const SpanSet *') -> 'bool': + i_converted = _ffi.cast('int64', i) + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.right_bigint_bigintspanset(i_converted, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def right_bigintset_bigint(s: 'const Set *', i: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) i_converted = _ffi.cast('int64', i) @@ -3443,6 +3583,14 @@ def right_bigintspan_bigint(s: 'const Span *', i: int) -> 'bool': return result if result != _ffi.NULL else None +def right_bigintspanset_bigint(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + i_converted = _ffi.cast('int64', i) + result = _lib.right_bigintspanset_bigint(ss_converted, i_converted) + _check_error() + return result if result != _ffi.NULL else None + + def right_float_floatset(d: float, s: 'const Set *') -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.right_float_floatset(d, s_converted) @@ -3457,6 +3605,13 @@ def right_float_floatspan(d: float, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def right_float_floatspanset(d: float, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.right_float_floatspanset(d, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def right_floatset_float(s: 'const Set *', d: float) -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.right_floatset_float(s_converted, d) @@ -3471,6 +3626,13 @@ def right_floatspan_float(s: 'const Span *', d: float) -> 'bool': return result if result != _ffi.NULL else None +def right_floatspanset_float(ss: 'const SpanSet *', d: float) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.right_floatspanset_float(ss_converted, d) + _check_error() + return result if result != _ffi.NULL else None + + def right_int_intset(i: int, s: 'const Set *') -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.right_int_intset(i, s_converted) @@ -3485,6 +3647,13 @@ def right_int_intspan(i: int, s: 'const Span *') -> 'bool': return result if result != _ffi.NULL else None +def right_int_intspanset(i: int, ss: 'const SpanSet *') -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.right_int_intspanset(i, ss_converted) + _check_error() + return result if result != _ffi.NULL else None + + def right_intset_int(s: 'const Set *', i: int) -> 'bool': s_converted = _ffi.cast('const Set *', s) result = _lib.right_intset_int(s_converted, i) @@ -3499,6 +3668,13 @@ def right_intspan_int(s: 'const Span *', i: int) -> 'bool': return result if result != _ffi.NULL else None +def right_intspanset_int(ss: 'const SpanSet *', i: int) -> 'bool': + ss_converted = _ffi.cast('const SpanSet *', ss) + result = _lib.right_intspanset_int(ss_converted, i) + _check_error() + return result if result != _ffi.NULL else None + + def right_set_set(s1: 'const Set *', s2: 'const Set *') -> 'bool': s1_converted = _ffi.cast('const Set *', s1) s2_converted = _ffi.cast('const Set *', s2) From c04c098221243522449a1672e80c932e500459da Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sun, 1 Oct 2023 13:37:53 +0200 Subject: [PATCH 066/101] Improve tests --- .../pymeos/collections/number/floatspanset.py | 292 ++++++++++++++++-- pymeos/pymeos/collections/time/periodset.py | 4 +- 2 files changed, 267 insertions(+), 29 deletions(-) diff --git a/pymeos/pymeos/collections/number/floatspanset.py b/pymeos/pymeos/collections/number/floatspanset.py index 69a49a23..ac19386d 100644 --- a/pymeos/pymeos/collections/number/floatspanset.py +++ b/pymeos/pymeos/collections/number/floatspanset.py @@ -2,7 +2,14 @@ from typing import Union, overload, Optional, TYPE_CHECKING -from pymeos_cffi import floatspanset_in, floatspanset_out +from pymeos_cffi import floatspanset_in, floatspanset_out, spanset_width, \ + floatspanset_shift_scale, adjacent_floatspanset_float, contains_floatspanset_float, \ + spanset_eq, float_to_floatspanset, left_floatspanset_float, \ + overleft_floatspanset_float, right_floatspanset_float, overright_floatspanset_float, \ + distance_floatspanset_float, intersection_floatspanset_float, \ + intersection_spanset_span, intersection_spanset_spanset, \ + union_floatspanset_float, union_spanset_span, union_spanset_spanset, \ + minus_floatspanset_float, minus_spanset_span, minus_spanset_spanset from pymeos.collections import SpanSet @@ -11,7 +18,7 @@ class FloatSpanSet(SpanSet[float]): """ - Class for representing lists of disjoint intspans. + Class for representing lists of disjoint floatspans. ``FloatSpanSet`` objects can be created with a single argument of type string as in MobilityDB. @@ -32,11 +39,11 @@ class FloatSpanSet(SpanSet[float]): _mobilitydb_name = 'floatspanset' _parse_function = floatspanset_in - _parse_value_function = lambda span: floatspanset_in(span)[0] if isinstance(spanset, str) else floatspanset_in._inner[0] + _parse_value_function = lambda span: floatspanset_in(span)[0] if isinstance(span, str) else span._inner[0] # ------------------------- Output ---------------------------------------- - def __str__(self): + def __str__(self, max_decimals: int = 15): """ Return the string representation of the content of ``self``. @@ -46,7 +53,7 @@ def __str__(self): MEOS Functions: floatspanset_out """ - return floatspanset_out(self._inner) + return floatspanset_out(self._inner, max_decimals) # ------------------------- Conversions ----------------------------------- @@ -64,6 +71,28 @@ def to_span(self) -> FloatSpan: return FloatSpan(_inner=super().to_span()) # ------------------------- Accessors ------------------------------------- + + def width(self, ignore_gaps: Optional[bool] = False) -> float: + """ + Returns the width of the spanset. By default, i.e., when the second + argument is False, the function takes into account the gaps within, + i.e., returns the sum of the widths of the spans within. + Otherwise, the function returns the width of the spanset ignoring + any gap, i.e., the width from the lower bound of the first span to + the upper bound of the last span. + + Parameters: + ignore_gaps: Whether to take into account potential gaps in + the spanset. + + Returns: + A `float` representing the duration of the spanset + + MEOS Functions: + spanset_width + """ + return spanset_width(self._inner, ignore_gaps) + def start_span(self) -> FloatSpan: """ Returns the first span in ``self``. @@ -114,20 +143,233 @@ def spans(self) -> List[FloatSpan]: return [FloatSpan(_inner=ps[i]) for i in range(self.num_spans())] + # ------------------------- Transformations ------------------------------- + + def shift(self, delta: int) -> FloatSpanSet: + """ + Return a new ``FloatSpanSet`` with the lower and upper bounds shifted by + ``delta``. + + Args: + delta: The value to shift by + + Returns: + A new ``FloatSpanSet`` instance + + MEOS Functions: + floatspanset_shift_scale + """ + return self.shift_scale(delta, None) + + def scale(self, width: int) -> FloatSpan: + """ + Return a new ``FloatSpanSet`` with the lower and upper bounds scaled so + that the width is ``width``. + + Args: + width: The new width + + Returns: + A new ``FloatSpanSet`` instance + + MEOS Functions: + floatspanset_shift_scale + """ + return self.shift_scale(None, width) + + def shift_scale(self, delta: Optional[int], width: Optional[int]) -> FloatSpanSet: + """ + Return a new ``FloatSpanSet`` with the lower and upper bounds shifted by + ``delta`` and scaled so that the width is ``width``. + + Args: + delta: The value to shift by + width: The new width + + Returns: + A new ``FloatSpanSet`` instance + + MEOS Functions: + floatspanset_shift_scale + """ + d = delta if delta is not None else 0 + w = width if width is not None else 0 + modified = floatspanset_shift_scale(self._inner, d, w, delta is not None, + width is not None) + return FloatSpanSet(_inner=modified) + + # ------------------------- Topological Operations -------------------------------- + + def is_adjacent(self, other: Union[int, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is adjacent to ``other``. That is, they share + a bound but only one of them contains it. + + Args: + other: object to compare with + + Returns: + True if adjacent, False otherwise + + MEOS Functions: + adjacent_spanset_span, adjacent_spanset_spanset, + adjacent_floatspanset_float + """ + if isinstance(other, int): + return adjacent_floatspanset_float(self._inner, other) + else: + return super().is_adjacent(other) + + def contains(self, content: Union[int, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` contains ``content``. + + Args: + content: object to compare with + + Returns: + True if contains, False otherwise + + MEOS Functions: + contains_spanset_span, contains_spanset_spanset, + contains_floatspanset_float + """ + if isinstance(content, int): + return contains_floatspanset_float(self._inner, content) + else: + return super().contains(content) + + def is_same(self, other: Union[int, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` and the bounding period of ``other`` is the + same. + + Args: + other: object to compare with + + Returns: + True if equal, False otherwise + + MEOS Functions: + same_period_temporal + """ + if isinstance(other, int): + return spanset_eq(self._inner, float_to_floatspanset(other)) + else: + return super().is_same(other) + + # ------------------------- Position Operations --------------------------- + def is_left(self, other: Union[int, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is strictly left of ``other``. That is, + ``self`` ends before ``other`` starts. + + Args: + other: object to compare with + + Returns: + True if left, False otherwise + + MEOS Functions: + left_span_span, left_span_spanset, left_floatspan_float + """ + if isinstance(other, int): + return left_floatspanset_float(self._inner, other) + else: + return super().is_left(other) + + def is_over_or_left(self, other: Union[int, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is left ``other`` allowing overlap. That is, + ``self`` ends before ``other`` ends (or at the same value). + + Args: + other: object to compare with + + Returns: + True if before, False otherwise + + MEOS Functions: + overleft_span_span, overleft_span_spanset, overleft_floatspan_float + """ + if isinstance(other, int): + return overleft_floatspanset_float(self._inner, other) + else: + return super().is_over_or_left(other) + + def is_right(self, other: Union[int, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is strictly right ``other``. That is, ``self`` + starts after ``other`` ends. + + Args: + other: object to compare with + + Returns: + True if right, False otherwise + + MEOS Functions: + right_span_span, right_span_spanset, right_floatspan_float + """ + if isinstance(other, int): + return right_floatspanset_float(self._inner, other) + else: + return super().is_right(other) + + def is_over_or_right(self, other: Union[int, FloatSpan, FloatSpanSet]) -> bool: + """ + Returns whether ``self`` is right ``other`` allowing overlap. That is, + ``self`` starts after ``other`` starts (or at the same value). + + Args: + other: object to compare with + + Returns: + True if overlapping or after, False otherwise + + MEOS Functions: + overright_spanset_span, overright_spanset_spanset, + overright_floatspanset_float + """ + if isinstance(other, int): + return overright_floatspanset_float(self._inner, other) + else: + return super().is_over_or_right(other) + + # ------------------------- Distance Operations --------------------------- + def distance(self, other: Union[int, FloatSpan, FloatSpanSet]) -> float: + """ + Returns the distance between ``self`` and ``other``. + + Args: + other: object to compare with + + Returns: + A float value + + MEOS Functions: + distance_spanset_span, distance_spanset_spanset, + distance_floatspanset_float + """ + if isinstance(other, int): + return distance_floatspanset_float(self._inner, other) + else: + return super().distance(other) + # ------------------------- Set Operations -------------------------------- @overload - def intersection(self, other: FloatSpan) -> FloatSpanSet: + def intersection(self, other: int) -> Optional[int]: ... @overload - def intersection(self, other: FloatSpanSet) -> FloatSpanSet: + def intersection(self, other: FloatSpan) -> Optional[FloatSpanSet]: ... @overload - def intersection(self, other: float) -> float: + def intersection(self, other: FloatSpanSet) -> Optional[FloatSpanSet]: ... - def intersection(self, other: Union[float, FloatSpan, FloatSpanSet]) -> Union[float, FloatSpanSet]: + def intersection(self, other): """ Returns the intersection of ``self`` and ``other``. @@ -135,28 +377,24 @@ def intersection(self, other: Union[float, FloatSpan, FloatSpanSet]) -> Union[fl other: object to intersect with Returns: - An float or a :class:`FloatSpanSet` instance. The actual class depends + An int or a :class:`FloatSpanSet` instance. The actual class depends on ``other``. MEOS Functions: - intersection_floatspanset_int, intersection_spanset_spanset, + intersection_floatspanset_float, intersection_spanset_spanset, intersection_spanset_span """ from .floatspan import FloatSpan - if isinstance(other, float): - result = intersection_floatspanset_int(self._inner, float) - return timestamptz_to_int(result) if result is not None else None - elif isinstance(other, TimestampSet): - result = super().intersection(other) - return TimestampSet(_inner=result) if result is not None else None + if isinstance(other, int): + return intersection_floatspanset_float(self._inner, int) elif isinstance(other, FloatSpan): - result = super().intersection(other) + result = intersection_spanset_span(self._inner, other._inner) return FloatSpanSet(_inner=result) if result is not None else None elif isinstance(other, FloatSpanSet): - result = super().intersection(other) + result = intersection_spanset_spanset(self._inner, other._inner) return FloatSpanSet(_inner=result) if result is not None else None else: - raise TypeError(f'Operation not supported with type {other.__class__}') + super().intersection(other) def __mul__(self, other): """ @@ -169,7 +407,7 @@ def __mul__(self, other): A :class:`Time` instance. The actual class depends on ``other``. MEOS Functions: - intersection_floatspanset_int, intersection_spanset_spanset, + intersection_floatspanset_float, intersection_spanset_spanset, intersection_spanset_span """ return self.intersection(other) @@ -185,10 +423,10 @@ def minus(self, other: Time) -> FloatSpanSet: A :class:`FloatSpanSet` instance. MEOS Functions: - minus_spanset_span, minus_spanset_spanset, minus_floatspanset_int + minus_spanset_span, minus_spanset_spanset, minus_floatspanset_float """ if isinstance(other, float): - result = minus_floatspanset_int(self._inner, float) + result = minus_floatspanset_float(self._inner, float) else: result = super().minus(other) return FloatSpanSet(_inner=result) if result is not None else None @@ -205,7 +443,7 @@ def __sub__(self, other): MEOS Functions: minus_spanset_span, minus_spanset_spanset, - minus_floatspanset_int + minus_floatspanset_float """ return self.minus(other) @@ -220,11 +458,11 @@ def union(self, other: Time) -> FloatSpanSet: A :class:`FloatSpanSet` instance. MEOS Functions: - union_floatspanset_int, union_spanset_spanset, + union_floatspanset_float, union_spanset_spanset, union_spanset_span """ if isinstance(other, float): - result = union_floatspanset_int(self._inner, float) + result = union_floatspanset_float(self._inner, float) else: result = super().union(other) return FloatSpanSet(_inner=result) if result is not None else None @@ -240,7 +478,7 @@ def __add__(self, other): A :class:`FloatSpanSet` instance. MEOS Functions: - union_floatspanset_int, union_spanset_spanset, + union_floatspanset_float, union_spanset_spanset, union_spanset_span """ return self.union(other) diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py index 0779d2e5..fa8ff180 100644 --- a/pymeos/pymeos/collections/time/periodset.py +++ b/pymeos/pymeos/collections/time/periodset.py @@ -723,10 +723,10 @@ def intersection(self, other: Time) -> Union[PeriodSet, datetime, TimestampSet]: result = super().intersection(other) return TimestampSet(_inner=result) if result is not None else None elif isinstance(other, Period): - result = super().intersection(other) + result = intersection_spanset_span(self._inner, other._inner) return PeriodSet(_inner=result) if result is not None else None elif isinstance(other, PeriodSet): - result = super().intersection(other) + result = intersection_spanset_spanset(self._inner, other._inner) return PeriodSet(_inner=result) if result is not None else None else: raise TypeError(f'Operation not supported with type {other.__class__}') From 917a1d70bfab3f4cdc9b89f27dae7e01d45e73a1 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sun, 1 Oct 2023 14:06:24 +0200 Subject: [PATCH 067/101] Improve tests --- pymeos/pymeos/collections/base/span.py | 8 ++++++- pymeos/pymeos/collections/base/spanset.py | 11 +++++++-- .../pymeos/collections/number/floatspanset.py | 23 ++++++++----------- .../pymeos/collections/number/intspanset.py | 16 ++++--------- pymeos/pymeos/collections/time/periodset.py | 13 ++--------- 5 files changed, 32 insertions(+), 39 deletions(-) diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index 80d78a56..a8d7ec22 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -474,7 +474,13 @@ def intersection(self, other): intersection_span_span, intersection_spanset_span, intersection_period_timestamp """ - raise TypeError(f'Operation not supported with type {other.__class__}') + from .span import Span + if isinstance(other, Span): + return intersection_span_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return intersection_span_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') def __mul__(self, other): """ diff --git a/pymeos/pymeos/collections/base/spanset.py b/pymeos/pymeos/collections/base/spanset.py index 50651e0f..12a0501d 100644 --- a/pymeos/pymeos/collections/base/spanset.py +++ b/pymeos/pymeos/collections/base/spanset.py @@ -460,7 +460,13 @@ def intersection(self, other): MEOS Functions: intersection_spanset_spanset, intersection_spanset_span """ - raise TypeError(f'Operation not supported with type {other.__class__}') + from .span import Span + if isinstance(other, Span): + return intersection_spanset_span(self._inner, other._inner) + elif isinstance(other, SpanSet): + return intersection_spanset_spanset(self._inner, other._inner) + else: + raise TypeError(f'Operation not supported with type {other.__class__}') def __mul__(self, other): """ @@ -526,7 +532,8 @@ def union(self, other): A :class:`PeriodSet` instance. MEOS Functions: - union_periodset_timestamp, union_spanset_spanset, union_spanset_span + union_periodset_timestamp, union_spanset_spanset, + union_spanset_span """ from .span import Span if isinstance(other, Span): diff --git a/pymeos/pymeos/collections/number/floatspanset.py b/pymeos/pymeos/collections/number/floatspanset.py index ac19386d..e475813e 100644 --- a/pymeos/pymeos/collections/number/floatspanset.py +++ b/pymeos/pymeos/collections/number/floatspanset.py @@ -358,7 +358,7 @@ def distance(self, other: Union[int, FloatSpan, FloatSpanSet]) -> float: # ------------------------- Set Operations -------------------------------- @overload - def intersection(self, other: int) -> Optional[int]: + def intersection(self, other: Union[int, float]) -> Optional[float]: ... @overload @@ -385,16 +385,11 @@ def intersection(self, other): intersection_spanset_span """ from .floatspan import FloatSpan - if isinstance(other, int): - return intersection_floatspanset_float(self._inner, int) - elif isinstance(other, FloatSpan): - result = intersection_spanset_span(self._inner, other._inner) - return FloatSpanSet(_inner=result) if result is not None else None - elif isinstance(other, FloatSpanSet): - result = intersection_spanset_spanset(self._inner, other._inner) - return FloatSpanSet(_inner=result) if result is not None else None + if isinstance(other, int) or isinstance(other, float): + result = intersection_floatspanset_float(self._inner, float(other)) else: - super().intersection(other) + result = super().intersection(other) + return FloatSpanSet(_inner=result) if result is not None else None def __mul__(self, other): """ @@ -425,8 +420,8 @@ def minus(self, other: Time) -> FloatSpanSet: MEOS Functions: minus_spanset_span, minus_spanset_spanset, minus_floatspanset_float """ - if isinstance(other, float): - result = minus_floatspanset_float(self._inner, float) + if isinstance(other, int) or isinstance(other, float): + result = minus_floatspanset_float(self._inner, float(other)) else: result = super().minus(other) return FloatSpanSet(_inner=result) if result is not None else None @@ -461,8 +456,8 @@ def union(self, other: Time) -> FloatSpanSet: union_floatspanset_float, union_spanset_spanset, union_spanset_span """ - if isinstance(other, float): - result = union_floatspanset_float(self._inner, float) + if isinstance(other, int) or isinstance(other, float): + result = union_floatspanset_float(self._inner, float(other)) else: result = super().union(other) return FloatSpanSet(_inner=result) if result is not None else None diff --git a/pymeos/pymeos/collections/number/intspanset.py b/pymeos/pymeos/collections/number/intspanset.py index ce59c928..cb62753f 100644 --- a/pymeos/pymeos/collections/number/intspanset.py +++ b/pymeos/pymeos/collections/number/intspanset.py @@ -384,17 +384,11 @@ def intersection(self, other): intersection_intspanset_int, intersection_spanset_spanset, intersection_spanset_span """ - from .intspan import IntSpan if isinstance(other, int): - return intersection_intspanset_int(self._inner, int) - elif isinstance(other, IntSpan): - result = intersection_spanset_span(self._inner, other._inner) - return IntSpanSet(_inner=result) if result is not None else None - elif isinstance(other, IntSpanSet): - result = intersection_spanset_spanset(self._inner, other._inner) - return IntSpanSet(_inner=result) if result is not None else None + result = intersection_intspanset_int(self._inner, other) else: - super().intersection(other) + result = super().intersection(other) + return IntSpanSet(_inner=result) if result is not None else None def __mul__(self, other): """ @@ -426,7 +420,7 @@ def minus(self, other: Time) -> IntSpanSet: minus_spanset_span, minus_spanset_spanset, minus_intspanset_int """ if isinstance(other, int): - result = minus_intspanset_int(self._inner, int) + result = minus_intspanset_int(self._inner, other) else: result = super().minus(other) return IntSpanSet(_inner=result) if result is not None else None @@ -462,7 +456,7 @@ def union(self, other: Time) -> IntSpanSet: union_spanset_span """ if isinstance(other, int): - result = union_intspanset_int(self._inner, int) + result = union_intspanset_int(self._inner, other) else: result = super().union(other) return IntSpanSet(_inner=result) if result is not None else None diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py index fa8ff180..034ea886 100644 --- a/pymeos/pymeos/collections/time/periodset.py +++ b/pymeos/pymeos/collections/time/periodset.py @@ -714,22 +714,13 @@ def intersection(self, other: Time) -> Union[PeriodSet, datetime, TimestampSet]: intersection_spanset_span """ from .period import Period - from .timestampset import TimestampSet if isinstance(other, datetime): result = intersection_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) return timestamptz_to_datetime(result) if result is not None else None - elif isinstance(other, TimestampSet): - result = super().intersection(other) - return TimestampSet(_inner=result) if result is not None else None - elif isinstance(other, Period): - result = intersection_spanset_span(self._inner, other._inner) - return PeriodSet(_inner=result) if result is not None else None - elif isinstance(other, PeriodSet): - result = intersection_spanset_spanset(self._inner, other._inner) - return PeriodSet(_inner=result) if result is not None else None else: - raise TypeError(f'Operation not supported with type {other.__class__}') + result = super().intersection(other) + return PeriodSet(_inner=result) if result is not None else None def __mul__(self, other): """ From 86390ff1f5af135cb874b1f4fc8200ea185f3ba4 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 2 Oct 2023 11:00:41 +0200 Subject: [PATCH 068/101] Refactor RangePlotter into SpanPlotter --- pymeos/pymeos/plotters/__init__.py | 2 +- pymeos/pymeos/plotters/box_plotter.py | 4 ++-- pymeos/pymeos/plotters/range_plotter.py | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pymeos/pymeos/plotters/__init__.py b/pymeos/pymeos/plotters/__init__.py index 113688fe..358bfa6e 100644 --- a/pymeos/pymeos/plotters/__init__.py +++ b/pymeos/pymeos/plotters/__init__.py @@ -1,7 +1,7 @@ from .box_plotter import BoxPlotter from .point_sequence_plotter import TemporalPointSequencePlotter from .point_sequenceset_plotter import TemporalPointSequenceSetPlotter -from .range_plotter import RangePlotter +from .range_plotter import SpanPlotter from .sequence_plotter import TemporalSequencePlotter from .sequenceset_plotter import TemporalSequenceSetPlotter from .time_plotter import TimePlotter diff --git a/pymeos/pymeos/plotters/box_plotter.py b/pymeos/pymeos/plotters/box_plotter.py index 467feb1b..392083f3 100644 --- a/pymeos/pymeos/plotters/box_plotter.py +++ b/pymeos/pymeos/plotters/box_plotter.py @@ -1,6 +1,6 @@ from matplotlib import pyplot as plt -from .range_plotter import RangePlotter +from .range_plotter import SpanPlotter from .time_plotter import TimePlotter from ..boxes import TBox, STBox @@ -30,7 +30,7 @@ def plot_tbox(tbox: TBox, *args, axes=None, **kwargs): :func:`~pymeos.plotters.time_plotter.TimePlotter.plot_period` """ if not tbox.has_t: - return RangePlotter.plot_range(tbox.to_floatrange(), *args, axes=axes, **kwargs) + return SpanPlotter.plot_range(tbox.to_floatrange(), *args, axes=axes, **kwargs) if not tbox.has_x: return TimePlotter.plot_period(tbox.to_period(), *args, axes=axes, **kwargs) return BoxPlotter._plot_box(tbox.tmin(), tbox.tmax(), tbox.xmin(), tbox.xmax(), *args, axes=axes, **kwargs) diff --git a/pymeos/pymeos/plotters/range_plotter.py b/pymeos/pymeos/plotters/range_plotter.py index 5a1dfbc1..f20f5ca4 100644 --- a/pymeos/pymeos/plotters/range_plotter.py +++ b/pymeos/pymeos/plotters/range_plotter.py @@ -1,21 +1,21 @@ from typing import Union from matplotlib import pyplot as plt -from spans import floatrange, intrange +from pymeos import IntSpan, FloatSpan -class RangePlotter: +class SpanPlotter: """ - Plotter for :class:`floatrange` and :class:`intrange` objects. + Plotter for :class:`FloatSpan` and :class:`IntSpan` objects. """ @staticmethod - def plot_range(range: Union[floatrange, intrange], *args, axes=None, **kwargs): + def plot_range(span: Union[IntSpan, FloatSpan], *args, axes=None, **kwargs): """ - Plot a :class:`floatrange` or :class:`intrange` on the given axes. + Plot a :class:`FloatSpan` or :class:`IntSpan` on the given axes. Params: - range: The :class:`floatrange` or :class:`intrange` to plot. + range: The :class:`FloatSpan` or :class:`IntSpan` to plot. axes: The axes to plot on. If None, the current axes are used. *args: Additional arguments to pass to the plot function. **kwargs: Additional keyword arguments to pass to the plot function. @@ -24,8 +24,8 @@ def plot_range(range: Union[floatrange, intrange], *args, axes=None, **kwargs): List with the plotted elements. """ base = axes or plt.gca() - ll = base.axhline(range.lower, *args, linestyle='-' if range.lower_inc else '--', **kwargs) + ll = base.axhline(span.lower(), *args, linestyle='-' if span.lower_inc() else '--', **kwargs) kwargs.pop('label', None) - ul = base.axhline(range.upper, *args, linestyle='-' if range.upper_inc else '--', **kwargs) - s = base.axhspan(range.lower, range.upper, *args, alpha=0.3, **kwargs) + ul = base.axhline(span.upper(), *args, linestyle='-' if span.upper_inc() else '--', **kwargs) + s = base.axhspan(span.lower(), span.upper(), *args, alpha=0.3, **kwargs) return [ll, ul, s] From e89f4486607d121c02aa1243b7545c896a2c31ec Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 2 Oct 2023 11:01:05 +0200 Subject: [PATCH 069/101] Add conversion between int and float collections. --- pymeos/pymeos/collections/number/floatset.py | 17 ++++++- pymeos/pymeos/collections/number/floatspan.py | 44 ++++++++++++------- .../pymeos/collections/number/floatspanset.py | 29 +++++++----- pymeos/pymeos/collections/number/intset.py | 27 +++++++++--- pymeos/pymeos/collections/number/intspan.py | 22 ++++++++-- .../pymeos/collections/number/intspanset.py | 26 +++++++---- 6 files changed, 119 insertions(+), 46 deletions(-) diff --git a/pymeos/pymeos/collections/number/floatset.py b/pymeos/pymeos/collections/number/floatset.py index 562cf618..adfa24c3 100644 --- a/pymeos/pymeos/collections/number/floatset.py +++ b/pymeos/pymeos/collections/number/floatset.py @@ -1,9 +1,9 @@ from __future__ import annotations -from typing import List, Union, overload, Optional +from typing import List, Union, overload, Optional, TYPE_CHECKING from pymeos_cffi import floatset_in, floatset_make, floatset_out, \ - floatset_start_value, floatset_end_value, floatset_value_n, \ + floatset_start_value, floatset_end_value, floatset_value_n, \ floatset_values, contains_floatset_float, intersection_floatset_float, \ intersection_set_set, left_floatset_float, overleft_floatset_float, \ right_floatset_float, overright_floatset_float, minus_floatset_float, \ @@ -14,6 +14,9 @@ from .floatspanset import FloatSpanSet from ..base import Set +if TYPE_CHECKING: + from .intset import IntSet + class FloatSet(Set[float]): """ @@ -83,6 +86,16 @@ def to_span(self) -> FloatSpan: """ return FloatSpan(_inner=super().to_span()) + def to_intset(self) -> IntSet: + """ + Converts ``self`` to an :class:`IntSet` instance. + + Returns: + A new :class:`IntSet` instance + """ + from .intset import IntSet + return IntSet(elements=[int(x) for x in self.elements()]) + # ------------------------- Accessors ------------------------------------- def start_element(self) -> float: diff --git a/pymeos/pymeos/collections/number/floatspan.py b/pymeos/pymeos/collections/number/floatspan.py index ad151e51..da4d7b9f 100644 --- a/pymeos/pymeos/collections/number/floatspan.py +++ b/pymeos/pymeos/collections/number/floatspan.py @@ -10,13 +10,14 @@ right_floatspan_float, overright_floatspan_float, \ intersection_span_span, intersection_spanset_span, \ minus_floatspan_float, minus_span_span, minus_spanset_span, \ - union_floatspan_float, union_span_span, union_spanset_span, \ - floatspan_out, floatspan_make, span_width + union_floatspan_float, union_span_span, union_spanset_span, \ + floatspan_out, floatspan_make, span_width, floatspan_intspan from .. import Span if TYPE_CHECKING: from .floatspanset import FloatSpanSet + from .intspan import IntSpan class FloatSpan(Span[float]): @@ -75,6 +76,19 @@ def to_spanset(self) -> FloatSpanSet: from .floatspanset import FloatSpanSet return FloatSpanSet(_inner=super().to_spanset()) + def to_intspan(self) -> IntSpan: + """ + Converts ``self`` to a :class:`IntSpan` instance. + + Returns: + A new :class:`IntSpan` instance + + MEOS Functions: + floatspan_intspan + """ + from .intspan import IntSpan + return IntSpan(_inner=floatspan_intspan(self._inner)) + # ------------------------- Accessors ------------------------------------- def lower(self) -> float: """ @@ -163,8 +177,8 @@ def shift_scale(self, delta: Optional[float], width: Optional[float]) -> FloatSp """ d = delta if delta is not None else 0 w = width if width is not None else 0 - modified = floatspan_shift_scale(self._inner, d, w, delta is not None, - width is not None) + modified = floatspan_shift_scale(self._inner, d, w, delta is not None, + width is not None) return FloatSpan(_inner=modified) # ------------------------- Topological Operations -------------------------------- @@ -183,7 +197,7 @@ def is_adjacent(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> bool MEOS Functions: adjacent_span_span, adjacent_span_spanset, adjacent_floatspan_float """ - if isinstance(other, int) or isinstance(other, float) : + if isinstance(other, int) or isinstance(other, float): return adjacent_floatspan_float(self._inner, float(other)) else: return super().is_adjacent(other) @@ -201,7 +215,7 @@ def contains(self, content: Union[int, float, FloatSpan, FloatSpanSet]) -> bool: MEOS Functions: contains_set_set, contains_floatspan_float """ - if isinstance(content, int) or isinstance(content, float) : + if isinstance(content, int) or isinstance(content, float): return contains_floatspan_float(self._inner, float(content)) else: return super().contains(content) @@ -220,7 +234,7 @@ def is_same(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> bool: MEOS Functions: same_period_temporal """ - if isinstance(other, int) or isinstance(other, float) : + if isinstance(other, int) or isinstance(other, float): return span_eq(self._inner, float_to_floatspan(float(other))) else: return super().is_same(other) @@ -240,7 +254,7 @@ def is_left(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> bool: MEOS Functions: left_span_span, left_span_spanset, left_floatspan_float """ - if isinstance(other, int) or isinstance(other, float) : + if isinstance(other, int) or isinstance(other, float): return left_floatspan_float(self._inner, float(other)) else: return super().is_left(other) @@ -259,7 +273,7 @@ def is_over_or_left(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> MEOS Functions: overleft_span_span, overleft_span_spanset, overleft_floatspan_float """ - if isinstance(other, int) or isinstance(other, float) : + if isinstance(other, int) or isinstance(other, float): return overleft_floatspan_float(self._inner, float(other)) else: return super().is_over_or_left(other) @@ -278,7 +292,7 @@ def is_right(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> bool: MEOS Functions: right_span_span, right_span_spanset, right_floatspan_float """ - if isinstance(other, int) or isinstance(other, float) : + if isinstance(other, int) or isinstance(other, float): return right_floatspan_float(self._inner, float(other)) else: return super().is_right(other) @@ -297,7 +311,7 @@ def is_over_or_right(self, other: Union[float, FloatSpan, FloatSpanSet]) -> bool MEOS Functions: overright_span_span, overright_span_spanset, overright_floatspan_float """ - if isinstance(other, int) or isinstance(other, float) : + if isinstance(other, int) or isinstance(other, float): return overright_floatspan_float(self._inner, float(other)) else: return super().is_over_or_right(other) @@ -316,7 +330,7 @@ def distance(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> float: MEOS Functions: distance_span_span, distance_span_spanset, distance_floatspan_float, """ - if isinstance(other, int) or isinstance(other, float) : + if isinstance(other, int) or isinstance(other, float): return distance_floatspan_float(self._inner, float(other)) else: return super().distance(other) @@ -350,7 +364,7 @@ def intersection(self, other): intersection_floatset_float """ from .floatspanset import FloatSpanSet - if isinstance(other, int) or isinstance(other, float) : + if isinstance(other, int) or isinstance(other, float): return intersection_floatspan_float(self._inner, float(other)) elif isinstance(other, FloatSpan): result = intersection_span_span(self._inner, other._inner) @@ -375,7 +389,7 @@ def minus(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> FloatSpanS minus_span_span, minus_spanset_span, minus_floatspan_float """ from .floatspanset import FloatSpanSet - if isinstance(other, int) or isinstance(other, float) : + if isinstance(other, int) or isinstance(other, float): result = minus_floatspan_float(self._inner, float(other)) elif isinstance(other, FloatSpan): result = minus_span_span(self._inner, other._inner) @@ -399,7 +413,7 @@ def union(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> FloatSpanS union_spanset_span, union_span_span, union_floatspan_float """ from .floatspanset import FloatSpanSet - if isinstance(other, int) or isinstance(other, float) : + if isinstance(other, int) or isinstance(other, float): result = union_floatspan_float(self._inner, float(other)) elif isinstance(other, FloatSpan): result = union_span_span(self._inner, other._inner) diff --git a/pymeos/pymeos/collections/number/floatspanset.py b/pymeos/pymeos/collections/number/floatspanset.py index e475813e..94fa506e 100644 --- a/pymeos/pymeos/collections/number/floatspanset.py +++ b/pymeos/pymeos/collections/number/floatspanset.py @@ -1,20 +1,20 @@ from __future__ import annotations -from typing import Union, overload, Optional, TYPE_CHECKING +from typing import Union, overload, Optional, TYPE_CHECKING, List from pymeos_cffi import floatspanset_in, floatspanset_out, spanset_width, \ floatspanset_shift_scale, adjacent_floatspanset_float, contains_floatspanset_float, \ spanset_eq, float_to_floatspanset, left_floatspanset_float, \ overleft_floatspanset_float, right_floatspanset_float, overright_floatspanset_float, \ distance_floatspanset_float, intersection_floatspanset_float, \ - intersection_spanset_span, intersection_spanset_spanset, \ - union_floatspanset_float, union_spanset_span, union_spanset_spanset, \ - minus_floatspanset_float, minus_spanset_span, minus_spanset_spanset + union_floatspanset_float, minus_floatspanset_float, floatspanset_intspanset from pymeos.collections import SpanSet if TYPE_CHECKING: from .floatspan import FloatSpan + from .intspanset import IntSpanSet + class FloatSpanSet(SpanSet[float]): """ @@ -41,7 +41,6 @@ class FloatSpanSet(SpanSet[float]): _parse_function = floatspanset_in _parse_value_function = lambda span: floatspanset_in(span)[0] if isinstance(span, str) else span._inner[0] - # ------------------------- Output ---------------------------------------- def __str__(self, max_decimals: int = 15): """ @@ -70,6 +69,19 @@ def to_span(self) -> FloatSpan: from .floatspan import FloatSpan return FloatSpan(_inner=super().to_span()) + def to_intspanset(self) -> IntSpanSet: + """ + Returns an intspanset that encompasses ``self``. + + Returns: + A new :class:`IntSpanSet` instance + + MEOS Functions: + floatspanset_intspanset + """ + from .intspanset import IntSpanSet + return IntSpanSet(_inner=floatspanset_intspanset(self._inner)) + # ------------------------- Accessors ------------------------------------- def width(self, ignore_gaps: Optional[bool] = False) -> float: @@ -142,7 +154,6 @@ def spans(self) -> List[FloatSpan]: ps = super().spans() return [FloatSpan(_inner=ps[i]) for i in range(self.num_spans())] - # ------------------------- Transformations ------------------------------- def shift(self, delta: int) -> FloatSpanSet: @@ -194,8 +205,8 @@ def shift_scale(self, delta: Optional[int], width: Optional[int]) -> FloatSpanSe """ d = delta if delta is not None else 0 w = width if width is not None else 0 - modified = floatspanset_shift_scale(self._inner, d, w, delta is not None, - width is not None) + modified = floatspanset_shift_scale(self._inner, d, w, delta is not None, + width is not None) return FloatSpanSet(_inner=modified) # ------------------------- Topological Operations -------------------------------- @@ -384,7 +395,6 @@ def intersection(self, other): intersection_floatspanset_float, intersection_spanset_spanset, intersection_spanset_span """ - from .floatspan import FloatSpan if isinstance(other, int) or isinstance(other, float): result = intersection_floatspanset_float(self._inner, float(other)) else: @@ -477,4 +487,3 @@ def __add__(self, other): union_spanset_span """ return self.union(other) - diff --git a/pymeos/pymeos/collections/number/intset.py b/pymeos/pymeos/collections/number/intset.py index 7a376c13..57bf45bc 100644 --- a/pymeos/pymeos/collections/number/intset.py +++ b/pymeos/pymeos/collections/number/intset.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import List, Union, overload, Optional +from typing import List, Union, overload, Optional, TYPE_CHECKING from pymeos_cffi import intset_in, intset_make, intset_out, intset_start_value, \ intset_end_value, intset_value_n, intset_values, contains_intset_int, \ @@ -13,6 +13,9 @@ from .intspanset import IntSpanSet from ..base import Set +if TYPE_CHECKING: + from .floatset import FloatSet + class IntSet(Set[int]): """ @@ -82,6 +85,16 @@ def to_span(self) -> IntSpan: """ return IntSpan(_inner=super().to_span()) + def to_floatset(self) -> FloatSet: + """ + Converts ``self`` to a :class:`FloatSet` instance. + + Returns: + A new :class:`FloatSet` instance + """ + from .floatset import FloatSet + return FloatSet(elements=[float(x) for x in self.elements()]) + # ------------------------- Accessors ------------------------------------- def start_element(self) -> int: @@ -188,7 +201,7 @@ def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSet: """ return IntSet( _inner=intset_shift_scale(self._inner, delta, width, - delta is not None, width is not None)) + delta is not None, width is not None)) # ------------------------- Topological Operations -------------------------------- @@ -205,7 +218,7 @@ def contains(self, content: Union[IntSet, int]) -> bool: MEOS Functions: contains_set_set, contains_intset_int """ - if isinstance(content, int): + if isinstance(content, int): return contains_intset_int(self._inner, content) else: return super().contains(content) @@ -226,7 +239,7 @@ def is_left(self, content: Union[IntSet, int]) -> bool: MEOS Functions: left_set_set, left_intset_int """ - if isinstance(content, int): + if isinstance(content, int): return left_intset_int(self._inner, content) else: return super().is_left(content) @@ -245,7 +258,7 @@ def is_over_or_left(self, content: Union[IntSet, int]) -> bool: MEOS Functions: overleft_set_set, overleft_intset_int """ - if isinstance(content, int): + if isinstance(content, int): return overleft_intset_int(self._inner, content) else: return super().is_over_or_left(content) @@ -264,7 +277,7 @@ def is_right(self, content: Union[IntSet, int]) -> bool: MEOS Functions: right_set_set, right_intset_int """ - if isinstance(content, int): + if isinstance(content, int): return right_intset_int(self._inner, content) else: return super().is_right(content) @@ -283,7 +296,7 @@ def is_over_or_right(self, content: Union[IntSet, int]) -> bool: MEOS Functions: overright_set_set, overright_intset_int """ - if isinstance(content, int): + if isinstance(content, int): return overright_intset_int(self._inner, content) else: return super().is_over_or_right(content) diff --git a/pymeos/pymeos/collections/number/intspan.py b/pymeos/pymeos/collections/number/intspan.py index 679854c3..2ce60355 100644 --- a/pymeos/pymeos/collections/number/intspan.py +++ b/pymeos/pymeos/collections/number/intspan.py @@ -4,17 +4,18 @@ from pymeos_cffi import intspan_in, intspan_lower, intspan_upper, \ intspan_shift_scale, contains_intspan_int, adjacent_intspan_int, \ - span_width, int_to_intspan, span_eq, left_intspan_int,\ + span_width, int_to_intspan, span_eq, left_intspan_int, \ overleft_intspan_int, right_intspan_int, overright_intspan_int, \ intersection_intspan_int, intersection_span_span, intersection_spanset_span, \ minus_intspan_int, minus_span_span, minus_spanset_span, union_intspan_int, \ union_span_span, union_spanset_span, intspan_out, intspan_make, \ - distance_intspan_int + distance_intspan_int, intspan_floatspan from .. import Span if TYPE_CHECKING: from .intspanset import IntSpanSet + from .floatspan import FloatSpan class IntSpan(Span[int]): @@ -72,6 +73,19 @@ def to_spanset(self) -> IntSpanSet: from .intspanset import IntSpanSet return IntSpanSet(_inner=super().to_spanset()) + def to_floatspan(self) -> FloatSpan: + """ + Converts ``self`` to a :class:`FloatSpan` instance. + + Returns: + A new :class:`FloatSpan` instance + + MEOS Functions: + intspan_floatspan + """ + from .floatspan import FloatSpan + return FloatSpan(_inner=intspan_floatspan(self._inner)) + # ------------------------- Accessors ------------------------------------- def lower(self) -> int: """ @@ -159,8 +173,8 @@ def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSpan: """ d = delta if delta is not None else 0 w = width if width is not None else 0 - modified = intspan_shift_scale(self._inner, d, w, delta is not None, - width is not None) + modified = intspan_shift_scale(self._inner, d, w, delta is not None, + width is not None) return IntSpan(_inner=modified) # ------------------------- Topological Operations -------------------------------- diff --git a/pymeos/pymeos/collections/number/intspanset.py b/pymeos/pymeos/collections/number/intspanset.py index cb62753f..f191bb68 100644 --- a/pymeos/pymeos/collections/number/intspanset.py +++ b/pymeos/pymeos/collections/number/intspanset.py @@ -7,14 +7,14 @@ spanset_eq, int_to_intspanset, left_intspanset_int, \ overleft_intspanset_int, right_intspanset_int, overright_intspanset_int, \ distance_intspanset_int, intersection_intspanset_int, \ - intersection_spanset_span, intersection_spanset_spanset, \ - union_intspanset_int, union_spanset_span, union_spanset_spanset, \ - minus_intspanset_int, minus_spanset_span, minus_spanset_spanset + union_intspanset_int, minus_intspanset_int, intspanset_floatspanset from pymeos.collections import SpanSet if TYPE_CHECKING: from .intspan import IntSpan + from .floatspanset import FloatSpanSet + class IntSpanSet(SpanSet[int]): """ @@ -41,7 +41,6 @@ class IntSpanSet(SpanSet[int]): _parse_function = intspanset_in _parse_value_function = lambda span: intspanset_in(span)[0] if isinstance(span, str) else span._inner[0] - # ------------------------- Output ---------------------------------------- def __str__(self): """ @@ -70,6 +69,19 @@ def to_span(self) -> IntSpan: from .intspan import IntSpan return IntSpan(_inner=super().to_span()) + def to_floatspanset(self) -> FloatSpanSet: + """ + Converts ``self`` to a :class:`FloatSpanSet` instance. + + Returns: + A new :class:`FloatSpanSet` instance + + MEOS Functions: + intspanset_floatspanset + """ + from .floatspanset import FloatSpanSet + return FloatSpanSet(_inner=intspanset_floatspanset(self._inner)) + # ------------------------- Accessors ------------------------------------- def width(self, ignore_gaps: Optional[bool] = False) -> float: @@ -142,7 +154,6 @@ def spans(self) -> List[IntSpan]: ps = super().spans() return [IntSpan(_inner=ps[i]) for i in range(self.num_spans())] - # ------------------------- Transformations ------------------------------- def shift(self, delta: int) -> IntSpanSet: @@ -194,8 +205,8 @@ def shift_scale(self, delta: Optional[int], width: Optional[int]) -> IntSpanSet: """ d = delta if delta is not None else 0 w = width if width is not None else 0 - modified = intspanset_shift_scale(self._inner, d, w, delta is not None, - width is not None) + modified = intspanset_shift_scale(self._inner, d, w, delta is not None, + width is not None) return IntSpanSet(_inner=modified) # ------------------------- Topological Operations -------------------------------- @@ -476,4 +487,3 @@ def __add__(self, other): union_spanset_span """ return self.union(other) - From 2d094128889007b071128ba8850313c6eb2f510e Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 2 Oct 2023 11:57:01 +0200 Subject: [PATCH 070/101] Fix some typings. Fix at/minus functions of TInt and TFloat. Fix TBox tiling. Remove spans dependency. --- pymeos/pymeos/__init__.py | 3 +- pymeos/pymeos/boxes/tbox.py | 129 +++--- pymeos/pymeos/collections/base/collection.py | 14 +- pymeos/pymeos/collections/base/span.py | 11 +- .../pymeos/collections/number/floatspanset.py | 4 +- .../pymeos/collections/number/intspanset.py | 6 +- pymeos/pymeos/collections/time/periodset.py | 1 - pymeos/pymeos/main/tfloat.py | 45 +- pymeos/pymeos/main/tint.py | 54 ++- pymeos/pymeos/main/tnumber.py | 60 ++- pymeos/pymeos/main/tpoint.py | 67 ++- pymeos/pymeos/plotters/box_plotter.py | 2 +- .../pymeos/plotters/point_sequence_plotter.py | 2 +- pymeos/pyproject.toml | 1 - pymeos/tests/main/tfloat_test.py | 421 +++++++++--------- pymeos/tests/main/tint_test.py | 336 +++++++------- 16 files changed, 579 insertions(+), 577 deletions(-) diff --git a/pymeos/pymeos/__init__.py b/pymeos/pymeos/__init__.py index 3c939e42..48dbb6fb 100644 --- a/pymeos/pymeos/__init__.py +++ b/pymeos/pymeos/__init__.py @@ -25,7 +25,8 @@ 'Time', 'Period', 'TimestampSet', 'PeriodSet', 'TextSet', 'IntSet', 'IntSpan', 'IntSpanSet', - 'FloatSet', 'FloatSpan', 'FloatSpanSet' + 'FloatSet', 'FloatSpan', 'FloatSpanSet', + 'GeometrySet', 'GeographySet', # extras 'TInterpolation', # aggregators diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index 2d68a3ae..ce99b7c9 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -46,6 +46,9 @@ def _inner_span(self): from pymeos_cffi.functions import _ffi return _ffi.addressof(self._inner.span) + def _is_float(self) -> bool: + return self._inner.span.basetype == 5 + # ------------------------- Constructors ---------------------------------- def __init__(self, string: Optional[str] = None, *, xmin: Optional[Union[str, int, float]] = None, @@ -58,7 +61,7 @@ def __init__(self, string: Optional[str] = None, *, tmax_inc: bool = False, _inner=None): assert (_inner is not None) or (string is not None) != ( - (xmin is not None and xmax is not None) or + (xmin is not None and xmax is not None) or (tmin is not None and tmax is not None)), \ "Either string must be not None or at least a bound pair (xmin/max or tmin/max) must be not None" if _inner is not None: @@ -73,10 +76,10 @@ def __init__(self, string: Optional[str] = None, *, span = intspan_make(xmin, xmax, xmin_inc, xmax_inc) else: span = floatspan_make(float(xmin), float(xmax), xmin_inc, - xmax_inc) + xmax_inc) if tmin is not None and tmax is not None: period = Period(lower=tmin, upper=tmax, lower_inc=tmin_inc, - upper_inc=tmax_inc)._inner + upper_inc=tmax_inc)._inner self._inner = tbox_make(span, period) def __copy__(self) -> TBox: @@ -200,23 +203,23 @@ def from_value_time(value: Union[int, float, IntSpan, FloatSpan], span_timestamp_to_tbox, span_period_to_tbox """ if isinstance(value, int) and isinstance(time, datetime): - result = int_timestamp_to_tbox(value, - datetime_to_timestamptz(time)) + result = int_timestamp_to_tbox(value, + datetime_to_timestamptz(time)) elif isinstance(value, int) and isinstance(time, Period): result = int_period_to_tbox(value, time._inner) elif isinstance(value, float) and isinstance(time, datetime): - result = float_timestamp_to_tbox(value, - datetime_to_timestamptz(time)) + result = float_timestamp_to_tbox(value, + datetime_to_timestamptz(time)) elif isinstance(value, float) and isinstance(time, Period): result = float_period_to_tbox(value, time._inner) elif isinstance(value, IntSpan) and isinstance(time, datetime): result = span_timestamp_to_tbox(value._inner, - datetime_to_timestamptz(time)) + datetime_to_timestamptz(time)) elif isinstance(value, IntSpan) and isinstance(time, Period): result = span_period_to_tbox(value._inner, time._inner) elif isinstance(value, FloatSpan) and isinstance(time, datetime): result = span_timestamp_to_tbox(value._inner, - datetime_to_timestamptz(time)) + datetime_to_timestamptz(time)) elif isinstance(value, FloatSpan) and isinstance(time, Period): result = span_period_to_tbox(value._inner, time._inner) else: @@ -265,7 +268,7 @@ def __repr__(self): return (f'{self.__class__.__name__}' f'({self})') - def as_wkb(self) -> str: + def as_wkb(self) -> bytes: """ Returns the WKB representation of ``self``. @@ -291,7 +294,7 @@ def as_hexwkb(self) -> str: return tbox_as_hexwkb(self._inner, -1)[0] # ------------------------- Conversions ---------------------------------- - def to_floatspan(self) -> Span: + def to_floatspan(self) -> FloatSpan: """ Returns the numeric span of ``self``. @@ -542,7 +545,7 @@ def scale_time(self, duration: timedelta) -> TBox: return self.shift_scale_time(duration=duration) def shift_scale_value(self, shift: Optional[Union[int, float]] = None, - width: Optional[Union[int, float]] = None) -> TBox: + width: Optional[Union[int, float]] = None) -> TBox: """ Returns a new TBox with the value span shifted by `shift` and width `width`. @@ -570,21 +573,21 @@ def shift_scale_value(self, shift: Optional[Union[int, float]] = None, hasshift = shift is not None haswidth = width is not None if (shift is None or isinstance(shift, int)) and \ - (width is None or isinstance(width, int)) : + (width is None or isinstance(width, int)): result = tbox_shift_scale_int(self._inner, - shift if shift else 0, width if width else 0, - hasshift, haswidth) + shift if shift else 0, width if width else 0, + hasshift, haswidth) elif (shift is None or isinstance(shift, float)) and \ - (width is None or isinstance(width, float)) : + (width is None or isinstance(width, float)): result = tbox_shift_scale_float(self._inner, - shift if shift else 0.0, width if width else 0.0, - hasshift, haswidth) + shift if shift else 0.0, width if width else 0.0, + hasshift, haswidth) else: raise TypeError(f'Operation not supported with type {self.__class__}') return TBox(_inner=result) def shift_scale_time(self, shift: Optional[timedelta] = None, - duration: Optional[timedelta] = None) -> TBox: + duration: Optional[timedelta] = None) -> TBox: """ Returns a new TBox with the temporal span shifted by `shift` and duration `duration`. @@ -618,7 +621,7 @@ def shift_scale_time(self, shift: Optional[timedelta] = None, ) return TBox(_inner=result) - def round(self, maxdd : int = 0) -> TBox: + def round(self, maxdd: int = 0) -> TBox: """ Returns `self` rounded to the given number of decimal digits. @@ -703,8 +706,8 @@ def __mul__(self, other): return self.intersection(other) # ------------------------- Topological Operations ------------------------ - def is_adjacent(self, - other: Union[int, float, IntSpan, FloatSpan, TBox, TNumber]) -> bool: + def is_adjacent(self, + other: Union[int, float, IntSpan, FloatSpan, TBox, TNumber]) -> bool: """ Returns whether ``self`` is adjacent to ``other``. That is, they share only the temporal or numerical bound and only one of them contains it. @@ -727,15 +730,15 @@ def is_adjacent(self, adjacent_tbox_tbox, tnumber_to_tbox """ if isinstance(other, int): - return adjacent_span_span(self._inner_span(), - float_to_floaspan(float(other))) + return adjacent_span_span(self._inner_span(), + float_to_floatspan(float(other))) elif isinstance(other, float): - return adjacent_span_span(self._inner_span(), - float_to_floaspan(other)) + return adjacent_span_span(self._inner_span(), + float_to_floatspan(other)) elif isinstance(other, IntSpan): from pymeos_cffi.functions import _ffi return adjacent_span_span(_ffi.addressof(self._inner, 'span'), - other._inner) + other._inner) elif isinstance(other, FloatSpan): return adjacent_span_span(self._inner.span, other._inner) elif isinstance(other, TBox): @@ -770,7 +773,7 @@ def is_contained_in(self, container: Union[TBox, TNumber]) -> bool: return contained_tbox_tbox(self._inner, container._inner) elif isinstance(container, TNumber): return contained_tbox_tbox(self._inner, - tnumber_to_tbox(container._inner)) + tnumber_to_tbox(container._inner)) else: raise TypeError(f'Operation not supported with type {container.__class__}') @@ -798,8 +801,8 @@ def contains(self, content: Union[TBox, TNumber]) -> bool: if isinstance(content, TBox): return contains_tbox_tbox(self._inner, content._inner) elif isinstance(content, TNumber): - return contains_tbox_tbox(self._inner, - tnumber_to_tbox(content._inner)) + return contains_tbox_tbox(self._inner, + tnumber_to_tbox(content._inner)) else: raise TypeError(f'Operation not supported with type {content.__class__}') @@ -844,7 +847,7 @@ def overlaps(self, other: Union[TBox, TNumber]) -> bool: return overlaps_tbox_tbox(self._inner, other._inner) elif isinstance(other, TNumber): return overlaps_tbox_tbox(self._inner, - tnumber_to_tbox(other._inner)) + tnumber_to_tbox(other._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -949,7 +952,7 @@ def is_over_or_right(self, other: Union[TBox, TNumber]) -> bool: return overright_tbox_tbox(self._inner, other._inner) elif isinstance(other, TNumber): return overright_tbox_tbox(self._inner, - tnumber_to_tbox(other._inner)) + tnumber_to_tbox(other._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -991,7 +994,7 @@ def is_over_or_before(self, other: Union[TBox, TNumber]) -> bool: return overbefore_tbox_tbox(self._inner, other._inner) elif isinstance(other, TNumber): return overbefore_tbox_tbox(self._inner, - tnumber_to_tbox(other._inner)) + tnumber_to_tbox(other._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -1033,7 +1036,7 @@ def is_over_or_after(self, other: Union[TBox, TNumber]) -> bool: return overafter_tbox_tbox(self._inner, other._inner) elif isinstance(other, TNumber): return overafter_tbox_tbox(self._inner, - tnumber_to_tbox(other._inner)) + tnumber_to_tbox(other._inner)) else: raise TypeError(f'Operation not supported with type {other.__class__}') @@ -1062,7 +1065,7 @@ def nearest_approach_distance(self, other: Union[TBox, TNumber]) -> float: # ------------------------- Splitting -------------------------------------- def tile(self, size: float, duration: Union[timedelta, str], origin: float = 0.0, start: Union[datetime, str, None] = None) -> \ - List[List[TBox]]: + List[TBox]: """ Returns 2d matrix of TBoxes resulting of tiling ``self``. @@ -1074,41 +1077,43 @@ def tile(self, size: float, duration: Union[timedelta, str], start time used by default is Monday, January 3, 2000. Returns: - A 2d array of :class:`TBox` instances. + An array of :class:`TBox` instances. MEOS Functions: - tbox_tile_list + tintbox_tile_list, tfloabox_tile_list """ dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) st = datetime_to_timestamptz(start) if isinstance(start, datetime) \ else pg_timestamptz_in(start, -1) if isinstance(start, str) \ else pg_timestamptz_in('2000-01-03', -1) - tiles, rows, columns = tbox_tile_list(self._inner, size, dt, origin, st) - return [[TBox(_inner=tiles + (c * rows + r)) for c in range(columns)] - for r in range(rows)] - - def tile_flat(self, size: float, duration: Union[timedelta, str], - origin: float = 0.0, - start: Union[datetime, str, None] = None) -> List[TBox]: - """ - Returns an array of TBoxes resulting of tiling ``self``. - - Args: - size: size of the numeric dimension of the tiles - duration: size of the temporal dimenstion of the tiles - origin: origin of the numeric dimension of the tiles - start: origin of the temporal dimension of the tiles. If None, the - start time used by default is Monday, January 3, 2000. - - Returns: - An array of :class:`TBox` instances. - - MEOS Functions: - tbox_tile_list - """ - tiles = self.tile(size, duration, origin, start) - return [box for row in tiles for box in row] + if self._is_float(): + tiles, count = tfloatbox_tile_list(self._inner, size, dt, origin, st) + else: + tiles, count = tintbox_tile_list(self._inner, int(size), dt, int(origin), st) + return [TBox(_inner=tiles + c) for c in range(count)] + + # def tile_flat(self, size: float, duration: Union[timedelta, str], + # origin: float = 0.0, + # start: Union[datetime, str, None] = None) -> List[TBox]: + # """ + # Returns an array of TBoxes resulting of tiling ``self``. + # + # Args: + # size: size of the numeric dimension of the tiles + # duration: size of the temporal dimenstion of the tiles + # origin: origin of the numeric dimension of the tiles + # start: origin of the temporal dimension of the tiles. If None, the + # start time used by default is Monday, January 3, 2000. + # + # Returns: + # An array of :class:`TBox` instances. + # + # MEOS Functions: + # tbox_tile_list + # """ + # tiles = self.tile(size, duration, origin, start) + # return [box for row in tiles for box in row] # ------------------------- Comparisons ----------------------------------- def __eq__(self, other): diff --git a/pymeos/pymeos/collections/base/collection.py b/pymeos/pymeos/collections/base/collection.py index 49b09162..dc77ce6a 100644 --- a/pymeos/pymeos/collections/base/collection.py +++ b/pymeos/pymeos/collections/base/collection.py @@ -1,15 +1,7 @@ from __future__ import annotations from abc import ABC, abstractmethod -from datetime import datetime, timedelta -from typing import Generic, TypeVar, Type, Callable, Any, TYPE_CHECKING, Iterable -from typing import Optional, Union, overload, get_args, List - -from pymeos_cffi import * - -if TYPE_CHECKING: - from .spanset import SpanSet - from .span import Span +from typing import Generic, TypeVar T = TypeVar('T') Self = TypeVar('Self', bound='Set[Any]') @@ -19,7 +11,7 @@ class Collection(Generic[T], ABC): # ------------------------- Topological Operations ------------------------ # @abstractmethod # def is_adjacent(self, other) -> bool: - # raise NotImplementedError() + # raise NotImplementedError() @abstractmethod def is_contained_in(self, container) -> bool: @@ -39,7 +31,7 @@ def overlaps(self, other) -> bool: # @abstractmethod # def is_same(self, other) -> bool: - # raise NotImplementedError() + # raise NotImplementedError() # ------------------------- Position Operations --------------------------- @abstractmethod diff --git a/pymeos/pymeos/collections/base/span.py b/pymeos/pymeos/collections/base/span.py index a8d7ec22..b82e2f51 100644 --- a/pymeos/pymeos/collections/base/span.py +++ b/pymeos/pymeos/collections/base/span.py @@ -1,9 +1,8 @@ from __future__ import annotations from abc import ABC, abstractmethod -from datetime import timedelta -from typing import Generic, TypeVar, Type, Callable, Any, TYPE_CHECKING from typing import Optional, Union +from typing import TypeVar, Type, Callable, Any, TYPE_CHECKING from pymeos_cffi import * @@ -36,7 +35,7 @@ def __init__(self, string: Optional[str] = None, *, _inner=None): super().__init__() assert (_inner is not None) or ((string is not None) != \ - (lower is not None and upper is not None)), \ + (lower is not None and upper is not None)), \ "Either string must be not None or both lower and upper must be not" if _inner is not None: self._inner = _inner @@ -46,7 +45,7 @@ def __init__(self, string: Optional[str] = None, *, lower_converted = self.__class__._parse_value_function(lower) upper_converted = self.__class__._parse_value_function(upper) self._inner = self.__class__._make_function(lower_converted, - upper_converted, lower_inc, upper_inc) + upper_converted, lower_inc, upper_inc) def __copy__(self: Self) -> Self: """ @@ -474,11 +473,11 @@ def intersection(self, other): intersection_span_span, intersection_spanset_span, intersection_period_timestamp """ - from .span import Span + from .spanset import SpanSet if isinstance(other, Span): return intersection_span_span(self._inner, other._inner) elif isinstance(other, SpanSet): - return intersection_span_spanset(self._inner, other._inner) + return intersection_spanset_span(other._inner, self._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') diff --git a/pymeos/pymeos/collections/number/floatspanset.py b/pymeos/pymeos/collections/number/floatspanset.py index 94fa506e..c9d0bb26 100644 --- a/pymeos/pymeos/collections/number/floatspanset.py +++ b/pymeos/pymeos/collections/number/floatspanset.py @@ -417,7 +417,7 @@ def __mul__(self, other): """ return self.intersection(other) - def minus(self, other: Time) -> FloatSpanSet: + def minus(self, other: Union[int, FloatSpan, FloatSpanSet]) -> FloatSpanSet: """ Returns the difference of ``self`` and ``other``. @@ -452,7 +452,7 @@ def __sub__(self, other): """ return self.minus(other) - def union(self, other: Time) -> FloatSpanSet: + def union(self, other: Union[int, FloatSpan, FloatSpanSet]) -> FloatSpanSet: """ Returns the union of ``self`` and ``other``. diff --git a/pymeos/pymeos/collections/number/intspanset.py b/pymeos/pymeos/collections/number/intspanset.py index f191bb68..b4e27fc4 100644 --- a/pymeos/pymeos/collections/number/intspanset.py +++ b/pymeos/pymeos/collections/number/intspanset.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Union, overload, Optional, TYPE_CHECKING +from typing import Union, overload, Optional, TYPE_CHECKING, List from pymeos_cffi import intspanset_in, intspanset_out, spanset_width, \ intspanset_shift_scale, adjacent_intspanset_int, contains_intspanset_int, \ @@ -417,7 +417,7 @@ def __mul__(self, other): """ return self.intersection(other) - def minus(self, other: Time) -> IntSpanSet: + def minus(self, other: Union[int, IntSpan, IntSpanSet]) -> IntSpanSet: """ Returns the difference of ``self`` and ``other``. @@ -452,7 +452,7 @@ def __sub__(self, other): """ return self.minus(other) - def union(self, other: Time) -> IntSpanSet: + def union(self, other: Union[int, IntSpan, IntSpanSet]) -> IntSpanSet: """ Returns the union of ``self`` and ``other``. diff --git a/pymeos/pymeos/collections/time/periodset.py b/pymeos/pymeos/collections/time/periodset.py index 034ea886..45147c93 100644 --- a/pymeos/pymeos/collections/time/periodset.py +++ b/pymeos/pymeos/collections/time/periodset.py @@ -713,7 +713,6 @@ def intersection(self, other: Time) -> Union[PeriodSet, datetime, TimestampSet]: intersection_periodset_timestamp, intersection_spanset_spanset, intersection_spanset_span """ - from .period import Period if isinstance(other, datetime): result = intersection_periodset_timestamp(self._inner, datetime_to_timestamptz(other)) diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index e413efb2..54c29404 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -5,7 +5,6 @@ from typing import Optional, List, Union, TYPE_CHECKING, Set, overload from pymeos_cffi import * -from spans.types import floatrange, intrange from .tnumber import TNumber from ..temporal import TInterpolation, Temporal, TInstant, TSequence, TSequenceSet @@ -668,8 +667,8 @@ def temporal_greater(self, other: Union[int, float, Temporal]) -> Temporal: return Temporal._factory(result) # ------------------------- Restrictions ---------------------------------- - def at(self, other: Union[int, float, List[int], List[float], - floatrange, List[floatrange], TBox, Time]) -> TFloat: + def at(self, other: Union[float, int, FloatSet, IntSet, FloatSpan, IntSpan, \ + FloatSpanSet, IntSpanSet, TBox, Time]) -> TFloat: """ Returns a new temporal float with the values of `self` restricted to the value or time `other`. @@ -681,26 +680,24 @@ def at(self, other: Union[int, float, List[int], List[float], A new temporal float. MEOS Functions: - tfloat_at_value, tfloat_at_values, tfloat_at_span, tfloat_at_spanset, + tfloat_at_value, temporal_at_values, tnumber_at_span, tnumber_at_spanset, temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, temporal_at_periodset """ if isinstance(other, int) or isinstance(other, float): result = tfloat_at_value(self._inner, float(other)) - elif isinstance(other, floatrange): - result = tnumber_at_span(self._inner, floatrange_to_floatspan(other)) - elif isinstance(other, list) and (isinstance(other[0], int) or \ - isinstance(other[0], float)): - result = temporal_at_values(self._inner, floatset_make(other)) - # elif isinstance(other, list) and (isinstance(other[0], floatrange) or isinstance(other[0], intrange)): - # results = [tnumber_at_span(self._inner, value) for value in other if other is not None] - # result = temporal_merge_array(results, len(results)) + elif isinstance(other, IntSet): + return super().at(other.to_floatset()) + elif isinstance(other, IntSpan): + return super().at(other.to_floatspan()) + elif isinstance(other, IntSpanSet): + return super().at(other.to_floatspanset()) else: return super().at(other) return Temporal._factory(result) - def minus(self, other: Union[int, float, List[int], List[float], - floatrange, List[floatrange], TBox, Time]) -> Temporal: + def minus(self, other: Union[float, int, FloatSet, IntSet, FloatSpan, IntSpan, \ + FloatSpanSet, IntSpanSet, TBox, Time]) -> Temporal: """ Returns a new temporal float with the values of `self` restricted to the complement of the time or value `other`. @@ -712,18 +709,18 @@ def minus(self, other: Union[int, float, List[int], List[float], A new temporal float. MEOS Functions: - tfloat_minus_value, tnumber_minus_span, + tfloat_minus_value, temporal_minus_values, tnumber_minus_span, tnumber_minus_spanset, temporal_minus_timestamp, temporal_minus_timestampset, temporal_minus_period, temporal_minus_periodset """ if isinstance(other, int) or isinstance(other, float): result = tfloat_minus_value(self._inner, float(other)) - elif isinstance(other, floatrange): - result = tnumber_minus_span(self._inner, - floatrange_to_floatspan(other)) - elif isinstance(other, list) and isinstance(other[0], float): - result = temporal_minus_values(self._inner, - floatset_make(other)) + elif isinstance(other, IntSet): + return super().minus(other.to_floatset()) + elif isinstance(other, IntSpan): + return super().minus(other.to_floatspan()) + elif isinstance(other, IntSpanSet): + return super().minus(other.to_floatspanset()) else: return super().minus(other) return Temporal._factory(result) @@ -827,11 +824,11 @@ def value_split(self, size: float, start: Optional[float] = 0) -> \ return [_TemporalFactory.create_temporal(fragments[i]) for i in \ range(count)] - def value_time_split(self, value_size: float, + def value_time_split(self, value_size: float, duration: Union[str, timedelta], value_start: Optional[float] = 0.0, time_start: Optional[Union[str, datetime]] = None) -> \ - List[Temporal]: + List[Temporal]: """ Splits `self` into fragments with respect to value and period buckets. @@ -859,7 +856,7 @@ def value_time_split(self, value_size: float, if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) fragments, _, _, count = tfloat_value_time_split(self._inner, - value_size, dt, value_start, st) + value_size, dt, value_start, st) return [Temporal._factory(fragments[i]) for i in range(count)] # ------------------------- Database Operations --------------------------- diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index b46c8676..0dfdda4e 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -1,14 +1,13 @@ from __future__ import annotations from abc import ABC -from functools import reduce from typing import Optional, Union, List, TYPE_CHECKING, Set, overload from pymeos_cffi import * from .tnumber import TNumber -from ..temporal import TInterpolation, Temporal, TInstant, TSequence, TSequenceSet from ..collections import * +from ..temporal import TInterpolation, Temporal, TInstant, TSequence, TSequenceSet if TYPE_CHECKING: from ..boxes import TBox @@ -653,9 +652,8 @@ def temporal_greater(self, other: Union[int, Temporal]) -> Temporal: return Temporal._factory(result) # ------------------------- Restrictions ---------------------------------- - def at(self, other: Union[int, List[int], - intrange, floatrange, List[intrange], List[floatrange], TBox, - datetime, TimestampSet, Period, PeriodSet]) -> Temporal: + def at(self, other: Union[int, float, IntSet, FloatSet, IntSpan, FloatSpan, \ + IntSpanSet, FloatSpanSet, TBox, Time]) -> Temporal: """ Returns a new temporal int with th e values of `self` restricted to the time or value `other`. @@ -667,20 +665,24 @@ def at(self, other: Union[int, List[int], A new temporal int. MEOS Functions: - tint_at_value, temporal_at_timestamp, temporal_at_timestampset, - temporal_at_period, temporal_at_periodset + tint_at_value, temporal_at_values, tnumber_at_span, tnumber_at_spanset, + temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, + temporal_at_periodset """ - if isinstance(other, int): - result = tint_at_value(self._inner, other) - elif isinstance(other, list) and isinstance(other[0], int): - result = temporal_at_values(self._inner, intset_make(other)) + if isinstance(other, int) or isinstance(other, float): + result = tint_at_value(self._inner, int(other)) + elif isinstance(other, FloatSet): + return super().at(other.to_intset()) + elif isinstance(other, FloatSpan): + return super().at(other.to_intspan()) + elif isinstance(other, FloatSpanSet): + return super().at(other.to_intspanset()) else: return super().at(other) return Temporal._factory(result) - def minus(self, other: Union[int, List[int], - intrange, floatrange, List[intrange], List[floatrange], TBox, - datetime, TimestampSet, Period, PeriodSet]) -> Temporal: + def minus(self, other: Union[int, float, IntSet, FloatSet, IntSpan, FloatSpan, \ + IntSpanSet, FloatSpanSet, TBox, Time]) -> Temporal: """ Returns a new temporal int with the values of `self` restricted to the complement of the time or value `other`. @@ -692,14 +694,18 @@ def minus(self, other: Union[int, List[int], A new temporal int. MEOS Functions: - tint_minus_value, temporal_minus_timestamp, - temporal_minus_timestampset, temporal_minus_period, - temporal_minus_periodset + tint_minus_value, temporal_minus_values, tnumber_minus_span, tnumber_minus_spanset, + temporal_minus_timestamp, temporal_minus_timestampset, + temporal_minus_period, temporal_minus_periodset """ - if isinstance(other, int): - result = tint_minus_value(self._inner, other) - elif isinstance(other, list) and isinstance(other[0], int): - result = temporal_minus_values(self._inner, intset_make(other)) + if isinstance(other, int) or isinstance(other, float): + result = tint_minus_value(self._inner, int(other)) + elif isinstance(other, FloatSet): + return super().minus(other.to_intset()) + elif isinstance(other, FloatSpan): + return super().minus(other.to_intspan()) + elif isinstance(other, FloatSpanSet): + return super().minus(other.to_intspanset()) else: return super().minus(other) return Temporal._factory(result) @@ -749,9 +755,9 @@ def value_split(self, size: int, start: Optional[int] = 0) -> List[TInt]: def value_time_split(self, value_size: int, duration: Union[str, timedelta], - value_start: Optional[int] = 0, + value_start: Optional[int] = 0, time_start: Optional[Union[str, datetime]] = None) -> \ - List[TInt]: + List[TInt]: """ Splits `self` into fragments with respect to value and period buckets. @@ -779,7 +785,7 @@ def value_time_split(self, value_size: int, if isinstance(duration, timedelta) \ else pg_interval_in(duration, -1) tiles, _, _, count = tint_value_time_split(self._inner, value_size, dt, - value_start, st) + value_start, st) return [Temporal._factory(tiles[i]) for i in range(count)] # ------------------------- Database Operations --------------------------- diff --git a/pymeos/pymeos/main/tnumber.py b/pymeos/pymeos/main/tnumber.py index 4064d7eb..e90dc925 100644 --- a/pymeos/pymeos/main/tnumber.py +++ b/pymeos/pymeos/main/tnumber.py @@ -1,11 +1,11 @@ from __future__ import annotations from abc import ABC -from typing import Union, List, TYPE_CHECKING, TypeVar +from typing import Union, TYPE_CHECKING, TypeVar from pymeos_cffi import * -from spans import intrange, floatrange +from ..collections import IntSet, FloatSet from ..temporal import Temporal if TYPE_CHECKING: @@ -19,6 +19,7 @@ TI = TypeVar('TI', 'TInstant[int]', 'TInstant[float]') TS = TypeVar('TS', 'TSequence[int]', 'TSequence[float]') TSS = TypeVar('TSS', 'TSequenceSet[int]', 'TSequenceSet[float]') +Self = TypeVar('Self', bound='TNumber[Any]') class TNumber(Temporal[TBase, TG, TI, TS, TSS], ABC): @@ -62,7 +63,7 @@ def time_weighted_average(self) -> float: return tnumber_twavg(self._inner) # ------------------------- Transformations ------------------------------- - def shift_value(self, delta: Union[int, float]) -> Self: + def shift_value(self: Self, delta: Union[int, float]) -> Self: """ Returns a new :class:`TNumber` with the value dimension shifted by ``delta``. @@ -83,7 +84,7 @@ def shift_value(self, delta: Union[int, float]) -> Self: raise TypeError(f'Operation not supported with type {self.__class__}') return Temporal._factory(shifted) - def scale_value(self, width: Union[int, float]) -> Self: + def scale_value(self: Self, width: Union[int, float]) -> Self: """ Returns a new :class:`TNumber` scaled so the value dimension has width ``width``. @@ -104,8 +105,8 @@ def scale_value(self, width: Union[int, float]) -> Self: raise TypeError(f'Operation not supported with type {self.__class__}') return Temporal._factory(scaled) - def shift_scale_value(self, shift: Union[int, float] = None, - width: Union[int, float] = None) -> Self: + def shift_scale_value(self: Self, shift: Union[int, float] = None, + width: Union[int, float] = None) -> Self: """ Returns a new :class:`TNumber` with the value dimension shifted by ``shift`` and scaled so the value dimension has width ``width``. @@ -123,18 +124,18 @@ def shift_scale_value(self, shift: Union[int, float] = None, 'shift and width must not be both None' if isinstance(self, TInt): - scaled = tint_shift_scale_value(self._inner, - int(shift) if shift else None, int(width) if width else None) + scaled = tint_shift_scale_value(self._inner, + int(shift) if shift else None, int(width) if width else None) elif isinstance(self, TFloat): - scaled = tfloat_shift_scale_value(self._inner, - float(shift) if shift else None, float(width) if width else None) + scaled = tfloat_shift_scale_value(self._inner, + float(shift) if shift else None, float(width) if width else None) else: raise TypeError(f'Operation not supported with type {self.__class__}') return Temporal._factory(scaled) # ------------------------- Restrictions ---------------------------------- - def at(self, other: Union[IntSpan, FloatSpan, IntSpanSet, FloatSpanSet, - TBox, Time]) -> TG: + def at(self, other: Union[IntSet, FloatSet, IntSpan, FloatSpan, IntSpanSet, FloatSpanSet, + TBox, Time]) -> TG: """ Returns a new temporal object with the values of `self` restricted to the value or time `other`. @@ -146,19 +147,17 @@ def at(self, other: Union[IntSpan, FloatSpan, IntSpanSet, FloatSpanSet, A new temporal object of the same subtype as `self`. MEOS Functions: - tnumber_at_span, tnumber_at_spanset, tnumber_at_tbox, + temporal_at_values, tnumber_at_span, tnumber_at_spanset, tnumber_at_tbox, temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, temporal_at_periodset """ from ..boxes import TBox - from ..collections import IntSpan, FloatSpan, IntSpanSet, FloatSpanSet - if isinstance(other, IntSpan): + from ..collections import IntSet, FloatSet, IntSpan, FloatSpan, IntSpanSet, FloatSpanSet + if isinstance(other, IntSet) or isinstance(other, FloatSet): + result = temporal_at_values(self._inner, other._inner) + elif isinstance(other, IntSpan) or isinstance(other, FloatSpan): result = tnumber_at_span(self._inner, other._inner) - elif isinstance(other, FloatSpan): - result = tnumber_at_span(self._inner, other._inner) - elif isinstance(other, IntSpanSet): - result = tnumber_at_spanset(self._inner, other._inner) - elif isinstance(other, FloatSpanSet): + elif isinstance(other, IntSpanSet) or isinstance(other, FloatSpanSet): result = tnumber_at_spanset(self._inner, other._inner) elif isinstance(other, TBox): result = tnumber_at_tbox(self._inner, other._inner) @@ -166,8 +165,8 @@ def at(self, other: Union[IntSpan, FloatSpan, IntSpanSet, FloatSpanSet, return super().at(other) return Temporal._factory(result) - def minus(self, other: Union[IntSpan, FloatSpan, IntSpanSet, FloatSpanSet, - TBox, Time]) -> TG: + def minus(self, other: Union[IntSet, FloatSet, IntSpan, FloatSpan, IntSpanSet, FloatSpanSet, + TBox, Time]) -> TG: """ Returns a new temporal object with the values of `self` restricted to the complement of the value or time `other`. @@ -180,19 +179,17 @@ def minus(self, other: Union[IntSpan, FloatSpan, IntSpanSet, FloatSpanSet, A new temporal object of the same subtype as `self`. MEOS Functions: - tnumber_minus_span, tnumber_minus_spanset, tnumber_minus_tbox, + temporal_minus_values, tnumber_minus_span, tnumber_minus_spanset, tnumber_minus_tbox, temporal_minus_timestamp, temporal_minus_timestampset, temporal_minus_period, temporal_minus_periodset """ from ..boxes import TBox - from ..collections import IntSpan, FloatSpan, IntSpanSet, FloatSpanSet - if isinstance(other, IntSpan): - result = tnumber_minus_span(self._inner, other._inner) - elif isinstance(other, FloatSpan): + from ..collections import IntSet, FloatSet, IntSpan, FloatSpan, IntSpanSet, FloatSpanSet + if isinstance(other, IntSet) or isinstance(other, FloatSet): + result = temporal_minus_values(self._inner, other._inner) + elif isinstance(other, IntSpan) or isinstance(other, FloatSpan): result = tnumber_minus_span(self._inner, other._inner) - elif isinstance(other, IntSpanSet): - result = tnumber_minus_spanset(self._inner, other._inner) - elif isinstance(other, FloatSpanSet): + elif isinstance(other, IntSpanSet) or isinstance(other, FloatSpanSet): result = tnumber_minus_spanset(self._inner, other._inner) elif isinstance(other, TBox): result = tnumber_minus_tbox(self._inner, other._inner) @@ -640,7 +637,7 @@ def distance(self, other: Union[int, float, TNumber]) -> TFloat: return Temporal._factory(result) def nearest_approach_distance(self, other: Union[int, float, - TNumber, TBox]) -> float: + TNumber, TBox]) -> float: """ Returns the nearest approach distance between `self` and `other`. @@ -665,4 +662,3 @@ def nearest_approach_distance(self, other: Union[int, float, return nad_tnumber_tbox(self._inner, other._inner) else: raise TypeError(f'Operation not supported with type {other.__class__}') - diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 96fd79ed..6b510316 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -11,7 +11,7 @@ from pymeos_cffi import * from .tbool import TBool -from .tfloat import TFloat, TFloatSeqSet +from .tfloat import TFloat, TFloatInst, TFloatSeq, TFloatSeqSet from ..temporal import Temporal, TInstant, TSequence, TSequenceSet, TInterpolation from ..collections import * @@ -23,6 +23,7 @@ TS = TypeVar('TS', bound='TPointSeq') TSS = TypeVar('TSS', bound='TPointSeqSet') Self = TypeVar('Self', bound='TPoint') +TF = TypeVar('TF', bound='TFloat', covariant=True) class TPoint(Temporal[shp.Point, TG, TI, TS, TSS], ABC): @@ -219,7 +220,7 @@ def speed(self) -> TFloat: result = tpoint_speed(self._inner) return Temporal._factory(result) - def x(self) -> TFloat: + def x(self) -> TF: """ Returns the x coordinate of the temporal point. @@ -232,7 +233,7 @@ def x(self) -> TFloat: result = tpoint_get_x(self._inner) return Temporal._factory(result) - def y(self) -> TFloat: + def y(self) -> TF: """ Returns the y coordinate of the temporal point. @@ -245,7 +246,7 @@ def y(self) -> TFloat: result = tpoint_get_y(self._inner) return Temporal._factory(result) - def z(self) -> TFloat: + def z(self) -> TF: """ Returns the z coordinate of the temporal point. @@ -1173,44 +1174,29 @@ def value(self, precision: int = 15) -> shp.Point: """ return self.start_value(precision=precision) + def x(self) -> TFloatInst: + return super().x() + + def y(self) -> TFloatInst: + return super().y() + + def z(self) -> TFloatInst: + return super().z() + class TPointSeq(TSequence[shpb.BaseGeometry, TG, TI, TS, TSS], TPoint[TG, TI, TS, TSS], ABC): """ Abstract class for temporal point sequences. """ - # @staticmethod - # def from_arrays(t: List[Union[datetime, str]], x: List[float], y: List[float], z: Optional[List[float]] = None, - # srid: int = 0, geodetic: bool = False, lower_inc: bool = True, upper_inc: bool = False, - # interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True) -> TPointSeq: - # """ - # Creates a temporal point sequence from arrays of timestamps and coordinates. - # - # Args: - # t: The array of timestamps. - # x: The array of x coordinates. - # y: The array of y coordinates. - # z: The array of z coordinates. - # srid: The spatial reference system identifier. - # geodetic: Whether the coordinates are geodetic. - # lower_inc: Whether the lower bound is inclusive. - # upper_inc: Whether the upper bound is inclusive. - # interpolation: The interpolation method. - # normalize: Whether to normalize the timestamps. - # - # Returns: - # A new :class:`TPointSeq` object. - # - # MEOS Functions: - # tpointseq_make_coords - # """ - # from ..factory import _TemporalFactory - # assert len(t) == len(x) == len(y) - # times = [datetime_to_timestamptz(ti) if isinstance(ti, datetime) else pg_timestamptz_in(ti, -1) for ti in t] - # return _TemporalFactory.create_temporal( - # tpointseq_make_coords(x, y, z, times, len(t), srid, geodetic, lower_inc, upper_inc, interpolation, - # normalize) - # ) + def x(self) -> TFloatSeq: + return super().x() + + def y(self) -> TFloatSeq: + return super().y() + + def z(self) -> TFloatSeq: + return super().z() def plot(self, *args, **kwargs): """ @@ -1256,6 +1242,15 @@ def to_dataframe(self, precision: int = 15) -> GeoDataFrame: } return GeoDataFrame(data, crs=self.srid()).set_index(keys=['sequence', 'time']) + def x(self) -> TFloatSeqSet: + return super().x() + + def y(self) -> TFloatSeqSet: + return super().y() + + def z(self) -> TFloatSeqSet: + return super().z() + def plot(self, *args, **kwargs): """ Plots the temporal point sequence set. diff --git a/pymeos/pymeos/plotters/box_plotter.py b/pymeos/pymeos/plotters/box_plotter.py index 392083f3..6c0f262d 100644 --- a/pymeos/pymeos/plotters/box_plotter.py +++ b/pymeos/pymeos/plotters/box_plotter.py @@ -30,7 +30,7 @@ def plot_tbox(tbox: TBox, *args, axes=None, **kwargs): :func:`~pymeos.plotters.time_plotter.TimePlotter.plot_period` """ if not tbox.has_t: - return SpanPlotter.plot_range(tbox.to_floatrange(), *args, axes=axes, **kwargs) + return SpanPlotter.plot_range(tbox.to_floatspan(), *args, axes=axes, **kwargs) if not tbox.has_x: return TimePlotter.plot_period(tbox.to_period(), *args, axes=axes, **kwargs) return BoxPlotter._plot_box(tbox.tmin(), tbox.tmax(), tbox.xmin(), tbox.xmax(), *args, axes=axes, **kwargs) diff --git a/pymeos/pymeos/plotters/point_sequence_plotter.py b/pymeos/pymeos/plotters/point_sequence_plotter.py index 20e895a2..68286a47 100644 --- a/pymeos/pymeos/plotters/point_sequence_plotter.py +++ b/pymeos/pymeos/plotters/point_sequence_plotter.py @@ -40,7 +40,7 @@ def plot_xy(sequence: Union[TPointSeq, List[TPointInst]], *args, axes=None, show else: plot_func = base.scatter - ins = sequence.instants() if isinstance(sequence, TPointSeq) else sequence + ins : list[TPointInst] = sequence.instants() if isinstance(sequence, TPointSeq) else sequence x = [i.x().value() for i in ins] y = [i.y().value() for i in ins] diff --git a/pymeos/pyproject.toml b/pymeos/pyproject.toml index 83ebd4d3..62034919 100644 --- a/pymeos/pyproject.toml +++ b/pymeos/pyproject.toml @@ -36,7 +36,6 @@ requires-python = '>=3.7' dependencies = [ 'pymeos-cffi >=1.1.0a4', 'python-dateutil', - 'spans', 'postgis', 'shapely', 'geopandas' diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index 500d3c76..eb257423 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -1,15 +1,14 @@ from copy import copy -from operator import not_ from datetime import datetime, timezone, timedelta +from operator import not_ import pytest -from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, \ +from pymeos import TBoolInst, TBoolSeq, TBoolSeqSet, \ TFloat, TFloatInst, TFloatSeq, TFloatSeqSet, \ TInt, TIntInst, TIntSeq, TIntSeqSet, \ TInterpolation, TBox, TimestampSet, Period, PeriodSet, \ - IntSpan, FloatSpan, IntSpanSet, FloatSpanSet - + FloatSpan, FloatSpanSet, FloatSet from tests.conftest import TestPyMEOS @@ -66,7 +65,7 @@ def test_from_base_time_constructor(self, source, type, interpolation): '[1.5@2019-09-01 00:00:00+00, 2.5@2019-09-02 00:00:00+00]'), ('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}', TFloatSeqSet, TInterpolation.LINEAR, '{[1.5@2019-09-01 00:00:00+00, 2.5@2019-09-02 00:00:00+00], ' - '[1.5@2019-09-03 00:00:00+00, 1.5@2019-09-05 00:00:00+00]}'), + '[1.5@2019-09-03 00:00:00+00, 1.5@2019-09-05 00:00:00+00]}'), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -186,7 +185,7 @@ class TestTFloatOutputs(TestTFloat): '[1.5@2019-09-03 00:00:00+00, 1.5@2019-09-05 00:00:00+00]}'), (tfsts, 'Interp=Step;[1.5@2019-09-01 00:00:00+00, 2.5@2019-09-02 00:00:00+00]'), (tfstss, 'Interp=Step;{[1.5@2019-09-01 00:00:00+00, 2.5@2019-09-02 00:00:00+00], ' - '[1.5@2019-09-03 00:00:00+00, 1.5@2019-09-05 00:00:00+00]}') + '[1.5@2019-09-03 00:00:00+00, 1.5@2019-09-05 00:00:00+00]}') ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet', 'Stepwise Sequence', 'Stepwise SequenceSet'] @@ -229,7 +228,7 @@ def test_as_wkt(self, temporal, expected): (tfds, '011B00060200000003000000000000F83F00A01E4E7134020000000000000004400000F66B85340200'), (tfs, '011B000E0200000003000000000000F83F00A01E4E7134020000000000000004400000F66B85340200'), (tfss, '011B000F020000000200000003000000000000F83F00A01E4E7134020000000000000004400000F66B85340200' - '0200000003000000000000F83F0060CD8999340200000000000000F83F00207CC5C1340200') + '0200000003000000000000F83F0060CD8999340200000000000000F83F00207CC5C1340200') ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -452,17 +451,17 @@ def test_value_span(self, temporal, expected): assert temporal.value_span() == expected # @pytest.mark.parametrize( - # 'temporal, expected', - # [ - # (tfi, FloatSpanSet('{[1.5, 1.5]}')), - # (tfds, FloatSpanSet('{[1.5, 1.5], [2.5, 2.5]}')), - # (tfs, FloatSpanSet('{[1.5, 1.5]}')), - # (tfss, FloatSpanSet('{[1.5, 1.5]}')), - # ], - # ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + # 'temporal, expected', + # [ + # (tfi, FloatSpanSet('{[1.5, 1.5]}')), + # (tfds, FloatSpanSet('{[1.5, 1.5], [2.5, 2.5]}')), + # (tfs, FloatSpanSet('{[1.5, 1.5]}')), + # (tfss, FloatSpanSet('{[1.5, 1.5]}')), + # ], + # ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] # ) # def test_value_spans(self, temporal, expected): - # assert temporal.value_spans() == expected + # assert temporal.value_spans() == expected @pytest.mark.parametrize( 'temporal, expected', @@ -761,12 +760,12 @@ def test_timestamps(self, temporal, expected): (tfds, [TFloatSeq('[1.5@2019-09-01]'), TFloatSeq('[2.5@2019-09-02]')]), (tfs, [TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]')]), (tfss, [TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]'), - TFloatSeq('[1.5@2019-09-03, 1.5@2019-09-05]')]), + TFloatSeq('[1.5@2019-09-03, 1.5@2019-09-05]')]), (tfsts, [TFloatSeq('Interp=Step;[1.5@2019-09-01, 1.5@2019-09-02)'), - TFloatSeq('Interp=Step;[2.5@2019-09-02]')]), + TFloatSeq('Interp=Step;[2.5@2019-09-02]')]), (tfstss, [TFloatSeq('Interp=Step;[1.5@2019-09-01, 1.5@2019-09-02)'), - TFloatSeq('Interp=Step;[2.5@2019-09-02]'), - TFloatSeq('Interp=Step;[1.5@2019-09-03, 1.5@2019-09-05]')]), + TFloatSeq('Interp=Step;[2.5@2019-09-02]'), + TFloatSeq('Interp=Step;[1.5@2019-09-03, 1.5@2019-09-05]')]), ], ids=['Discrete Sequence', 'Sequence', 'SequenceSet', 'Stepwise Sequence', 'Stepwise SequenceSet'] ) @@ -807,15 +806,15 @@ def test_lower_upper_inc(self, temporal, expected): assert temporal.upper_inc() == expected def test_sequenceset_sequence_functions(self): - tfss1 =TFloatSeqSet('{[1@2019-09-01, 2@2019-09-02],' - '[1@2019-09-03, 1@2019-09-05], [3@2019-09-06]}') + tfss1 = TFloatSeqSet('{[1@2019-09-01, 2@2019-09-02],' + '[1@2019-09-03, 1@2019-09-05], [3@2019-09-06]}') assert tfss1.num_sequences() == 3 assert tfss1.start_sequence() == TFloatSeq('[1@2019-09-01, 2@2019-09-02]') assert tfss1.end_sequence() == TFloatSeq('[3@2019-09-06]') assert tfss1.sequence_n(1) == TFloatSeq('[1@2019-09-03, 1@2019-09-05]') assert tfss1.sequences() == [TFloatSeq('[1@2019-09-01, 2@2019-09-02]'), - TFloatSeq('[1@2019-09-03, 1@2019-09-05]'), - TFloatSeq('[3@2019-09-06]')] + TFloatSeq('[1@2019-09-03, 1@2019-09-05]'), + TFloatSeq('[3@2019-09-06]')] @pytest.mark.parametrize( 'temporal, expected', @@ -856,10 +855,10 @@ class TestTFloatTransformations(TestTFloat): tfss_d = TFloatSeqSet('{[1.5@2019-09-01],[2.5@2019-09-03]}') tfs_s = TFloatSeq('[1.5@2019-09-01, 1.5@2019-09-02]') tfss_s = TFloatSeqSet('{[1.5@2019-09-01, 1.5@2019-09-02],' - '[2.5@2019-09-03, 2.5@2019-09-05]}') + '[2.5@2019-09-03, 2.5@2019-09-05]}') tfs_l = TFloatSeq('Interp=Step;[1.5@2019-09-01, 2.5@2019-09-02]') tfss_l = TFloatSeqSet('Interp=Step;{[1.5@2019-09-01, 2.5@2019-09-02],' - '[1.5@2019-09-03, 1.5@2019-09-05]}') + '[1.5@2019-09-03, 1.5@2019-09-05]}') @pytest.mark.parametrize( 'temporal, expected', @@ -880,16 +879,16 @@ def test_to_instant(self, temporal, expected): 'temporal, interpolation, expected', [ (TFloatInst('1.5@2019-09-01'), TInterpolation.LINEAR, - TFloatSeq('[1.5@2019-09-01]')), + TFloatSeq('[1.5@2019-09-01]')), (TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}'), - TInterpolation.DISCRETE, - TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}')), + TInterpolation.DISCRETE, + TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}')), (TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]'), - TInterpolation.LINEAR, - TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]')), + TInterpolation.LINEAR, + TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]')), (TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}'), - TInterpolation.LINEAR, - TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]')), + TInterpolation.LINEAR, + TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -902,16 +901,16 @@ def test_to_sequence(self, temporal, interpolation, expected): 'temporal, interpolation, expected', [ (TFloatInst('1.5@2019-09-01'), TInterpolation.LINEAR, - TFloatSeqSet('{[1.5@2019-09-01]}')), + TFloatSeqSet('{[1.5@2019-09-01]}')), (TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02}'), - TInterpolation.LINEAR, - TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02]}')), + TInterpolation.LINEAR, + TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02]}')), (TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02]'), - TInterpolation.LINEAR, - TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}')), + TInterpolation.LINEAR, + TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}')), (TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}'), - TInterpolation.LINEAR, - TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}')), + TInterpolation.LINEAR, + TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -924,32 +923,32 @@ def test_to_sequenceset(self, temporal, interpolation, expected): 'temporal, interpolation, expected', [ (tfi, TInterpolation.DISCRETE, - TFloatSeq('{1.5@2019-09-01}')), + TFloatSeq('{1.5@2019-09-01}')), (tfds, TInterpolation.DISCRETE, tfds), (tfs_d, TInterpolation.DISCRETE, - TFloatSeq('{1.5@2019-09-01}')), + TFloatSeq('{1.5@2019-09-01}')), (tfss_d, TInterpolation.DISCRETE, - TFloatSeq('{1.5@2019-09-01,2.5@2019-09-03}')), + TFloatSeq('{1.5@2019-09-01,2.5@2019-09-03}')), - (tfi, TInterpolation.STEPWISE, - TFloatSeq('Interp=Step;[1.5@2019-09-01]')), - (tfds, TInterpolation.STEPWISE, - TFloatSeqSet('Interp=Step;{[1.5@2019-09-01], [2.5@2019-09-02]}')), + (tfi, TInterpolation.STEPWISE, + TFloatSeq('Interp=Step;[1.5@2019-09-01]')), + (tfds, TInterpolation.STEPWISE, + TFloatSeqSet('Interp=Step;{[1.5@2019-09-01], [2.5@2019-09-02]}')), (tfs_s, TInterpolation.STEPWISE, - TFloatSeq('Interp=Step;[1.5@2019-09-01, 1.5@2019-09-02]')), + TFloatSeq('Interp=Step;[1.5@2019-09-01, 1.5@2019-09-02]')), (tfss_s, TInterpolation.STEPWISE, - TFloatSeqSet('Interp=Step;{[1.5@2019-09-01, 1.5@2019-09-02],' - '[2.5@2019-09-03, 2.5@2019-09-05]}')), + TFloatSeqSet('Interp=Step;{[1.5@2019-09-01, 1.5@2019-09-02],' + '[2.5@2019-09-03, 2.5@2019-09-05]}')), - (tfi, TInterpolation.LINEAR, - TFloatSeq('[1.5@2019-09-01]')), - (tfds, TInterpolation.LINEAR, - TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02]}')), + (tfi, TInterpolation.LINEAR, + TFloatSeq('[1.5@2019-09-01]')), + (tfds, TInterpolation.LINEAR, + TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02]}')), (tfs_l, TInterpolation.LINEAR, - TFloatSeqSet('{[1.5@2019-09-01, 1.5@2019-09-02), [2.5@2019-09-02]}')), + TFloatSeqSet('{[1.5@2019-09-01, 1.5@2019-09-02), [2.5@2019-09-02]}')), (tfss_l, TInterpolation.LINEAR, - TFloatSeqSet('{[1.5@2019-09-01, 1.5@2019-09-02),' - '[2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), + TFloatSeqSet('{[1.5@2019-09-01, 1.5@2019-09-02),' + '[2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), ], ids=['Instant to discrete', 'Discrete Sequence to discrete', 'Sequence to discrete', 'SequenceSet to discrete', 'Instant to step', 'Discrete Sequence to step', 'Sequence to step', 'SequenceSet to step', @@ -967,15 +966,15 @@ def test_set_interpolation(self, temporal, interpolation, expected): (tfs, 2, TFloatSeq('[3.5@2019-09-01, 4.5@2019-09-02]')), (tfs, -2, TFloatSeq('[-0.5@2019-09-01, 0.5@2019-09-02]')), (tfss, 2, TFloatSeqSet('{[3.5@2019-09-01, 4.5@2019-09-02],' - '[3.5@2019-09-03, 3.5@2019-09-05]}')), + '[3.5@2019-09-03, 3.5@2019-09-05]}')), (tfss, -2, TFloatSeqSet('{[-0.5@2019-09-01, 0.5@2019-09-02],' - '[-0.5@2019-09-03, -0.5@2019-09-05]}')), + '[-0.5@2019-09-03, -0.5@2019-09-05]}')), ], ids=['Instant positive', 'Instant negative', - 'Discrete Sequence positive', 'Discrete Sequence negative', - 'Sequence positive', 'Sequence negative', + 'Discrete Sequence positive', 'Discrete Sequence negative', + 'Sequence positive', 'Sequence negative', 'Sequence Set positive', 'Sequence Set negative', - ] + ] ) def test_shift_value(self, tfloat, delta, expected): assert tfloat.shift_value(delta) == expected @@ -986,9 +985,9 @@ def test_shift_value(self, tfloat, delta, expected): (tfds, 4, TFloatSeq('{1.5@2019-09-01, 5.5@2019-09-02}')), (tfs, 4, TFloatSeq('[1.5@2019-09-01, 5.5@2019-09-02]')), (tfss, 4, TFloatSeqSet('{[1.5@2019-09-01, 5.5@2019-09-02],' - '[1.5@2019-09-03, 1.5@2019-09-05]}')), + '[1.5@2019-09-03, 1.5@2019-09-05]}')), ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set',] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set', ] ) def test_scale_value(self, tfloat, width, expected): assert tfloat.scale_value(width) == expected @@ -1002,15 +1001,15 @@ def test_scale_value(self, tfloat, width, expected): (tfs, 2, 3, TFloatSeq('[3.5@2019-09-01, 6.5@2019-09-02]')), (tfs, -2, 3, TFloatSeq('[-0.5@2019-09-01, 2.5@2019-09-02]')), (tfss, 2, 3, TFloatSeqSet('{[3.5@2019-09-01, 6.5@2019-09-02],' - '[3.5@2019-09-03, 3.5@2019-09-05]}')), + '[3.5@2019-09-03, 3.5@2019-09-05]}')), (tfss, -2, 3, TFloatSeqSet('{[-0.5@2019-09-01, 2.5@2019-09-02],' - '[-0.5@2019-09-03, -0.5@2019-09-05]}')), + '[-0.5@2019-09-03, -0.5@2019-09-05]}')), ], ids=['Instant positive', 'Instant negative', - 'Discrete Sequence positive', 'Discrete Sequence negative', - 'Sequence positive', 'Sequence negative', + 'Discrete Sequence positive', 'Discrete Sequence negative', + 'Sequence positive', 'Sequence negative', 'Sequence Set positive', 'Sequence Set negative', - ] + ] ) def test_shift_scale_value(self, tfloat, delta, width, expected): assert tfloat.shift_scale_value(delta, width) == expected @@ -1020,7 +1019,7 @@ def test_shift_scale_value(self, tfloat, delta, width, expected): [(tfi, timedelta(days=4), TFloatInst('1.5@2019-09-05')), (tfi, timedelta(days=-4), TFloatInst('1.5@2019-08-28')), (tfi, timedelta(hours=2), TFloatInst('1.5@2019-09-01 02:00:00')), - (tfi, timedelta(hours=-2), TFloatInst('1.5@2019-08-31 22:00:00')), + (tfi, timedelta(hours=-2), TFloatInst('1.5@2019-08-31 22:00:00')), (tfds, timedelta(days=4), TFloatSeq('{1.5@2019-09-05, 2.5@2019-09-06}')), (tfds, timedelta(days=-4), TFloatSeq('{1.5@2019-08-28, 2.5@2019-08-29}')), (tfds, timedelta(hours=2), TFloatSeq('{1.5@2019-09-01 02:00:00, 2.5@2019-09-02 02:00:00}')), @@ -1030,23 +1029,23 @@ def test_shift_scale_value(self, tfloat, delta, width, expected): (tfs, timedelta(hours=2), TFloatSeq('[1.5@2019-09-01 02:00:00, 2.5@2019-09-02 02:00:00]')), (tfs, timedelta(hours=-2), TFloatSeq('[1.5@2019-08-31 22:00:00, 2.5@2019-09-01 22:00:00]')), (tfss, timedelta(days=4), - TFloatSeqSet('{[1.5@2019-09-05, 2.5@2019-09-06],[1.5@2019-09-07, 1.5@2019-09-09]}')), + TFloatSeqSet('{[1.5@2019-09-05, 2.5@2019-09-06],[1.5@2019-09-07, 1.5@2019-09-09]}')), (tfss, timedelta(days=-4), - TFloatSeqSet('{[1.5@2019-08-28, 2.5@2019-08-29],[1.5@2019-08-30, 1.5@2019-09-01]}')), + TFloatSeqSet('{[1.5@2019-08-28, 2.5@2019-08-29],[1.5@2019-08-30, 1.5@2019-09-01]}')), (tfss, timedelta(hours=2), - TFloatSeqSet('{[1.5@2019-09-01 02:00:00, 2.5@2019-09-02 02:00:00],' - '[1.5@2019-09-03 02:00:00, 1.5@2019-09-05 02:00:00]}')), + TFloatSeqSet('{[1.5@2019-09-01 02:00:00, 2.5@2019-09-02 02:00:00],' + '[1.5@2019-09-03 02:00:00, 1.5@2019-09-05 02:00:00]}')), (tfss, timedelta(hours=-2), - TFloatSeqSet('{[1.5@2019-08-31 22:00:00, 2.5@2019-09-01 22:00:00],' - '[1.5@2019-09-02 22:00:00, 1.5@2019-09-04 22:00:00]}')), + TFloatSeqSet('{[1.5@2019-08-31 22:00:00, 2.5@2019-09-01 22:00:00],' + '[1.5@2019-09-02 22:00:00, 1.5@2019-09-04 22:00:00]}')), ], ids=['Instant positive days', 'Instant negative days', 'Instant positive hours', 'Instant negative hours', - 'Discrete Sequence positive days', 'Discrete Sequence negative days', + 'Discrete Sequence positive days', 'Discrete Sequence negative days', 'Discrete Sequence positive hours', 'Discrete Sequence negative hours', - 'Sequence positive days', 'Sequence negative days', + 'Sequence positive days', 'Sequence negative days', 'Sequence positive hours', 'Sequence negative hours', - 'Sequence Set positive days', 'Sequence Set negative days', + 'Sequence Set positive days', 'Sequence Set negative days', 'Sequence Set positive hours', 'Sequence Set negative hours'] ) def test_shift_time(self, tfloat, delta, expected): @@ -1061,11 +1060,11 @@ def test_shift_time(self, tfloat, delta, expected): (tfs, timedelta(days=4), TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-05]')), (tfs, timedelta(hours=2), TFloatSeq('[1.5@2019-09-01 00:00:00, 2.5@2019-09-01 02:00:00]')), (tfss, timedelta(days=4), - TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), + TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), (tfss, timedelta(hours=2), - TFloatSeqSet('{[1.5@2019-09-01 00:00:00, 2.5@2019-09-01 00:30:00],' - '[1.5@2019-09-01 01:00:00, 1.5@2019-09-01 02:00:00]}')), - ], + TFloatSeqSet('{[1.5@2019-09-01 00:00:00, 2.5@2019-09-01 00:30:00],' + '[1.5@2019-09-01 01:00:00, 1.5@2019-09-01 02:00:00]}')), + ], ids=['Instant positive days', 'Instant positive hours', 'Discrete Sequence positive days', 'Discrete Sequence positive hours', 'Sequence positive days', 'Sequence positive hours', @@ -1076,8 +1075,8 @@ def test_scale_time(self, tfloat, delta, expected): def test_shift_scale_time(self): assert self.tfss.shift_scale_time(timedelta(days=4), timedelta(hours=2)) == \ - TFloatSeqSet('{[1.5@2019-09-05 00:00:00, 2.5@2019-09-05 00:30:00],' - '[1.5@2019-09-05 01:00:00, 1.5@2019-09-05 02:00:00]}') + TFloatSeqSet('{[1.5@2019-09-05 00:00:00, 2.5@2019-09-05 00:30:00],' + '[1.5@2019-09-05 01:00:00, 1.5@2019-09-05 02:00:00]}') @pytest.mark.parametrize( 'tfloat, delta, expected', @@ -1088,11 +1087,11 @@ def test_shift_scale_time(self): (tfs, timedelta(days=4), TFloatSeq('{1.5@2019-09-01}')), (tfs, timedelta(hours=12), TFloatSeq('{1.5@2019-09-01, 2@2019-09-01 12:00:00, 2.5@2019-09-02}')), (tfss, timedelta(days=4), - TFloatSeq('{1.5@2019-09-01,1.5@2019-09-05}')), + TFloatSeq('{1.5@2019-09-01,1.5@2019-09-05}')), (tfss, timedelta(hours=12), - TFloatSeq('{1.5@2019-09-01, 2@2019-09-01 12:00:00, 2.5@2019-09-02,' - '1.5@2019-09-03, 1.5@2019-09-03 12:00:00, 1.5@2019-09-04, ' - '1.5@2019-09-04 12:00:00, 1.5@2019-09-05}')), + TFloatSeq('{1.5@2019-09-01, 2@2019-09-01 12:00:00, 2.5@2019-09-02,' + '1.5@2019-09-03, 1.5@2019-09-03 12:00:00, 1.5@2019-09-04, ' + '1.5@2019-09-04 12:00:00, 1.5@2019-09-05}')), ], ids=['Instant days', 'Instant hours', 'Discrete Sequence days', 'Discrete Sequence hours', @@ -1111,10 +1110,10 @@ def test_temporal_sample(self, tfloat, delta, expected): (tfs, timedelta(days=4), TFloatSeq('{[2@2019-08-31]}')), (tfs, timedelta(hours=12), TFloatSeq('{[1.75@2019-09-01, 2.25@2019-09-01 12:00:00+00, 2.5@2019-09-02]}')), (tfss, timedelta(days=4), - TFloatSeq('{[1.75@2019-08-31, 1.5@2019-09-04]}')), + TFloatSeq('{[1.75@2019-08-31, 1.5@2019-09-04]}')), (tfss, timedelta(hours=12), - TFloatSeq('{[1.75@2019-09-01, 2.25@2019-09-01 12:00:00, 2.5@2019-09-02],' - '[1.5@2019-09-03, 1.5@2019-09-05]}')), + TFloatSeq('{[1.75@2019-09-01, 2.25@2019-09-01 12:00:00, 2.5@2019-09-02],' + '[1.5@2019-09-03, 1.5@2019-09-05]}')), ], ids=['Instant days', 'Instant hours', 'Discrete Sequence days', 'Discrete Sequence hours', @@ -1130,7 +1129,7 @@ def test_temporal_precision(self, tfloat, delta, expected): (tfs, timedelta(hours=12), None), (tfss, timedelta(days=4), None), (tfss, timedelta(hours=12), - TFloatSeq('[1.5@2019-09-03, 1.5@2019-09-05]')), + TFloatSeq('[1.5@2019-09-03, 1.5@2019-09-05]')), ], ids=['Sequence days', 'Sequence hours', 'Sequence Set days', 'Sequence Set hours'] @@ -1141,17 +1140,17 @@ def test_stops(self, tfloat, delta, expected): @pytest.mark.parametrize( 'temporal, expected', [ - (TFloatInst('1.123456789@2019-09-01'), - TFloatInst('1.12@2019-09-01')), + (TFloatInst('1.123456789@2019-09-01'), + TFloatInst('1.12@2019-09-01')), (TFloatSeq('{1.123456789@2019-09-01,' - '2.123456789@2019-09-02}'), - TFloatSeq('{1.12@2019-09-01, 2.12@2019-09-02}')), - (TFloatSeq('[1.123456789@2019-09-01, 2.123456789@2019-09-02]'), - TFloatSeq('[1.12@2019-09-01, 2.12@2019-09-02]')), - (TFloatSeqSet('{[1.123456789@2019-09-01, 2.123456789@2019-09-02],' - '[1.123456789@2019-09-03, 1.123456789@2019-09-05]}'), - TFloatSeq('{[1.12@2019-09-01, 2.12@2019-09-02],' - '[1.12@2019-09-03, 1.12@2019-09-05]}')), + '2.123456789@2019-09-02}'), + TFloatSeq('{1.12@2019-09-01, 2.12@2019-09-02}')), + (TFloatSeq('[1.123456789@2019-09-01, 2.123456789@2019-09-02]'), + TFloatSeq('[1.12@2019-09-01, 2.12@2019-09-02]')), + (TFloatSeqSet('{[1.123456789@2019-09-01, 2.123456789@2019-09-02],' + '[1.123456789@2019-09-03, 1.123456789@2019-09-05]}'), + TFloatSeq('{[1.12@2019-09-01, 2.12@2019-09-02],' + '[1.12@2019-09-03, 1.12@2019-09-05]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1172,7 +1171,7 @@ class TestTFloatModifications(TestTFloat): (tfds, TFloatSeq('{1.5@2019-09-03}'), TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02, 1.5@2019-09-03}')), (tfs, TFloatSeq('[1.5@2019-09-03]'), TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02, 1.5@2019-09-03]}')), (tfss, TFloatSeq('[1.5@2019-09-06]'), - TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05],[1.5@2019-09-06]}')), + TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05],[1.5@2019-09-06]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1184,10 +1183,10 @@ def test_insert(self, temporal, sequence, expected): [ (tfi, TFloatInst('2.5@2019-09-01'), TFloatInst('2.5@2019-09-01')), (tfds, TFloatInst('2.5@2019-09-01'), TFloatSeq('{2.5@2019-09-01, 2.5@2019-09-02}')), - (tfs, TFloatInst('2.5@2019-09-01'), - TFloatSeqSet('{[2.5@2019-09-01], (1.5@2019-09-01, 2.5@2019-09-02]}')), + (tfs, TFloatInst('2.5@2019-09-01'), + TFloatSeqSet('{[2.5@2019-09-01], (1.5@2019-09-01, 2.5@2019-09-02]}')), (tfss, TFloatInst('2.5@2019-09-01'), - TFloatSeqSet('{[2.5@2019-09-01], (1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), + TFloatSeqSet('{[2.5@2019-09-01], (1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1201,9 +1200,9 @@ def test_update(self, temporal, instant, expected): (tfi, datetime(year=2019, month=9, day=2, tzinfo=timezone.utc), tfi), (tfds, datetime(year=2019, month=9, day=1, tzinfo=timezone.utc), TFloatSeq('{2.5@2019-09-02}')), (tfs, datetime(year=2019, month=9, day=1, tzinfo=timezone.utc), - TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), + TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), (tfss, datetime(year=2019, month=9, day=1, tzinfo=timezone.utc), - TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), + TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), ], ids=['Instant intersection', 'Instant disjoint', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1217,7 +1216,7 @@ def test_delete(self, temporal, time, expected): (tfds, TFloatInst('1.5@2019-09-03'), TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02, 1.5@2019-09-03}')), (tfs, TFloatInst('1.5@2019-09-03'), TFloatSeq('[1.5@2019-09-01, 2.5@2019-09-02, 1.5@2019-09-03]')), (tfss, TFloatInst('1.5@2019-09-06'), - TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-06]}')), + TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-06]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1230,7 +1229,7 @@ def test_append_instant(self, temporal, instant, expected): (tfds, TFloatSeq('{1.5@2019-09-03}'), TFloatSeq('{1.5@2019-09-01, 2.5@2019-09-02, 1.5@2019-09-03}')), (tfs, TFloatSeq('[1.5@2019-09-03]'), TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02], [1.5@2019-09-03]}')), (tfss, TFloatSeq('[1.5@2019-09-06]'), - TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05],[1.5@2019-09-06]}')), + TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05],[1.5@2019-09-06]}')), ], ids=['Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1311,7 +1310,8 @@ def test_temporal_sub_int_float(self, temporal, argument, expected): (tfi, tfloatarg, TFloatInst('3.75@2019-09-01')), (tfds, tfloatarg, TFloatSeq('{3.75@2019-09-01, 3.75@2019-09-02}')), (tfs, tfloatarg, TFloatSeqSet('{[3.75@2019-09-01, 4@2019-09-01 12:00:00+00, 3.75@2019-09-02]}')), - (tfss, tfloatarg, TFloatSeqSet('{[3.75@2019-09-01, 4@2019-09-01 12:00:00+00, 3.75@2019-09-02], [2.25@2019-09-03]}')), + (tfss, tfloatarg, + TFloatSeqSet('{[3.75@2019-09-01, 4@2019-09-01 12:00:00+00, 3.75@2019-09-02], [2.25@2019-09-03]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1326,12 +1326,12 @@ def test_temporal_mul_temporal(self, temporal, argument, expected): (tfds, 0, TFloat.from_base_temporal(0, tfds)), (tfs, 0, TFloat.from_base_temporal(0, tfs)), (tfss, 0, TFloat.from_base_temporal(0, tfss)), - + (tfi, 1, tfi), (tfds, 1, tfds), (tfs, 1, tfs), (tfss, 1, tfss), - + (tfi, 2, TFloatInst('3@2019-09-01')), (tfds, 2, TFloatSeq('{3@2019-09-01, 5@2019-09-02}')), (tfs, 2, TFloatSeq('[3@2019-09-01, 5@2019-09-02]')), @@ -1353,7 +1353,8 @@ def test_temporal_mul_int_float(self, temporal, argument, expected): (tfi, tfloatarg, TFloatInst('0.6@2019-09-01')), (tfds, tfloatarg, TFloatSeq('{0.6@2019-09-01, 1.667@2019-09-02}')), (tfs, tfloatarg, TFloatSeqSet('{[0.6@2019-09-01, 1@2019-09-01 12:00:00+00, 1.667@2019-09-02]}')), - (tfss, tfloatarg, TFloatSeqSet('{[0.6@2019-09-01, 1@2019-09-01 12:00:00+00, 1.667@2019-09-02], [1@2019-09-03]}')), + (tfss, tfloatarg, + TFloatSeqSet('{[0.6@2019-09-01, 1@2019-09-01 12:00:00+00, 1.667@2019-09-02], [1@2019-09-03]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1476,16 +1477,16 @@ class TestTFloatRestrictors(TestTFloat): (tfss, timestamp_set, TFloatSeq('{1.5@2019-09-01, 1.5@2019-09-03}')), (tfss, period, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02]}')), (tfss, period_set, - TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), + TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', + 'Instant-PeriodSet', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', 'Sequence-PeriodSet', 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', - 'SequenceSet-Period', 'SequenceSet-PeriodSet', + 'SequenceSet-Period', 'SequenceSet-PeriodSet', ] ) def test_at_time(self, temporal, restrictor, expected): @@ -1496,49 +1497,52 @@ def test_at_time(self, temporal, restrictor, expected): [ (tfi, 1.5, TFloatInst('1.5@2019-09-01')), (tfi, 2.5, None), + (tfi, FloatSet(elements=[1.5, 2.5]), tfi), (tfi, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - TFloatInst('1.5@2019-09-01')), - (tfi, [1.5,2.5], tfi), - # (tfi, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], - # TFloatInst('1.5@2019-09-01')), + TFloatInst('1.5@2019-09-01')), + (tfi, FloatSpanSet(span_list=[FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), + TFloatInst('1.5@2019-09-01')), (tfds, 1.5, TFloatSeq('{1.5@2019-09-01}')), (tfds, 2.5, TFloatSeq('{2.5@2019-09-02}')), + ( + tfds, FloatSet(elements=[1.5, 2.5]), + TFloatSeq("{1.5@2019-09-01 00:00:00+00, 2.5@2019-09-02 00:00:00+00}")), (tfds, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - TFloatInst('1.5@2019-09-01')), - (tfds, [1.5,2.5], tfds), - # (tfds, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], - # TFloatInst('1.5@2019-09-01')), + TFloatInst('1.5@2019-09-01')), + (tfds, FloatSpanSet(span_list=[FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), + TFloatSeqSet("{1.5@2019-09-01 00:00:00+00, 2.5@2019-09-02 00:00:00+00}")), (tfs, 1.5, TFloatSeq('[1.5@2019-09-01]')), (tfs, 2.5, TFloatSeq('[2.5@2019-09-02]')), + (tfs, FloatSet(elements=[1.5, 2.5]), TFloatSeqSet('{1.5@2019-09-01, 2.5@2019-09-02}')), (tfs, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - TFloatInst('1.5@2019-09-01')), - (tfs, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02]}')), - # (tfs, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], - # TFloatInst('1.5@2019-09-01')), + TFloatInst('1.5@2019-09-01')), + (tfs, FloatSpanSet(span_list=[FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), + TFloatSeq("{[1.5@2019-09-01 00:00:00+00], [2.5@2019-09-02 00:00:00+00]}")), (tfss, 1.5, TFloatSeqSet('{[1.5@2019-09-01],[1.5@2019-09-03, 1.5@2019-09-05]}')), (tfss, 2.5, TFloatSeqSet('{[2.5@2019-09-02]}')), + (tfss, FloatSet(elements=[1.5, 2.5]), TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02],' + '[1.5@2019-09-03, 1.5@2019-09-05]}')), (tfss, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - TFloatSeqSet('{[1.5@2019-09-01],[1.5@2019-09-03, 1.5@2019-09-05]}')), - (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01], [2.5@2019-09-02],' - '[1.5@2019-09-03, 1.5@2019-09-05]}')) - # (tfss, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], - # TFloatInst('1.5@2019-09-01')), - ], - ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', - 'Instant-ValueList', # 'Instant-RangeList', - 'Discrete Sequence-1.5', 'Discrete Sequence-2.5', 'Discrete Sequence-Range', - 'Discrete Sequence-ValueList', # 'Discrete Sequence-RangeList' - 'Sequence-1.5', 'Sequence-2.5', 'Sequence-Range', - 'Sequence-ValueList', # 'Sequence-RangeList', - 'SequenceSet-1.5', 'SequenceSet-2.5', 'Sequence Set-Range', - 'SequenceSet-ValueList', # 'SequenceSet-RangeList' + TFloatSeqSet('{[1.5@2019-09-01],[1.5@2019-09-03, 1.5@2019-09-05]}')), + (tfss, FloatSpanSet(span_list=[FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), + TFloatSeqSet( + "{[1.5@2019-09-01 00:00:00+00], [2.5@2019-09-02 00:00:00+00], [1.5@2019-09-03 00:00:00+00, 1.5@2019-09-05 00:00:00+00]}")), + ], + ids=['Instant-1.5', 'Instant-2.5', 'Instant-Set', + 'Instant-Span', 'Instant-SpanSet', + 'Discrete Sequence-1.5', 'Discrete Sequence-2.5', 'Discrete Sequence-Set', + 'Discrete Sequence-Span', 'Discrete Sequence-SpanSet', + 'Sequence-1.5', 'Sequence-2.5', 'Sequence-Set', + 'Sequence-Span', 'Sequence-SpanSet', + 'SequenceSet-1.5', 'SequenceSet-2.5', 'Sequence Set-Set', + 'SequenceSet-Span', 'SequenceSet-SpanSet' ] ) def test_at_values(self, temporal, restrictor, expected): @@ -1589,7 +1593,7 @@ def test_at_max(self, temporal, expected): (tfs, period_set, None), (tfss, timestamp, - TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), + TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02],[1.5@2019-09-03, 1.5@2019-09-05]}')), (tfss, timestamp_set, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02],(1.5@2019-09-03, 1.5@2019-09-05]}')), (tfss, period, TFloatSeqSet('{[1.5@2019-09-03, 1.5@2019-09-05]}')), @@ -1601,7 +1605,7 @@ def test_at_max(self, temporal, expected): 'Discrete Sequence-PeriodSet', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', 'Sequence-PeriodSet', 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', - 'SequenceSet-Period', 'SequenceSet-PeriodSet', + 'SequenceSet-Period', 'SequenceSet-PeriodSet', ] ) def test_minus_time(self, temporal, restrictor, expected): @@ -1615,38 +1619,38 @@ def test_minus_time(self, temporal, restrictor, expected): (tfi, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), None), # (tfi, [1.5,2.5], TFloatInst('1.5@2019-09-01')), # (tfi, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], - # TFloatInst('1.5@2019-09-01')), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], + # TFloatInst('1.5@2019-09-01')), (tfds, 1.5, TFloatSeq('{2.5@2019-09-02}')), (tfds, 2.5, TFloatSeq('{1.5@2019-09-01}')), (tfds, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - TFloatSeq('{2.5@2019-09-02}')), + TFloatSeq('{2.5@2019-09-02}')), # (tfds, [1.5,2.5], TFloatSeq('{1.5@2019-09-01}')), # (tfds, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], - # TFloatInst('1.5@2019-09-01')), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], + # TFloatInst('1.5@2019-09-01')), (tfs, 1.5, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), (tfs, 2.5, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), (tfs, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), + TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), # (tfs, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02)}')), # (tfs, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], - # TFloatInst('1.5@2019-09-01')), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], + # TFloatInst('1.5@2019-09-01')), (tfss, 1.5, TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), (tfss, 2.5, TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),[1.5@2019-09-03, 1.5@2019-09-05]}')), (tfs, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), + TFloatSeqSet('{(1.5@2019-09-01, 2.5@2019-09-02]}')), # (tfss, [1.5,2.5], TFloatSeqSet('{[1.5@2019-09-01, 2.5@2019-09-02),' - # '[1.5@2019-09-03, 1.5@2019-09-05]}')) + # '[1.5@2019-09-03, 1.5@2019-09-05]}')) # (tfss, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], - # TFloatInst('1.5@2019-09-01')), + # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)], + # TFloatInst('1.5@2019-09-01')), ], - ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', + ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', # 'Instant-ValueList', 'Instant-RangeList', 'Discrete Sequence-1.5', 'Discrete Sequence-2.5', 'Discrete Sequence-Range', @@ -1715,10 +1719,10 @@ def test_minus_max(self, temporal, expected): ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', 'Instant-PeriodSet', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', - 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', + 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', - 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', + 'Sequence-PeriodSet', + 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', 'SequenceSet-PeriodSet', ] ) @@ -1730,41 +1734,41 @@ def test_at_minus_time(self, temporal, restrictor): [ (tfi, 1.5), (tfi, 2.5), + (tfi, FloatSet(elements=[1.5, 2.5])), (tfi, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)), - (tfi, [1.5,2.5]), - # (tfi, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), + (tfi, FloatSpanSet(span_list=[FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)])), (tfds, 1.5), (tfds, 2.5), + (tfds, FloatSet(elements=[1.5, 2.5])), (tfds, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)), - (tfds, [1.5,2.5]), - # (tfds, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), + (tfds, FloatSpanSet(span_list=[FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)])), (tfs, 1.5), (tfs, 2.5), + (tfs, FloatSet(elements=[1.5, 2.5])), (tfs, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)), - (tfs, [1.5,2.5]), - # (tfs, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), + (tfs, FloatSpanSet(span_list=[FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)])), (tfss, 1.5), (tfss, 2.5), + (tfss, FloatSet(elements=[1.5, 2.5])), (tfss, FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True)), - (tfss, [1.5,2.5]), - # (tfss, [FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), - # FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)]), - ], - ids=['Instant-1.5', 'Instant-2.5', 'Instant-Range', - 'Instant-ValueList', # 'Instant-RangeList', - 'Discrete Sequence-1.5', 'Discrete Sequence-2.5', - 'Discrete Sequence-Range', - 'Discrete Sequence-ValueList', # 'Discrete Sequence-RangeList', - 'Sequence-1.5', 'Sequence-2.5', 'Sequence-Range', - 'Sequence-ValueList', # 'Sequence-RangeList', - 'SequenceSet-1.5', 'SequenceSet-2.5', 'SequenceSet-Range', - 'SequenceSet-ValueList', # 'SequenceSet-RangeList', + (tfss, FloatSpanSet(span_list=[FloatSpan(lower=1.5, upper=1.5, lower_inc=True, upper_inc=True), + FloatSpan(lower=2.5, upper=2.5, lower_inc=True, upper_inc=True)])), + ], + ids=['Instant-1.5', 'Instant-2.5', 'Instant-Set', + 'Instant-Span', 'Instant-SpanSet', + 'Discrete Sequence-1.5', 'Discrete Sequence-2.5', + 'Discrete Sequence-Set', + 'Discrete Sequence-Span', 'Discrete Sequence-SpanSet', + 'Sequence-1.5', 'Sequence-2.5', 'Sequence-Set', + 'Sequence-Span', 'Sequence-SpanSet', + 'SequenceSet-1.5', 'SequenceSet-2.5', 'SequenceSet-Set', + 'SequenceSet-Span', 'SequenceSet-SpanSet', ] ) def test_at_minus_values(self, temporal, restrictor): @@ -2053,10 +2057,10 @@ class TestTFloatSplitOperations(TestTFloat): 'temporal, expected', [ (tfi, [TFloatInst('1@2019-09-01')]), - (tfds, [TFloatSeq('{1@2019-09-01}'),TFloatSeq('{2@2019-09-02}')]), - (tfs, [TFloatSeq('[1@2019-09-01, 2@2019-09-02)'),TFloatSeq('[2@2019-09-02]')]), + (tfds, [TFloatSeq('{1@2019-09-01}'), TFloatSeq('{2@2019-09-02}')]), + (tfs, [TFloatSeq('[1@2019-09-01, 2@2019-09-02)'), TFloatSeq('[2@2019-09-02]')]), (tfss, [TFloatSeqSet('{[1@2019-09-01, 2@2019-09-02),[1@2019-09-03, 1@2019-09-05]}'), - TFloatSeqSet('{[2@2019-09-02]}')]), + TFloatSeqSet('{[2@2019-09-02]}')]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -2070,7 +2074,7 @@ def test_value_split(self, temporal, expected): (tfds, [TFloatSeq('{1@2019-09-01, 2@2019-09-02}')]), (tfs, [TFloatSeq('[1@2019-09-01, 2@2019-09-02]')]), (tfss, [TFloatSeq('[1@2019-09-01,2@2019-09-02]'), - TFloatSeq('[1@2019-09-03, 1@2019-09-05]')]), + TFloatSeq('[1@2019-09-03, 1@2019-09-05]')]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -2081,12 +2085,12 @@ def test_time_split(self, temporal, expected): 'temporal, expected', [ (tfi, [TFloatInst('1@2019-09-01')]), - (tfds, [TFloatSeq('{1@2019-09-01}'), - TFloatSeq('{2@2019-09-02}')]), + (tfds, [TFloatSeq('{1@2019-09-01}'), + TFloatSeq('{2@2019-09-02}')]), (tfs, [TFloatSeq('[1@2019-09-01, 1.5@2019-09-01 12:00:00+00)'), - TFloatSeq('[1.5@2019-09-01 12:00:00+00, 2@2019-09-02]')]), + TFloatSeq('[1.5@2019-09-01 12:00:00+00, 2@2019-09-02]')]), (tfss, [TFloatSeq('[1@2019-09-01,2@2019-09-02]'), - TFloatSeq('[1@2019-09-03, 1@2019-09-05]')]), + TFloatSeq('[1@2019-09-03, 1@2019-09-05]')]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'], ) @@ -2099,10 +2103,10 @@ def test_time_split_n(self, temporal, expected): (tfi, [TFloatInst('1@2019-09-01')]), (tfds, [TFloatSeq('{1@2019-09-01}'), TFloatSeq('{2@2019-09-02}')]), (tfs, [TFloatSeq('[1@2019-09-01, 2@2019-09-02)'), - TFloatSeq('[2@2019-09-02]')]), + TFloatSeq('[2@2019-09-02]')]), (tfss, [TFloatSeq('{[1@2019-09-01, 2@2019-09-02)}'), - TFloatSeq('{[1@2019-09-03, 1@2019-09-05]}'), - TFloatSeq('{[2@2019-09-02]}')]), + TFloatSeq('{[1@2019-09-03, 1@2019-09-05]}'), + TFloatSeq('{[2@2019-09-02]}')]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -2274,9 +2278,9 @@ class TestTFloatTemporalComparisons(TestTFloat): (tfi, TBoolInst('False@2019-09-01')), (tfds, TBoolSeq('{False@2019-09-01, False@2019-09-02}')), (tfs, TBoolSeqSet('{[False@2019-09-01, True@2019-09-01 12:00:00+00],' - '(False@2019-09-01 12:00:00+00, False@2019-09-02]}')), + '(False@2019-09-01 12:00:00+00, False@2019-09-02]}')), (tfss, TBoolSeqSet('{[False@2019-09-01, True@2019-09-01 12:00:00+00],' - '(False@2019-09-01 12:00:00+00, False@2019-09-02],[True@2019-09-03]}')) + '(False@2019-09-01 12:00:00+00, False@2019-09-02],[True@2019-09-03]}')) ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -2301,9 +2305,10 @@ def test_temporal_equal_int(self, temporal, expected): [ (tfi, TBoolInst('False@2019-09-01')), (tfds, TBoolSeq('{False@2019-09-01, False@2019-09-02}')), - (tfs, TBoolSeq('{[False@2019-09-01, True@2019-09-01 12:00:00], (False@2019-09-01 12:00:00, False@2019-09-02]}')), + (tfs, + TBoolSeq('{[False@2019-09-01, True@2019-09-01 12:00:00], (False@2019-09-01 12:00:00, False@2019-09-02]}')), (tfss, TBoolSeqSet('{[False@2019-09-01, True@2019-09-01 12:00:00],' - '(False@2019-09-01 12:00:00, False@2019-09-02],[False@2019-09-03, False@2019-09-05]}')) + '(False@2019-09-01 12:00:00, False@2019-09-02],[False@2019-09-03, False@2019-09-05]}')) ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -2342,9 +2347,9 @@ def test_temporal_equal_float(self, temporal, expected): (tfi, TBoolInst('True@2019-09-01')), (tfds, TBoolSeq('{True@2019-09-01, True@2019-09-02}')), (tfs, TBoolSeqSet('{[True@2019-09-01, False@2019-09-01 12:00:00+00],' - '(True@2019-09-01 12:00:00+00, True@2019-09-02]}')), + '(True@2019-09-01 12:00:00+00, True@2019-09-02]}')), (tfss, TBoolSeqSet('{[True@2019-09-01, False@2019-09-01 12:00:00+00],' - '(True@2019-09-01 12:00:00+00, True@2019-09-02],[False@2019-09-03]}')) + '(True@2019-09-01 12:00:00+00, True@2019-09-02],[False@2019-09-03]}')) ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -2370,13 +2375,11 @@ def test_temporal_not_equal_int(self, temporal, expected): (tfi, TBoolInst('True@2019-09-01')), (tfds, TBoolSeq('{True@2019-09-01, True@2019-09-02}')), (tfs, TBoolSeqSet('{[True@2019-09-01, False@2019-09-01 12:00:00],' - '(True@2019-09-01 12:00:00, True@2019-09-02]}')), + '(True@2019-09-01 12:00:00, True@2019-09-02]}')), (tfss, TBoolSeqSet('{[True@2019-09-01, False@2019-09-01 12:00:00],' - '(True@2019-09-01 12:00:00, True@2019-09-02], [True@2019-09-03, True@2019-09-05]}')) + '(True@2019-09-01 12:00:00, True@2019-09-02], [True@2019-09-03, True@2019-09-05]}')) ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_temporal_not_equal_int(self, temporal, expected): assert temporal.temporal_not_equal(2) == expected - - diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index f9ed1809..bb0498e8 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -1,14 +1,13 @@ from copy import copy -from operator import not_ from datetime import datetime, timezone, timedelta -from spans.types import intrange, floatrange +from operator import not_ import pytest -from pymeos import TBool, TBoolInst, TBoolSeq, TBoolSeqSet, \ +from pymeos import TBoolInst, TBoolSeq, TBoolSeqSet, \ TFloat, TFloatInst, TFloatSeq, TFloatSeqSet, \ TInt, TIntInst, TIntSeq, TIntSeqSet, \ - TInterpolation, TBox, TimestampSet, Period, PeriodSet, IntSpan, FloatSpan + TInterpolation, TBox, TimestampSet, Period, PeriodSet, IntSpan, IntSet, IntSpanSet from tests.conftest import TestPyMEOS @@ -46,7 +45,7 @@ def test_from_base_constructor(self, source, type, interpolation): (Period('[2019-09-01, 2019-09-02]'), TIntSeq, TInterpolation.STEPWISE), (PeriodSet('{[2019-09-01, 2019-09-02],[2019-09-03, 2019-09-05]}'), TIntSeqSet, TInterpolation.STEPWISE) ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_from_base_time_constructor(self, source, type, interpolation): ti = TInt.from_base_time(1, source) @@ -218,7 +217,7 @@ def test_as_wkt(self, temporal, expected): (tids, '011D000602000000030100000000A01E4E71340200020000000000F66B85340200'), (tis, '011D000A02000000030100000000A01E4E71340200020000000000F66B85340200'), (tiss, '011D000B0200000002000000030100000000A01E4E71340200020000000000F66B85340200' - '0200000003010000000060CD89993402000100000000207CC5C1340200') + '0200000003010000000060CD89993402000100000000207CC5C1340200') ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -341,7 +340,7 @@ def test_as_hexwkb(self, temporal, expected): ) def test_as_mfjson(self, temporal, expected): assert temporal.as_mfjson() == expected - + class TestTIntConversions(TestTInt): tii = TIntInst('1@2019-09-01') @@ -436,18 +435,18 @@ def test_values(self, temporal, expected): def test_value_span(self, temporal, expected): assert temporal.value_span() == expected - # @pytest.mark.parametrize( - # 'temporal, expected', - # [ - # (tii, IntSpanSet('{[1,1]}')), - # (tids, IntSpanSet('{[1,2]}')), - # (tis, IntSpanSet('{[1,2]}')), - # (tiss, IntSpanSet('{[1,2]}')), - # ], - # ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] - # ) - # def test_value_spans(self, temporal, expected): - # assert temporal.value_spans() == expected + @pytest.mark.parametrize( + 'temporal, expected', + [ + (tii, IntSpanSet('{[1,1]}')), + (tids, IntSpanSet('{[1,2]}')), + (tis, IntSpanSet('{[1,2]}')), + (tiss, IntSpanSet('{[1,2]}')), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] + ) + def test_value_spans(self, temporal, expected): + assert temporal.value_spans() == expected @pytest.mark.parametrize( 'temporal, expected', @@ -786,15 +785,15 @@ def test_lower_upper_inc(self, temporal, expected): assert temporal.upper_inc() == expected def test_sequenceset_sequence_functions(self): - tiss1 =TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],' - '[1@2019-09-03, 1@2019-09-05], [3@2019-09-06]}') + tiss1 = TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],' + '[1@2019-09-03, 1@2019-09-05], [3@2019-09-06]}') assert tiss1.num_sequences() == 3 assert tiss1.start_sequence() == TIntSeq('[1@2019-09-01, 2@2019-09-02]') assert tiss1.end_sequence() == TIntSeq('[3@2019-09-06]') assert tiss1.sequence_n(1) == TIntSeq('[1@2019-09-03, 1@2019-09-05]') assert tiss1.sequences() == [TIntSeq('[1@2019-09-01, 2@2019-09-02]'), - TIntSeq('[1@2019-09-03, 1@2019-09-05]'), - TIntSeq('[3@2019-09-06]')] + TIntSeq('[1@2019-09-03, 1@2019-09-05]'), + TIntSeq('[3@2019-09-06]')] @pytest.mark.parametrize( 'temporal, expected', @@ -851,16 +850,16 @@ def test_to_instant(self, temporal, expected): 'temporal, interpolation, expected', [ (TIntInst('1@2019-09-01'), TInterpolation.STEPWISE, - TIntSeq('[1@2019-09-01]')), + TIntSeq('[1@2019-09-01]')), (TIntSeq('{1@2019-09-01, 2@2019-09-02}'), - TInterpolation.DISCRETE, - TIntSeq('{1@2019-09-01, 2@2019-09-02}')), + TInterpolation.DISCRETE, + TIntSeq('{1@2019-09-01, 2@2019-09-02}')), (TIntSeq('[1@2019-09-01, 2@2019-09-02]'), - TInterpolation.STEPWISE, - TIntSeq('[1@2019-09-01, 2@2019-09-02]')), + TInterpolation.STEPWISE, + TIntSeq('[1@2019-09-01, 2@2019-09-02]')), (TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}'), - TInterpolation.STEPWISE, - TIntSeq('[1@2019-09-01, 2@2019-09-02]')), + TInterpolation.STEPWISE, + TIntSeq('[1@2019-09-01, 2@2019-09-02]')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -873,16 +872,16 @@ def test_to_sequence(self, temporal, interpolation, expected): 'temporal, interpolation, expected', [ (TIntInst('1@2019-09-01'), TInterpolation.STEPWISE, - TIntSeqSet('{[1@2019-09-01]}')), + TIntSeqSet('{[1@2019-09-01]}')), (TIntSeq('{1@2019-09-01, 2@2019-09-02}'), - TInterpolation.STEPWISE, - TIntSeqSet('{[1@2019-09-01], [2@2019-09-02]}')), + TInterpolation.STEPWISE, + TIntSeqSet('{[1@2019-09-01], [2@2019-09-02]}')), (TIntSeq('[1@2019-09-01, 2@2019-09-02]'), - TInterpolation.STEPWISE, - TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), + TInterpolation.STEPWISE, + TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), (TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}'), - TInterpolation.STEPWISE, - TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), + TInterpolation.STEPWISE, + TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -895,17 +894,17 @@ def test_to_sequenceset(self, temporal, interpolation, expected): 'temporal, interpolation, expected', [ (tii, TInterpolation.DISCRETE, - TIntSeq('{1@2019-09-01}')), + TIntSeq('{1@2019-09-01}')), (tids, TInterpolation.DISCRETE, tids), (tis_d, TInterpolation.DISCRETE, - TIntSeq('{1@2019-09-01}')), + TIntSeq('{1@2019-09-01}')), (tiss_d, TInterpolation.DISCRETE, - TIntSeq('{1@2019-09-01,2@2019-09-03}')), + TIntSeq('{1@2019-09-01,2@2019-09-03}')), - (tii, TInterpolation.STEPWISE, - TIntSeq('[1@2019-09-01]')), - (tids, TInterpolation.STEPWISE, - TIntSeqSet('{[1@2019-09-01], [2@2019-09-02]}')), + (tii, TInterpolation.STEPWISE, + TIntSeq('[1@2019-09-01]')), + (tids, TInterpolation.STEPWISE, + TIntSeqSet('{[1@2019-09-01], [2@2019-09-02]}')), (tis, TInterpolation.STEPWISE, tis), (tiss, TInterpolation.STEPWISE, tiss), ], @@ -924,15 +923,15 @@ def test_set_interpolation(self, temporal, interpolation, expected): (tis, 2, TIntSeq('[3@2019-09-01, 4@2019-09-02]')), (tis, -2, TIntSeq('[-1@2019-09-01, 0@2019-09-02]')), (tiss, 2, TIntSeqSet('{[3@2019-09-01, 4@2019-09-02],' - '[3@2019-09-03, 3@2019-09-05]}')), + '[3@2019-09-03, 3@2019-09-05]}')), (tiss, -2, TIntSeqSet('{[-1@2019-09-01, 0@2019-09-02],' - '[-1@2019-09-03, -1@2019-09-05]}')), + '[-1@2019-09-03, -1@2019-09-05]}')), ], ids=['Instant positive', 'Instant negative', - 'Discrete Sequence positive', 'Discrete Sequence negative', - 'Sequence positive', 'Sequence negative', + 'Discrete Sequence positive', 'Discrete Sequence negative', + 'Sequence positive', 'Sequence negative', 'Sequence Set positive', 'Sequence Set negative', - ] + ] ) def test_shift_value(self, tint, delta, expected): assert tint.shift_value(delta) == expected @@ -943,9 +942,9 @@ def test_shift_value(self, tint, delta, expected): (tids, 3, TIntSeq('{1@2019-09-01, 4@2019-09-02}')), (tis, 3, TIntSeq('[1@2019-09-01, 4@2019-09-02]')), (tiss, 3, TIntSeqSet('{[1@2019-09-01, 4@2019-09-02],' - '[1@2019-09-03, 1@2019-09-05]}')), + '[1@2019-09-03, 1@2019-09-05]}')), ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set',] + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set', ] ) def test_scale_value(self, tint, width, expected): assert tint.scale_value(width) == expected @@ -959,15 +958,15 @@ def test_scale_value(self, tint, width, expected): (tis, 2, 3, TIntSeq('[3@2019-09-01, 6@2019-09-02]')), (tis, -2, 3, TIntSeq('[-1@2019-09-01, 2@2019-09-02]')), (tiss, 2, 3, TIntSeqSet('{[3@2019-09-01, 6@2019-09-02],' - '[3@2019-09-03, 3@2019-09-05]}')), + '[3@2019-09-03, 3@2019-09-05]}')), (tiss, -2, 3, TIntSeqSet('{[-1@2019-09-01, 2@2019-09-02],' - '[-1@2019-09-03, -1@2019-09-05]}')), + '[-1@2019-09-03, -1@2019-09-05]}')), ], ids=['Instant positive', 'Instant negative', - 'Discrete Sequence positive', 'Discrete Sequence negative', - 'Sequence positive', 'Sequence negative', + 'Discrete Sequence positive', 'Discrete Sequence negative', + 'Sequence positive', 'Sequence negative', 'Sequence Set positive', 'Sequence Set negative', - ] + ] ) def test_shift_scale_value(self, tint, delta, width, expected): assert tint.shift_scale_value(delta, width) == expected @@ -977,7 +976,7 @@ def test_shift_scale_value(self, tint, delta, width, expected): [(tii, timedelta(days=4), TIntInst('1@2019-09-05')), (tii, timedelta(days=-4), TIntInst('1@2019-08-28')), (tii, timedelta(hours=2), TIntInst('1@2019-09-01 02:00:00')), - (tii, timedelta(hours=-2), TIntInst('1@2019-08-31 22:00:00')), + (tii, timedelta(hours=-2), TIntInst('1@2019-08-31 22:00:00')), (tids, timedelta(days=4), TIntSeq('{1@2019-09-05, 2@2019-09-06}')), (tids, timedelta(days=-4), TIntSeq('{1@2019-08-28, 2@2019-08-29}')), (tids, timedelta(hours=2), TIntSeq('{1@2019-09-01 02:00:00, 2@2019-09-02 02:00:00}')), @@ -987,23 +986,23 @@ def test_shift_scale_value(self, tint, delta, width, expected): (tis, timedelta(hours=2), TIntSeq('[1@2019-09-01 02:00:00, 2@2019-09-02 02:00:00]')), (tis, timedelta(hours=-2), TIntSeq('[1@2019-08-31 22:00:00, 2@2019-09-01 22:00:00]')), (tiss, timedelta(days=4), - TIntSeqSet('{[1@2019-09-05, 2@2019-09-06],[1@2019-09-07, 1@2019-09-09]}')), + TIntSeqSet('{[1@2019-09-05, 2@2019-09-06],[1@2019-09-07, 1@2019-09-09]}')), (tiss, timedelta(days=-4), - TIntSeqSet('{[1@2019-08-28, 2@2019-08-29],[1@2019-08-30, 1@2019-09-01]}')), + TIntSeqSet('{[1@2019-08-28, 2@2019-08-29],[1@2019-08-30, 1@2019-09-01]}')), (tiss, timedelta(hours=2), - TIntSeqSet('{[1@2019-09-01 02:00:00, 2@2019-09-02 02:00:00],' - '[1@2019-09-03 02:00:00, 1@2019-09-05 02:00:00]}')), + TIntSeqSet('{[1@2019-09-01 02:00:00, 2@2019-09-02 02:00:00],' + '[1@2019-09-03 02:00:00, 1@2019-09-05 02:00:00]}')), (tiss, timedelta(hours=-2), - TIntSeqSet('{[1@2019-08-31 22:00:00, 2@2019-09-01 22:00:00],' - '[1@2019-09-02 22:00:00, 1@2019-09-04 22:00:00]}')), + TIntSeqSet('{[1@2019-08-31 22:00:00, 2@2019-09-01 22:00:00],' + '[1@2019-09-02 22:00:00, 1@2019-09-04 22:00:00]}')), ], ids=['Instant positive days', 'Instant negative days', 'Instant positive hours', 'Instant negative hours', - 'Discrete Sequence positive days', 'Discrete Sequence negative days', + 'Discrete Sequence positive days', 'Discrete Sequence negative days', 'Discrete Sequence positive hours', 'Discrete Sequence negative hours', - 'Sequence positive days', 'Sequence negative days', + 'Sequence positive days', 'Sequence negative days', 'Sequence positive hours', 'Sequence negative hours', - 'Sequence Set positive days', 'Sequence Set negative days', + 'Sequence Set positive days', 'Sequence Set negative days', 'Sequence Set positive hours', 'Sequence Set negative hours'] ) def test_shift_time(self, tint, delta, expected): @@ -1018,11 +1017,11 @@ def test_shift_time(self, tint, delta, expected): (tis, timedelta(days=4), TIntSeq('[1@2019-09-01, 2@2019-09-05]')), (tis, timedelta(hours=2), TIntSeq('[1@2019-09-01 00:00:00, 2@2019-09-01 02:00:00]')), (tiss, timedelta(days=4), - TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), + TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), (tiss, timedelta(hours=2), - TIntSeqSet('{[1@2019-09-01 00:00:00, 2@2019-09-01 00:30:00],' - '[1@2019-09-01 01:00:00, 1@2019-09-01 02:00:00]}')), - ], + TIntSeqSet('{[1@2019-09-01 00:00:00, 2@2019-09-01 00:30:00],' + '[1@2019-09-01 01:00:00, 1@2019-09-01 02:00:00]}')), + ], ids=['Instant positive days', 'Instant positive hours', 'Discrete Sequence positive days', 'Discrete Sequence positive hours', 'Sequence positive days', 'Sequence positive hours', @@ -1033,8 +1032,8 @@ def test_scale_time(self, tint, delta, expected): def test_shift_scale_time(self): assert self.tiss.shift_scale_time(timedelta(days=4), timedelta(hours=2)) == \ - TIntSeqSet('{[1@2019-09-05 00:00:00, 2@2019-09-05 00:30:00],' - '[1@2019-09-05 01:00:00, 1@2019-09-05 02:00:00]}') + TIntSeqSet('{[1@2019-09-05 00:00:00, 2@2019-09-05 00:30:00],' + '[1@2019-09-05 01:00:00, 1@2019-09-05 02:00:00]}') @pytest.mark.parametrize( 'tint, delta, expected', @@ -1045,11 +1044,11 @@ def test_shift_scale_time(self): (tis, timedelta(days=4), TIntSeq('{1@2019-09-01}')), (tis, timedelta(hours=12), TIntSeq('{1@2019-09-01, 1@2019-09-01 12:00:00, 2@2019-09-02}')), (tiss, timedelta(days=4), - TIntSeq('{1@2019-09-01,1@2019-09-05}')), + TIntSeq('{1@2019-09-01,1@2019-09-05}')), (tiss, timedelta(hours=12), - TIntSeq('{1@2019-09-01, 1@2019-09-01 12:00:00, 2@2019-09-02,' - '1@2019-09-03, 1@2019-09-03 12:00:00, 1@2019-09-04, ' - '1@2019-09-04 12:00:00, 1@2019-09-05}')), + TIntSeq('{1@2019-09-01, 1@2019-09-01 12:00:00, 2@2019-09-02,' + '1@2019-09-03, 1@2019-09-03 12:00:00, 1@2019-09-04, ' + '1@2019-09-04 12:00:00, 1@2019-09-05}')), ], ids=['Instant days', 'Instant hours', 'Discrete Sequence days', 'Discrete Sequence hours', @@ -1072,7 +1071,7 @@ def test_temporal_sample(self, tint, delta, expected): def test_to_tfloat(self, temporal, expected): assert temporal.to_tfloat() == expected - + class TestTIntModifications(TestTInt): tii = TIntInst('1@2019-09-01') tids = TIntSeq('{1@2019-09-01, 2@2019-09-02}') @@ -1086,7 +1085,7 @@ class TestTIntModifications(TestTInt): (tids, TIntSeq('{1@2019-09-03}'), TIntSeq('{1@2019-09-01, 2@2019-09-02, 1@2019-09-03}')), (tis, TIntSeq('[1@2019-09-03]'), TIntSeqSet('{[1@2019-09-01, 2@2019-09-02, 1@2019-09-03]}')), (tiss, TIntSeq('[1@2019-09-06]'), - TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05],[1@2019-09-06]}')), + TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05],[1@2019-09-06]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1098,10 +1097,10 @@ def test_insert(self, temporal, sequence, expected): [ (tii, TIntInst('2@2019-09-01'), TIntInst('2@2019-09-01')), (tids, TIntInst('2@2019-09-01'), TIntSeq('{2@2019-09-01, 2@2019-09-02}')), - (tis, TIntInst('2@2019-09-01'), - TIntSeqSet('{[2@2019-09-01], (1@2019-09-01, 2@2019-09-02]}')), + (tis, TIntInst('2@2019-09-01'), + TIntSeqSet('{[2@2019-09-01], (1@2019-09-01, 2@2019-09-02]}')), (tiss, TIntInst('2@2019-09-01'), - TIntSeqSet('{[2@2019-09-01], (1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), + TIntSeqSet('{[2@2019-09-01], (1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1115,9 +1114,9 @@ def test_update(self, temporal, instant, expected): (tii, datetime(year=2019, month=9, day=2, tzinfo=timezone.utc), tii), (tids, datetime(year=2019, month=9, day=1, tzinfo=timezone.utc), TIntSeq('{2@2019-09-02}')), (tis, datetime(year=2019, month=9, day=1, tzinfo=timezone.utc), - TIntSeqSet('{(1@2019-09-01, 2@2019-09-02]}')), + TIntSeqSet('{(1@2019-09-01, 2@2019-09-02]}')), (tiss, datetime(year=2019, month=9, day=1, tzinfo=timezone.utc), - TIntSeqSet('{(1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), + TIntSeqSet('{(1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), ], ids=['Instant intersection', 'Instant disjoint', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1131,7 +1130,7 @@ def test_delete(self, temporal, time, expected): (tids, TIntInst('1@2019-09-03'), TIntSeq('{1@2019-09-01, 2@2019-09-02, 1@2019-09-03}')), (tis, TIntInst('1@2019-09-03'), TIntSeq('[1@2019-09-01, 2@2019-09-02, 1@2019-09-03]')), (tiss, TIntInst('1@2019-09-06'), - TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-06]}')), + TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-06]}')), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1144,7 +1143,7 @@ def test_append_instant(self, temporal, instant, expected): (tids, TIntSeq('{1@2019-09-03}'), TIntSeq('{1@2019-09-01, 2@2019-09-02, 1@2019-09-03}')), (tis, TIntSeq('[1@2019-09-03]'), TIntSeqSet('{[1@2019-09-01, 2@2019-09-02], [1@2019-09-03]}')), (tiss, TIntSeq('[1@2019-09-06]'), - TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05],[1@2019-09-06]}')), + TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05],[1@2019-09-06]}')), ], ids=['Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1290,7 +1289,7 @@ def test_temporal_div_temporal(self, temporal, argument, expected): ids=['Instant 1', 'Discrete Sequence 1', 'Sequence 1', 'SequenceSet 1', 'Instant 2', 'Discrete Sequence 2', 'Sequence 2', 'SequenceSet 2'] ) - def test_temporal_div_int_float (self, temporal, argument, expected): + def test_temporal_div_int_float(self, temporal, argument, expected): assert temporal.div(argument) == expected assert (temporal / argument) == expected @@ -1353,16 +1352,16 @@ class TestTIntRestrictors(TestTInt): (tiss, timestamp_set, TIntSeq('{1@2019-09-01, 1@2019-09-03}')), (tiss, period, TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), (tiss, period_set, - TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), + TIntSeqSet('{[1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', 'Instant-PeriodSet', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', + 'Sequence-PeriodSet', 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', - 'SequenceSet-Period', 'SequenceSet-PeriodSet', + 'SequenceSet-Period', 'SequenceSet-PeriodSet', ] ) def test_at_time(self, temporal, restrictor, expected): @@ -1373,39 +1372,44 @@ def test_at_time(self, temporal, restrictor, expected): [ (tii, 1, TIntInst('1@2019-09-01')), (tii, 2, None), + (tii, IntSet(elements=[1, 2]), tii), (tii, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntInst('1@2019-09-01')), - (tii, [1,2], tii), - # (tii, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], - # TIntInst('1@2019-09-01')), + (tii, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)]), + TIntInst('1@2019-09-01')), (tids, 1, TIntSeq('{1@2019-09-01}')), (tids, 2, TIntSeq('{2@2019-09-02}')), + (tids, IntSet(elements=[1, 2]), tids), (tids, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeq('{1@2019-09-01}')), - (tids, [1,2], tids), - # (tids, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), tids), + (tids, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)]), tids), (tis, 1, TIntSeq('[1@2019-09-01, 1@2019-09-02)')), (tis, 2, TIntSeq('[2@2019-09-02]')), + (tis, IntSet(elements=[1, 2]), tis), (tis, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeq('[1@2019-09-01, 1@2019-09-02)')), - (tis, [1,2], tis), - # (tis, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], - # TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), + (tis, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)]), + TIntSeqSet('{[1@2019-09-01, 2@2019-09-02]}')), (tiss, 1, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), (tiss, 2, TIntSeqSet('{[2@2019-09-02]}')), - (tiss, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), - (tiss, [1,2], tiss) - # (tiss, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], - # tiss), - ], - ids=['Instant-1', 'Instant-2', 'Instant-Range', - 'Instant-ValueList', # 'Instant-RangeList', - 'Discrete Sequence-1', 'Discrete Sequence-2', 'Discrete Sequence-Range', - 'Discrete Sequence-ValueList', # 'Discrete Sequence-RangeList', - 'Sequence-1', 'Sequence-2', 'Sequence-Range', - 'Sequence-ValueList', # 'Sequence-RangeList', - 'SequenceSet-1', 'SequenceSet-2', 'SequenceSet-Range', - 'SequenceSet-ValueList', # 'SequenceSet-RangeList' + (tiss, IntSet(elements=[1, 2]), tiss), + (tiss, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), + (tiss, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)]), + tiss), + ], + ids=['Instant-1', 'Instant-2', 'Instant-Set', + 'Instant-Span', 'Instant-SpanSet', + 'Discrete Sequence-1', 'Discrete Sequence-2', 'Discrete Sequence-Set', + 'Discrete Sequence-Span', 'Discrete Sequence-SpanSet', + 'Sequence-1', 'Sequence-2', 'Sequence-Set', + 'Sequence-Span', 'Sequence-SpanSet', + 'SequenceSet-1', 'SequenceSet-2', 'SequenceSet-Set', + 'SequenceSet-Span', 'SequenceSet-SpanSet' ] ) def test_at_values(self, temporal, restrictor, expected): @@ -1424,7 +1428,6 @@ def test_at_values(self, temporal, restrictor, expected): def test_at_min(self, temporal, expected): assert temporal.at_min() == expected - @pytest.mark.parametrize( 'temporal, expected', [ @@ -1457,20 +1460,20 @@ def test_at_max(self, temporal, expected): (tis, period_set, None), (tiss, timestamp, - TIntSeqSet('{(1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), + TIntSeqSet('{(1@2019-09-01, 2@2019-09-02],[1@2019-09-03, 1@2019-09-05]}')), (tiss, timestamp_set, TIntSeqSet('{(1@2019-09-01, 2@2019-09-02],(1@2019-09-03, 1@2019-09-05]}')), (tiss, period, TIntSeqSet('{[1@2019-09-03, 1@2019-09-05]}')), (tiss, period_set, None), ], ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', + 'Instant-PeriodSet', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', - 'Sequence-PeriodSet', + 'Sequence-PeriodSet', 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', - 'SequenceSet-PeriodSet', + 'SequenceSet-PeriodSet', ] ) def test_minus_time(self, temporal, restrictor, expected): @@ -1481,40 +1484,44 @@ def test_minus_time(self, temporal, restrictor, expected): [ (tii, 1, None), (tii, 2, TIntInst('1@2019-09-01')), + (tii, IntSet(elements=[1, 2]), None), (tii, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), None), - (tii, [1,2], None), - # (tii, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], - # None), + (tii, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)]), + None), (tids, 1, TIntSeq('{2@2019-09-02}')), (tids, 2, TIntSeq('{1@2019-09-01}')), + (tids, IntSet(elements=[1, 2]), None), (tids, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeq('{2@2019-09-02}')), - (tids, [1,2], None), - # (tids, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], - # None), + (tids, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)]), + None), (tis, 1, TIntSeqSet('{[2@2019-09-02]}')), (tis, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02)}')), + (tis, IntSet(elements=[1, 2]), None), (tis, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeqSet('{[2@2019-09-02]}')), - (tis, [1,2], None), - # (tis, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], - # None), + (tis, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)]), + None), (tiss, 1, TIntSeqSet('{[2@2019-09-02]}')), (tiss, 2, TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}')), + (tiss, IntSet(elements=[1, 2]), None), (tis, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), TIntSeqSet('{[2@2019-09-02]}')), - (tiss, [1,2], None) - # (tiss, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)], - # None), - ], - ids=['Instant-1', 'Instant-2', 'Instant-Range', - 'Instant-ValueList', # 'Instant-RangeList', - 'Discrete Sequence-1', 'Discrete Sequence-2', 'Discrete Sequence-Range', - 'Discrete Sequence-ValueList', # 'Discrete Sequence-RangeList', - 'Sequence-1', 'Sequence-2', 'Sequence-Range', - 'Sequence-ValueList', # 'Sequence-RangeList', - 'SequenceSet-1', 'SequenceSet-2', 'SequenceSet-Range', - 'SequenceSet-ValueList', # 'SequenceSet-RangeList', + (tiss, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)]), + None), + ], + ids=['Instant-1', 'Instant-2', 'Instant-Set', + 'Instant-Span', 'Instant-SpanSet', + 'Discrete Sequence-1', 'Discrete Sequence-2', 'Discrete Sequence-Set', + 'Discrete Sequence-Span', 'Discrete Sequence-SpanSet', + 'Sequence-1', 'Sequence-2', 'Sequence-Set', + 'Sequence-Span', 'Sequence-SpanSet', + 'SequenceSet-1', 'SequenceSet-2', 'SequenceSet-Set', + 'SequenceSet-Span', 'SequenceSet-SpanSet' ] ) def test_minus_values(self, temporal, restrictor, expected): @@ -1558,9 +1565,10 @@ def test_minus_min(self, temporal, expected): (tii, period_set), (tii, 1), (tii, 2), + (tii, IntSet(elements=[1, 2])), (tii, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True)), - (tii, [1,2]), - # (tii, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)]), + (tii, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)])), (tids, timestamp), (tids, timestamp_set), @@ -1568,9 +1576,10 @@ def test_minus_min(self, temporal, expected): (tids, period_set), (tids, 1), (tids, 2), + (tids, IntSet(elements=[1, 2])), (tids, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True)), - (tids, [1,2]), - # (tds, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)]), + (tids, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)])), (tis, timestamp), (tis, timestamp_set), @@ -1578,9 +1587,10 @@ def test_minus_min(self, temporal, expected): (tis, period_set), (tis, 1), (tis, 2), + (tis, IntSet(elements=[1, 2])), (tis, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True)), - (tis, [1,2]), - # (tis, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)]), + (tis, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)])), (tiss, timestamp), (tiss, timestamp_set), @@ -1588,25 +1598,26 @@ def test_minus_min(self, temporal, expected): (tiss, period_set), (tiss, 1), (tiss, 2), + (tiss, IntSet(elements=[1, 2])), (tiss, IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True)), - (tiss, [1,2]), - # (tiss, [IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), IntSpan(2, 2, True, True)]), + (tiss, IntSpanSet(span_list=[IntSpan(lower=1, upper=1, lower_inc=True, upper_inc=True), + IntSpan(lower=2, upper=2, lower_inc=True, upper_inc=True)])), ], - ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', - 'Instant-PeriodSet', 'Instant-1', 'Instant-2', - 'Instant-Range', 'Instant-[1,2]', # 'Instant-RangeSet', + ids=['Instant-Timestamp', 'Instant-TimestampSet', 'Instant-Period', + 'Instant-PeriodSet', 'Instant-1', 'Instant-2', + 'Instant-Set', 'Instant-Span', 'Instant-SpanSet', 'Discrete Sequence-Timestamp', 'Discrete Sequence-TimestampSet', 'Discrete Sequence-Period', 'Discrete Sequence-PeriodSet', 'Discrete Sequence-1', - 'Discrete Sequence-2', 'Discrete Sequence-Range', - 'Discrete Sequence-[1,2]', # 'Discrete Sequence-RangeSet', + 'Discrete Sequence-2', 'Discrete Sequence-Set', + 'Discrete Sequence-Span', 'Discrete Sequence-SpanSet', 'Sequence-Timestamp', 'Sequence-TimestampSet', 'Sequence-Period', 'Sequence-PeriodSet', 'Sequence-1', 'Sequence-2', - 'Sequence-Range', 'Sequence-[1,2]', # 'Sequence-RangeSet', - 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', + 'Sequence-Set', 'Sequence-Span', 'Sequence-SpanSet', + 'SequenceSet-Timestamp', 'SequenceSet-TimestampSet', 'SequenceSet-Period', 'SequenceSet-PeriodSet', - 'SequenceSet-1', 'SequenceSet-2', - 'SequenceSet-Range', 'SequenceSet-[1,2]', # 'SequenceSet-RangeSet', - ] + 'SequenceSet-1', 'SequenceSet-2', + 'SequenceSet-Set', 'SequenceSet-Span', 'SequenceSet-SpanSet', + ] ) def test_at_minus(self, temporal, restrictor): assert TInt.merge(temporal.at(restrictor), temporal.minus(restrictor)) == temporal @@ -1894,9 +1905,10 @@ class TestTIntSplitOperations(TestTInt): 'temporal, expected', [ (tii, [TIntInst('1@2019-09-01')]), - (tids, [TIntSeq('{1@2019-09-01}'),TIntSeq('{2@2019-09-02}')]), - (tis, [TIntSeq('[1@2019-09-01, 1@2019-09-02)'),TIntSeq('[2@2019-09-02]')]), - (tiss, [TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}'),TIntSeq('[2@2019-09-02]')]), + (tids, [TIntSeq('{1@2019-09-01}'), TIntSeq('{2@2019-09-02}')]), + (tis, [TIntSeq('[1@2019-09-01, 1@2019-09-02)'), TIntSeq('[2@2019-09-02]')]), + (tiss, + [TIntSeqSet('{[1@2019-09-01, 1@2019-09-02),[1@2019-09-03, 1@2019-09-05]}'), TIntSeq('[2@2019-09-02]')]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1912,7 +1924,7 @@ def test_value_split(self, temporal, expected): (tids, [TIntSeq('{1@2019-09-01,2@2019-09-02}')]), (tis, [TIntSeq('[1@2019-09-01, 2@2019-09-02]')]), (tiss, [TIntSeq('[1@2019-09-01,2@2019-09-02]'), - TIntSeq('[1@2019-09-03, 1@2019-09-05]')]), + TIntSeq('[1@2019-09-03, 1@2019-09-05]')]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -1925,10 +1937,10 @@ def test_time_split(self, temporal, expected): (tii, [TIntInst('1@2019-09-01')]), (tids, [TIntSeq('{1@2019-09-01}'), TIntSeq('{2@2019-09-02}')]), (tis, [TIntSeq('[1@2019-09-01, 1@2019-09-02)'), - TIntSeq('[2@2019-09-02]')]), + TIntSeq('[2@2019-09-02]')]), (tiss, [TIntSeq('{[1@2019-09-01, 1@2019-09-02)}'), - TIntSeq('{[1@2019-09-03, 1@2019-09-05]}'), - TIntSeq('{[2@2019-09-02]}')]), + TIntSeq('{[1@2019-09-03, 1@2019-09-05]}'), + TIntSeq('{[2@2019-09-02]}')]), ], ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) @@ -2148,5 +2160,3 @@ def test_temporal_not_equal_int(self, temporal, expected): assert temporal.temporal_not_equal(1) == expected assert temporal.temporal_not_equal(2) == ~expected - - From d560a8bdefdefc04ab7abf7c43b96166b05cd74b Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 2 Oct 2023 12:10:47 +0200 Subject: [PATCH 071/101] Remove expandable capacity since make_exp functions are no longer exported by the normal MEOS API --- pymeos/pymeos/main/tbool.py | 3 +- pymeos/pymeos/main/tfloat.py | 3 +- pymeos/pymeos/main/tint.py | 3 +- pymeos/pymeos/main/tpoint.py | 12 ++---- pymeos/pymeos/main/ttext.py | 3 +- pymeos/pymeos/temporal/temporal.py | 58 +++++++++++--------------- pymeos/pymeos/temporal/tsequence.py | 26 +++--------- pymeos/pymeos/temporal/tsequenceset.py | 20 ++------- 8 files changed, 41 insertions(+), 87 deletions(-) diff --git a/pymeos/pymeos/main/tbool.py b/pymeos/pymeos/main/tbool.py index 50153b04..669df77f 100644 --- a/pymeos/pymeos/main/tbool.py +++ b/pymeos/pymeos/main/tbool.py @@ -463,12 +463,11 @@ class TBoolSeq(TSequence[bool, 'TBool', 'TBoolInst', 'TBoolSeq', def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TBoolInst]]] = None, lower_inc: bool = True, upper_inc: bool = False, - expandable: Union[bool, int] = False, interpolation: TInterpolation = TInterpolation.STEPWISE, normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, + interpolation=interpolation, normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index 54c29404..fdbe0a8c 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -912,12 +912,11 @@ class TFloatSeq(TSequence[float, 'TFloat', 'TFloatInst', 'TFloatSeq', def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TFloatInst]]] = None, lower_inc: bool = True, upper_inc: bool = False, - expandable: Union[bool, float] = False, interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, + interpolation=interpolation, normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index 0dfdda4e..ac9f86f3 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -833,11 +833,10 @@ class TIntSeq(TSequence[int, 'TInt', 'TIntInst', 'TIntSeq', 'TIntSeqSet'], TInt) def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TIntInst]]] = None, lower_inc: bool = True, upper_inc: bool = False, - expandable: Union[bool, int] = False, interpolation: TInterpolation = TInterpolation.STEPWISE, normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, - lower_inc=lower_inc, upper_inc=upper_inc, expandable=expandable, + lower_inc=lower_inc, upper_inc=upper_inc, interpolation=interpolation, normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 6b510316..9235a958 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -1877,12 +1877,11 @@ class TGeomPointSeq(TPointSeq['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TGeomPointInst]]] = None, lower_inc: bool = True, upper_inc: bool = False, - expandable: Union[bool, int] = False, interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, + interpolation=interpolation, normalize=normalize, _inner=_inner) @@ -1896,12 +1895,11 @@ class TGeogPointSeq(TPointSeq['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TGeogPointInst]]] = None, lower_inc: bool = True, upper_inc: bool = False, - expandable: Union[bool, int] = False, interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, + interpolation=interpolation, normalize=normalize, _inner=_inner) @@ -1915,10 +1913,9 @@ class TGeomPointSeqSet(TPointSeqSet['TGeomPoint', 'TGeomPointInst', def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, TGeomPointSeq]]] = None, - expandable: Union[bool, int] = False, normalize: bool = True, _inner=None): super().__init__(string=string, sequence_list=sequence_list, - expandable=expandable, normalize=normalize, _inner=_inner) + normalize=normalize, _inner=_inner) class TGeogPointSeqSet(TPointSeqSet['TGeogPoint', 'TGeogPointInst', @@ -1931,8 +1928,7 @@ class TGeogPointSeqSet(TPointSeqSet['TGeogPoint', 'TGeogPointInst', def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, TGeogPointSeq]]] = None, - expandable: Union[bool, int] = False, normalize: bool = True, _inner=None): super().__init__(string=string, sequence_list=sequence_list, - expandable=expandable, normalize=normalize, + normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/main/ttext.py b/pymeos/pymeos/main/ttext.py index ef66b752..10daf832 100644 --- a/pymeos/pymeos/main/ttext.py +++ b/pymeos/pymeos/main/ttext.py @@ -793,12 +793,11 @@ class TTextSeq(TSequence[str, 'TText', 'TTextInst', 'TTextSeq', def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, TTextInst]]] = None, lower_inc: bool = True, upper_inc: bool = False, - expandable: Union[bool, int] = False, interpolation: TInterpolation = TInterpolation.STEPWISE, normalize: bool = True, _inner=None): super().__init__(string=string, instant_list=instant_list, lower_inc=lower_inc, upper_inc=upper_inc, - expandable=expandable, interpolation=interpolation, + interpolation=interpolation, normalize=normalize, _inner=_inner) diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index b217d90c..64a8b9d2 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -43,9 +43,6 @@ class Temporal(Generic[TBase, TG, TI, TS, TSS], ABC): _parse_function = None - def _expandable(self) -> bool: - return False - # ------------------------- Constructors ---------------------------------- @classmethod def _factory(cls, inner): @@ -154,7 +151,7 @@ def from_merge(cls: Type[Self], *temporals: TG) -> Self: temporal_merge_array """ result = temporal_merge_array([temp._inner for temp in temporals \ - if temp is not None], len(temporals)) + if temp is not None], len(temporals)) return Temporal._factory(result) @classmethod @@ -171,7 +168,7 @@ def from_merge_array(cls: Type[Self], temporals: List[TG]) -> Self: objects. """ result = temporal_merge_array([temp._inner for temp in temporals \ - if temp is not None], len(temporals)) + if temp is not None], len(temporals)) return Temporal._factory(result) # ------------------------- Output ---------------------------------------- @@ -186,7 +183,7 @@ def as_wkt(self) -> str: pass def as_mfjson(self, with_bbox: bool = True, flags: int = 3, - precision: int = 6, srs: Optional[str] = None) -> str: + precision: int = 6, srs: Optional[str] = None) -> str: """ Returns the temporal object as a MF-JSON string. @@ -525,7 +522,7 @@ def scale_time(self, duration: timedelta) -> Self: return Temporal._factory(scaled) def shift_scale_time(self, shift: Optional[timedelta] = None, - duration: Optional[timedelta] = None) -> Self: + duration: Optional[timedelta] = None) -> Self: """ Returns a new :class:`Temporal` with the time dimension shifted by ``shift`` and scaled so the temporal dimension has duration @@ -549,7 +546,7 @@ def shift_scale_time(self, shift: Optional[timedelta] = None, return Temporal._factory(scaled) def temporal_sample(self, duration: Union[str, timedelta], - start: Optional[Union[str, datetime]] = None) -> TG: + start: Optional[Union[str, datetime]] = None) -> TG: """ Returns a new :class:`Temporal` downsampled with respect to ``duration``. @@ -576,7 +573,7 @@ def temporal_sample(self, duration: Union[str, timedelta], return Temporal._factory(result) def temporal_precision(self, duration: Union[str, timedelta], - start: Optional[Union[str, datetime]] = None) -> TG: + start: Optional[Union[str, datetime]] = None) -> TG: """ Returns a new :class:`Temporal` with precision reduced to ``duration``. @@ -646,7 +643,7 @@ def to_dataframe(self) -> DataFrame: # ------------------------- Modifications --------------------------------- def append_instant(self, instant: TInstant[TBase], max_dist: Optional[float] = 0.0, - max_time: Optional[timedelta] = None) -> TG: + max_time: Optional[timedelta] = None) -> TG: """ Returns a new :class:`Temporal` object equal to `self` with `instant` appended. @@ -664,9 +661,7 @@ def append_instant(self, instant: TInstant[TBase], max_dist: Optional[float] = 0 else: interv = timedelta_to_interval(max_time) new_inner = temporal_append_tinstant(self._inner, instant._inner, - max_dist, interv, self._expandable()) - if new_inner == self._inner: - return self + max_dist, interv, False) return Temporal._factory(new_inner) def append_sequence(self, sequence: TSequence[TBase]) -> TG: @@ -680,14 +675,10 @@ def append_sequence(self, sequence: TSequence[TBase]) -> TG: MEOS Functions: temporal_append_tsequence """ - new_inner = temporal_append_tsequence(self._inner, sequence._inner, - self._expandable()) - if new_inner == self._inner: - return self + new_inner = temporal_append_tsequence(self._inner, sequence._inner, False) return Temporal._factory(new_inner) - def merge(self, other: Union[type(None), Temporal[TBase], - List[Temporal[TBase]]]) -> TG: + def merge(self, other: Union[type(None), Temporal[TBase], List[Temporal[TBase]]]) -> TG: """ Returns a new :class:`Temporal` object that is the result of merging `self` with `other`. @@ -703,7 +694,7 @@ def merge(self, other: Union[type(None), Temporal[TBase], new_temp = temporal_merge(self._inner, other._inner) elif isinstance(other, list): new_temp = temporal_merge_array([self._inner, - *(o._inner for o in other)], len(other) + 1) + *(o._inner for o in other)], len(other) + 1) else: raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(new_temp) @@ -759,16 +750,16 @@ def delete(self, other: Time, connect: bool = True) -> TG: """ if isinstance(other, datetime): new_inner = temporal_delete_timestamp(self._inner, - datetime_to_timestamptz(other), connect) + datetime_to_timestamptz(other), connect) elif isinstance(other, TimestampSet): new_inner = temporal_delete_timestampset(self._inner, other._inner, - connect) + connect) elif isinstance(other, Period): new_inner = temporal_delete_period(self._inner, other._inner, - connect) + connect) elif isinstance(other, PeriodSet): new_inner = temporal_delete_periodset(self._inner, other._inner, - connect) + connect) else: raise TypeError(f'Operation not supported with type {other.__class__}') if new_inner == self._inner: @@ -793,7 +784,7 @@ def at(self, other: Time) -> TG: """ if isinstance(other, datetime): result = temporal_at_timestamp(self._inner, - datetime_to_timestamptz(other)) + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): result = temporal_at_timestampset(self._inner, other._inner) elif isinstance(other, Period): @@ -853,7 +844,7 @@ def minus(self, other: Time) -> TG: result = temporal_minus_periodset(self._inner, other._inner) elif isinstance(other, datetime): result = temporal_minus_timestamp(self._inner, - datetime_to_timestamptz(other)) + datetime_to_timestamptz(other)) elif isinstance(other, TimestampSet): result = temporal_minus_timestampset(self._inner, other._inner) else: @@ -941,7 +932,7 @@ def is_contained_in(self, container: Union[Time, Temporal, Box]) -> bool: return self.bounding_box().is_contained_in(container) def is_temporally_contained_in(self, - container: Union[Time, Temporal, Box]) -> bool: + container: Union[Time, Temporal, Box]) -> bool: """ Returns whether the bounding period of `self` is contained in the bounding period of `container`. @@ -1167,7 +1158,7 @@ def hausdorff_distance(self, other: Temporal) -> float: # ------------------------- Split Operations ------------------------------ def time_split(self, duration: Union[str, timedelta], - start: Optional[Union[str, datetime]] = None) -> List[TG]: + start: Optional[Union[str, datetime]] = None) -> List[TG]: """ Returns a list of temporal objects of the same subtype as `self` with the same values as `self` but split in temporal tiles of duration @@ -1198,7 +1189,7 @@ def time_split(self, duration: Union[str, timedelta], fragments, times, count = temporal_time_split(self._inner, dt, st) from ..factory import _TemporalFactory return [_TemporalFactory.create_temporal(fragments[i]) \ - for i in range(count)] + for i in range(count)] def time_split_n(self, n: int) -> List[TG]: """ @@ -1219,14 +1210,14 @@ def time_split_n(self, n: int) -> List[TG]: return [self] st = temporal_start_timestamp(self._inner) dt = timedelta_to_interval((self.end_timestamp() - - self.start_timestamp()) / n) + self.start_timestamp()) / n) fragments, times, count = temporal_time_split(self._inner, dt, st) from ..factory import _TemporalFactory return [_TemporalFactory.create_temporal(fragments[i]) \ - for i in range(count)] + for i in range(count)] def stops(self, max_distance: Optional[float] = 0.0, - min_duration: Optional[timedelta] = timedelta()) -> TSS: + min_duration: Optional[timedelta] = timedelta()) -> TSS: """ Return the subsequences where the objects stay within an area with a given maximum size for at least the specified duration. @@ -1243,7 +1234,7 @@ def stops(self, max_distance: Optional[float] = 0.0, temporal_stops """ new_inner = temporal_stops(self._inner, max_distance, - timedelta_to_interval(min_duration)) + timedelta_to_interval(min_duration)) from ..factory import _TemporalFactory return _TemporalFactory.create_temporal(new_inner) @@ -1459,4 +1450,3 @@ def __ge__(self, other): temporal_ge """ return temporal_ge(self._inner, other._inner) - diff --git a/pymeos/pymeos/temporal/tsequence.py b/pymeos/pymeos/temporal/tsequence.py index d5275215..9d8bc300 100644 --- a/pymeos/pymeos/temporal/tsequence.py +++ b/pymeos/pymeos/temporal/tsequence.py @@ -22,18 +22,13 @@ class TSequence(Temporal[TBase, TG, TI, TS, TSS], ABC): defined over a continuous period of time. """ - def _expandable(self) -> bool: - return self._expandable_sequence - # ------------------------- Constructors ---------------------------------- def __init__(self, string: Optional[str] = None, *, instant_list: Optional[List[Union[str, Any]]] = None, lower_inc: bool = True, upper_inc: bool = False, - expandable: Union[bool, int] = False, interpolation: TInterpolation = TInterpolation.LINEAR, normalize: bool = True, _inner=None): - assert (_inner is not None) or ((string is not None) != \ - (instant_list is not None)), \ + assert (_inner is not None) or ((string is not None) != (instant_list is not None)), \ "Either string must be not None or instant_list must be not" if _inner is not None: self._inner = as_tsequence(_inner) @@ -41,19 +36,10 @@ def __init__(self, string: Optional[str] = None, *, self._inner = as_tsequence(self.__class__._parse_function(string)) else: self._instants = [x._inner if isinstance(x, self.ComponentClass) \ - else self.__class__._parse_function(x) for x in instant_list] + else self.__class__._parse_function(x) for x in instant_list] count = len(self._instants) - if not expandable: - self._inner = tsequence_make(self._instants, count, lower_inc, - upper_inc, interpolation.value, normalize) - else: - max_size = max(expandable, count) \ - if isinstance(expandable, int) else 2 * count - self._inner = tsequence_make_exp(self._instants, count, - max_size, lower_inc, upper_inc, interpolation.value, - normalize) - self._expandable_sequence = bool(expandable) or \ - self._inner.maxcount > self._inner.count + self._inner = tsequence_make(self._instants, count, lower_inc, + upper_inc, interpolation.value, normalize) @classmethod def from_instants(cls: Type[Self], @@ -75,8 +61,8 @@ def from_instants(cls: Type[Self], A new temporal sequence. """ return cls(instant_list=instant_list, lower_inc=lower_inc, - upper_inc=upper_inc, interpolation=interpolation, - normalize=normalize) + upper_inc=upper_inc, interpolation=interpolation, + normalize=normalize) # ------------------------- Accessors ------------------------------------- def lower_inc(self) -> bool: diff --git a/pymeos/pymeos/temporal/tsequenceset.py b/pymeos/pymeos/temporal/tsequenceset.py index c7d9ba7b..a59eb461 100644 --- a/pymeos/pymeos/temporal/tsequenceset.py +++ b/pymeos/pymeos/temporal/tsequenceset.py @@ -22,16 +22,11 @@ class TSequenceSet(Temporal[TBase, TG, TI, TS, TSS], ABC): defined by a set of temporal sequences. """ - def _expandable(self) -> bool: - return self._expandable_sequenceset - # ------------------------- Constructors ---------------------------------- def __init__(self, string: Optional[str] = None, *, sequence_list: Optional[List[Union[str, Any]]] = None, - expandable: Union[bool, int] = False, normalize: bool = True, _inner=None): - assert (_inner is not None) or ((string is not None) != \ - (sequence_list is not None)), \ + assert (_inner is not None) or ((string is not None) != (sequence_list is not None)), \ "Either string must be not None or sequence_list must be not" if _inner is not None: self._inner = as_tsequenceset(_inner) @@ -39,18 +34,9 @@ def __init__(self, string: Optional[str] = None, *, self._inner = as_tsequenceset(self.__class__._parse_function(string)) else: sequences = [x._inner if isinstance(x, self.ComponentClass) \ - else self.__class__._parse_function(x) for x in sequence_list] + else self.__class__._parse_function(x) for x in sequence_list] count = len(sequences) self._inner = tsequenceset_make(sequences, count, normalize) - if not expandable: - self._inner = tsequenceset_make(sequences, count, normalize) - else: - max_size = max(expandable, count) \ - if isinstance(expandable, int) else 2 * count - self._inner = tsequenceset_make_exp(sequences, count, max_size, - normalize) - self._expandable_sequenceset = bool(expandable) or \ - self._inner.maxcount > self._inner.count @classmethod def from_sequences(cls: Type[Self], @@ -108,7 +94,7 @@ def to_dataframe(self) -> DataFrame: sequences = self.sequences() data = { 'sequence': [i for i, seq in enumerate(sequences) \ - for _ in range(seq.num_instants())], + for _ in range(seq.num_instants())], 'time': [t for seq in sequences for t in seq.timestamps()], 'value': [v for seq in sequences for v in seq.values()] } From 7af53b779c4f792fc263b4e07104c7bf8fc7e834 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 2 Oct 2023 12:21:39 +0200 Subject: [PATCH 072/101] Fix some typing and TFloat value_spans method. --- pymeos/pymeos/boxes/stbox.py | 24 +++-- pymeos/pymeos/collections/number/floatspan.py | 4 +- .../pymeos/collections/number/floatspanset.py | 2 +- pymeos/pymeos/collections/number/intspan.py | 4 +- .../pymeos/collections/number/intspanset.py | 2 +- pymeos/pymeos/collections/time/period.py | 2 +- pymeos/pymeos/main/tfloat.py | 5 +- pymeos/tests/main/tfloat_test.py | 2 +- pymeos/tests/main/tgeompoint_test.py | 88 +++++++++---------- pymeos/tests/main/tint_test.py | 2 +- pymeos/tests/main/ttext_test.py | 2 +- 11 files changed, 61 insertions(+), 76 deletions(-) diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index 629371ba..893e0237 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -400,7 +400,7 @@ def geodetic(self) -> bool: """ return stbox_isgeodetic(self._inner) - def xmin(self) -> float: + def xmin(self) -> Optional[float]: """ Returns the minimum X coordinate of ``self``. @@ -412,7 +412,7 @@ def xmin(self) -> float: """ return stbox_xmin(self._inner) - def ymin(self) -> float: + def ymin(self) -> Optional[float]: """ Returns the minimum Y coordinate of ``self``. @@ -424,7 +424,7 @@ def ymin(self) -> float: """ return stbox_ymin(self._inner) - def zmin(self) -> float: + def zmin(self) -> Optional[float]: """ Returns the minimum Z coordinate of ``self``. @@ -436,7 +436,7 @@ def zmin(self) -> float: """ return stbox_zmin(self._inner) - def tmin(self) -> datetime: + def tmin(self) -> Optional[datetime]: """ Returns the starting time of ``self``. @@ -447,9 +447,7 @@ def tmin(self) -> datetime: stbox_tmin """ result = stbox_tmin(self._inner) - if not result: - return None - return timestamptz_to_datetime(result) + return timestamptz_to_datetime(result) if result else None def tmin_inc(self) -> bool: """ @@ -464,7 +462,7 @@ def tmin_inc(self) -> bool: """ return stbox_tmin_inc(self._inner) - def xmax(self) -> float: + def xmax(self) -> Optional[float]: """ Returns the maximum X coordinate of ``self``. @@ -476,7 +474,7 @@ def xmax(self) -> float: """ return stbox_xmax(self._inner) - def ymax(self) -> float: + def ymax(self) -> Optional[float]: """ Returns the maximum Y coordinate of ``self``. @@ -488,7 +486,7 @@ def ymax(self) -> float: """ return stbox_ymax(self._inner) - def zmax(self) -> float: + def zmax(self) -> Optional[float]: """ Returns the maximum Z coordinate of ``self``. @@ -500,7 +498,7 @@ def zmax(self) -> float: """ return stbox_zmax(self._inner) - def tmax(self) -> datetime: + def tmax(self) -> Optional[datetime]: """ Returns the ending time of ``self``. @@ -511,9 +509,7 @@ def tmax(self) -> datetime: stbox_tmax """ result = stbox_tmax(self._inner) - if not result: - return None - return timestamptz_to_datetime(result) + return timestamptz_to_datetime(result) if result else None def tmax_inc(self) -> bool: """ diff --git a/pymeos/pymeos/collections/number/floatspan.py b/pymeos/pymeos/collections/number/floatspan.py index da4d7b9f..c7b44584 100644 --- a/pymeos/pymeos/collections/number/floatspan.py +++ b/pymeos/pymeos/collections/number/floatspan.py @@ -396,7 +396,7 @@ def minus(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> FloatSpanS elif isinstance(other, FloatSpanSet): result = minus_spanset_span(other._inner, self._inner) else: - super().minus(other) + result = super().minus(other) return FloatSpanSet(_inner=result) if result is not None else None def union(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> FloatSpanSet: @@ -420,5 +420,5 @@ def union(self, other: Union[int, float, FloatSpan, FloatSpanSet]) -> FloatSpanS elif isinstance(other, FloatSpanSet): result = union_spanset_span(other._inner, self._inner) else: - super().union(other) + result = super().union(other) return FloatSpanSet(_inner=result) if result is not None else None diff --git a/pymeos/pymeos/collections/number/floatspanset.py b/pymeos/pymeos/collections/number/floatspanset.py index c9d0bb26..5654960d 100644 --- a/pymeos/pymeos/collections/number/floatspanset.py +++ b/pymeos/pymeos/collections/number/floatspanset.py @@ -172,7 +172,7 @@ def shift(self, delta: int) -> FloatSpanSet: """ return self.shift_scale(delta, None) - def scale(self, width: int) -> FloatSpan: + def scale(self, width: int) -> FloatSpanSet: """ Return a new ``FloatSpanSet`` with the lower and upper bounds scaled so that the width is ``width``. diff --git a/pymeos/pymeos/collections/number/intspan.py b/pymeos/pymeos/collections/number/intspan.py index 2ce60355..9d7d6226 100644 --- a/pymeos/pymeos/collections/number/intspan.py +++ b/pymeos/pymeos/collections/number/intspan.py @@ -392,7 +392,7 @@ def minus(self, other: Union[int, IntSpan, IntSpanSet]) -> IntSpanSet: elif isinstance(other, IntSpanSet): result = minus_spanset_span(other._inner, self._inner) else: - super().minus(other) + result = super().minus(other) return IntSpanSet(_inner=result) if result is not None else None def union(self, other: Union[int, IntSpan, IntSpanSet]) -> IntSpanSet: @@ -416,5 +416,5 @@ def union(self, other: Union[int, IntSpan, IntSpanSet]) -> IntSpanSet: elif isinstance(other, IntSpanSet): result = union_spanset_span(other._inner, self._inner) else: - super().union(other) + result = super().union(other) return IntSpanSet(_inner=result) if result is not None else None diff --git a/pymeos/pymeos/collections/number/intspanset.py b/pymeos/pymeos/collections/number/intspanset.py index b4e27fc4..f04f5f04 100644 --- a/pymeos/pymeos/collections/number/intspanset.py +++ b/pymeos/pymeos/collections/number/intspanset.py @@ -172,7 +172,7 @@ def shift(self, delta: int) -> IntSpanSet: """ return self.shift_scale(delta, None) - def scale(self, width: int) -> IntSpan: + def scale(self, width: int) -> IntSpanSet: """ Return a new ``IntSpanSet`` with the lower and upper bounds scaled so that the width is ``width``. diff --git a/pymeos/pymeos/collections/time/period.py b/pymeos/pymeos/collections/time/period.py index 3a96b16b..ddf07176 100644 --- a/pymeos/pymeos/collections/time/period.py +++ b/pymeos/pymeos/collections/time/period.py @@ -603,7 +603,7 @@ def minus(self, other: Time) -> PeriodSet: elif isinstance(other, PeriodSet): result = minus_span_spanset(self._inner, other._inner) else: - super().minus(other) + result = super().minus(other) return PeriodSet(_inner=result) if result is not None else None def union(self, other: Time) -> PeriodSet: diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index fdbe0a8c..acb29e8c 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -177,10 +177,7 @@ def value_spans(self) -> FloatSpanSet: MEOS Functions: tfloat_spanset """ - spanset = tnumber_valuespans(self._inner) - spans = spanset_spans(spanset) - count = spanset_num_spans(spanset) - return [floatspan_to_floatrange(spans[i]) for i in range(count)] + return FloatSpanSet(_inner=(tnumber_valuespans(self._inner))) def start_value(self) -> float: """ diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index eb257423..bb0515cd 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -88,7 +88,7 @@ def test_string_constructor(self, source, type, interpolation, expected): ids=['Sequence', 'SequenceSet'] ) def test_string_constructor_normalization(self, source, type, expected): - tf = type(source, normalize=1) + tf = type(source, normalize=True) assert isinstance(tf, type) assert str(tf) == expected diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index 21b5543c..87ab082c 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -95,7 +95,7 @@ def test_string_constructor(self, source, type, interpolation, expected): ids=['Sequence', 'SequenceSet'] ) def test_string_constructor_normalization(self, source, type, expected): - tp = type(source, normalize=1) + tp = type(source, normalize=True) assert isinstance(tp, type) assert str(tp) == expected @@ -1911,6 +1911,45 @@ def test_dyntimewarp_distance(self, temporal, argument, expected): def test_hausdorff_distance(self, temporal, argument, expected): assert temporal.hausdorff_distance(argument) == expected + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tpi, TGeomPointInst('Point(3 3)@2019-09-02'), 2.83), + (tpds, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), + (tps, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), + (tpss, TGeomPointInst('Point(3 3)@2019-09-08'), 2.83), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] + ) + def test_frechet_distance_round(self, temporal, argument, expected): + assert round(temporal.frechet_distance(argument), 2) == expected + + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tpi, TGeomPointInst('Point(3 3)@2019-09-02'), 2.83), + (tpds, TGeomPointInst('Point(3 3)@2019-09-03'), 4.24), + (tps, TGeomPointInst('Point(3 3)@2019-09-03'), 4.24), + (tpss, TGeomPointInst('Point(3 3)@2019-09-08'), 9.9), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] + ) + def test_dyntimewarp_distance_round(self, temporal, argument, expected): + assert round(temporal.dyntimewarp_distance(argument), 2) == expected + + @pytest.mark.parametrize( + 'temporal, argument, expected', + [ + (tpi, TGeomPointInst('Point(3 3)@2019-09-02'), 2.83), + (tpds, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), + (tps, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), + (tpss, TGeomPointInst('Point(3 3)@2019-09-08'), 2.83), + ], + ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] + ) + def test_hausdorff_distance_round(self, temporal, argument, expected): + assert round(temporal.hausdorff_distance(argument), 2) == expected + class TestTGeomPointEverSpatialOperations(TestTGeomPoint): tpi = TGeomPointInst('Point(1 1)@2019-09-01') @@ -2126,53 +2165,6 @@ def test_shortest_line(self, temporal, argument): assert temporal.shortest_line(argument) == LineString([(1,1), (1,1)]) -class TestTGeomPointSimilarityFunctions(TestTGeomPoint): - tfi = TGeomPointInst('Point(1 1)@2019-09-01') - tfds = TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') - tfs = TGeomPointSeq('[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02]') - tfss = TGeomPointSeqSet('{[Point(1 1)@2019-09-01, Point(2 2)@2019-09-02],' - '[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]}') - - @pytest.mark.parametrize( - 'temporal, argument, expected', - [ - (tfi, TGeomPointInst('Point(3 3)@2019-09-02'), 2.83), - (tfds, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), - (tfs, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), - (tfss, TGeomPointInst('Point(3 3)@2019-09-08'), 2.83), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] - ) - def test_frechet_distance(self, temporal, argument, expected): - assert round(temporal.frechet_distance(argument), 2) == expected - - @pytest.mark.parametrize( - 'temporal, argument, expected', - [ - (tfi, TGeomPointInst('Point(3 3)@2019-09-02'), 2.83), - (tfds, TGeomPointInst('Point(3 3)@2019-09-03'), 4.24), - (tfs, TGeomPointInst('Point(3 3)@2019-09-03'), 4.24), - (tfss, TGeomPointInst('Point(3 3)@2019-09-08'), 9.9), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] - ) - def test_dyntimewarp_distance(self, temporal, argument, expected): - assert round(temporal.dyntimewarp_distance(argument), 2) == expected - - @pytest.mark.parametrize( - 'temporal, argument, expected', - [ - (tfi, TGeomPointInst('Point(3 3)@2019-09-02'), 2.83), - (tfds, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), - (tfs, TGeomPointInst('Point(3 3)@2019-09-03'), 2.83), - (tfss, TGeomPointInst('Point(3 3)@2019-09-08'), 2.83), - ], - ids=['Instant', 'Discrete Sequence', 'Sequence', 'Sequence Set'] - ) - def test_hausdorff_distance(self, temporal, argument, expected): - assert round(temporal.hausdorff_distance(argument), 2) == expected - - class TestTGeomPointSplitOperations(TestTGeomPoint): tpi = TGeomPointInst('Point(1 1)@2019-09-01') tpds = TGeomPointSeq('{Point(1 1)@2019-09-01, Point(2 2)@2019-09-02}') diff --git a/pymeos/tests/main/tint_test.py b/pymeos/tests/main/tint_test.py index bb0498e8..aed0676b 100644 --- a/pymeos/tests/main/tint_test.py +++ b/pymeos/tests/main/tint_test.py @@ -85,7 +85,7 @@ def test_string_constructor(self, source, type, interpolation, expected): ids=['Sequence', 'SequenceSet'] ) def test_string_constructor_normalization(self, source, type, expected): - ti = type(source, normalize=1) + ti = type(source, normalize=True) assert isinstance(ti, type) assert str(ti) == expected diff --git a/pymeos/tests/main/ttext_test.py b/pymeos/tests/main/ttext_test.py index 0f72c129..9e99adef 100644 --- a/pymeos/tests/main/ttext_test.py +++ b/pymeos/tests/main/ttext_test.py @@ -82,7 +82,7 @@ def test_string_constructor(self, source, type, interpolation, expected): ids=['Sequence', 'SequenceSet'] ) def test_string_constructor_normalization(self, source, type, expected): - tt = type(source, normalize=1) + tt = type(source, normalize=True) assert isinstance(tt, type) assert str(tt) == expected From 31690b2c99804be2d0f9d30f5cc3304a718bcbd9 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 2 Oct 2023 12:24:18 +0200 Subject: [PATCH 073/101] Remove some outdated references to old ranges from the spans package --- pymeos/docs/conf.py | 1 - pymeos/pymeos/main/tfloat.py | 4 ++-- pymeos/pymeos/main/tint.py | 2 +- pymeos/pymeos/plotters/box_plotter.py | 4 ++-- pymeos/pymeos/plotters/range_plotter.py | 4 ++-- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pymeos/docs/conf.py b/pymeos/docs/conf.py index cc319cbe..25b030ba 100644 --- a/pymeos/docs/conf.py +++ b/pymeos/docs/conf.py @@ -29,7 +29,6 @@ # -- Intersphinx config -------- intersphinx_mapping = { - 'spans': ('https://runfalk.github.io/spans/', None), 'asyncpg': ('https://magicstack.github.io/asyncpg/current/', None), 'psycopg': ('https://www.psycopg.org/psycopg3/docs/', None), 'psycopg2': ('https://www.psycopg.org/docs/', None), diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index acb29e8c..740e84da 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -172,10 +172,10 @@ def value_spans(self) -> FloatSpanSet: Returns the value spans of `self` taking into account gaps. Returns: - A list of :class:`floatrange` with the value spans of `self`. + A :class:`FloatSpanSet` with the value spans of `self`. MEOS Functions: - tfloat_spanset + tnumber_valuespans """ return FloatSpanSet(_inner=(tnumber_valuespans(self._inner))) diff --git a/pymeos/pymeos/main/tint.py b/pymeos/pymeos/main/tint.py index ac9f86f3..745fd20a 100644 --- a/pymeos/pymeos/main/tint.py +++ b/pymeos/pymeos/main/tint.py @@ -157,7 +157,7 @@ def value_spans(self) -> IntSpanSet: An :class:`IntSpanSet` with the value spans of `self`. MEOS Functions: - tint_spanset + tnumber_valuespans """ return IntSpanSet(_inner=tnumber_valuespans(self._inner)) diff --git a/pymeos/pymeos/plotters/box_plotter.py b/pymeos/pymeos/plotters/box_plotter.py index 6c0f262d..e9fd5c63 100644 --- a/pymeos/pymeos/plotters/box_plotter.py +++ b/pymeos/pymeos/plotters/box_plotter.py @@ -14,7 +14,7 @@ class BoxPlotter: def plot_tbox(tbox: TBox, *args, axes=None, **kwargs): """ Plot a TBox on the given axes. If the TBox has only a temporal or spatial dimension, this is equivalent - to plotting the corresponding Period or Range respectively. + to plotting the corresponding Period or Span respectively. Params: tbox: The :class:`TBox` to plot. @@ -30,7 +30,7 @@ def plot_tbox(tbox: TBox, *args, axes=None, **kwargs): :func:`~pymeos.plotters.time_plotter.TimePlotter.plot_period` """ if not tbox.has_t: - return SpanPlotter.plot_range(tbox.to_floatspan(), *args, axes=axes, **kwargs) + return SpanPlotter.plot_span(tbox.to_floatspan(), *args, axes=axes, **kwargs) if not tbox.has_x: return TimePlotter.plot_period(tbox.to_period(), *args, axes=axes, **kwargs) return BoxPlotter._plot_box(tbox.tmin(), tbox.tmax(), tbox.xmin(), tbox.xmax(), *args, axes=axes, **kwargs) diff --git a/pymeos/pymeos/plotters/range_plotter.py b/pymeos/pymeos/plotters/range_plotter.py index f20f5ca4..e5584fb4 100644 --- a/pymeos/pymeos/plotters/range_plotter.py +++ b/pymeos/pymeos/plotters/range_plotter.py @@ -10,12 +10,12 @@ class SpanPlotter: """ @staticmethod - def plot_range(span: Union[IntSpan, FloatSpan], *args, axes=None, **kwargs): + def plot_span(span: Union[IntSpan, FloatSpan], *args, axes=None, **kwargs): """ Plot a :class:`FloatSpan` or :class:`IntSpan` on the given axes. Params: - range: The :class:`FloatSpan` or :class:`IntSpan` to plot. + span: The :class:`FloatSpan` or :class:`IntSpan` to plot. axes: The axes to plot on. If None, the current axes are used. *args: Additional arguments to pass to the plot function. **kwargs: Additional keyword arguments to pass to the plot function. From c279bf3e734c3ccff2a9a13fc688a2d52063cd8b Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 2 Oct 2023 15:17:22 +0200 Subject: [PATCH 074/101] Remove spans dependency and postgis support from pymeos_cffi --- pymeos_cffi/pymeos_cffi/__init__.py | 4 -- .../builder/templates/functions.py | 54 ++++--------------- pymeos_cffi/pymeos_cffi/functions.py | 54 ++++--------------- pymeos_cffi/pyproject.toml | 4 +- 4 files changed, 21 insertions(+), 95 deletions(-) diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 88cc9c9a..4d335512 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -39,10 +39,6 @@ 'geography_to_gserialized', 'gserialized_to_shapely_point', 'gserialized_to_shapely_geometry', - 'intrange_to_intspan', - 'intspan_to_intrange', - 'floatrange_to_floatspan', - 'floatspan_to_floatrange', 'as_tinstant', 'as_tsequence', 'as_tsequenceset', diff --git a/pymeos_cffi/pymeos_cffi/builder/templates/functions.py b/pymeos_cffi/pymeos_cffi/builder/templates/functions.py index 0b190530..0faffd00 100644 --- a/pymeos_cffi/pymeos_cffi/builder/templates/functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/templates/functions.py @@ -3,12 +3,10 @@ import _meos_cffi from .errors import raise_meos_exception -import postgis as pg import shapely.geometry as spg from dateutil.parser import parse -from shapely import wkt, wkb, get_srid +from shapely import wkt, get_srid from shapely.geometry.base import BaseGeometry -from spans.types import floatrange, intrange _ffi = _meos_cffi.ffi _lib = _meos_cffi.lib @@ -63,39 +61,25 @@ def interval_to_timedelta(interval: Any) -> timedelta: return timedelta(days=interval.day, microseconds=interval.time) -def geo_to_gserialized(geom: Union[pg.Geometry, BaseGeometry], geodetic: bool) -> 'GSERIALIZED *': +def geo_to_gserialized(geom: BaseGeometry, geodetic: bool) -> 'GSERIALIZED *': if geodetic: return geography_to_gserialized(geom) else: return geometry_to_gserialized(geom) -def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': - if isinstance(geom, pg.Geometry): - text = geom.wkt - # if geom.has_srid(): - # text = f'SRID={geom.srid};{text}' - elif isinstance(geom, BaseGeometry): - text = wkt.dumps(geom) - if get_srid(geom) > 0: - text = f'SRID={get_srid(geom)};{text}' - else: - raise TypeError('Parameter geom must be either a PostGIS Geometry or a Shapely BaseGeometry') +def geometry_to_gserialized(geom: BaseGeometry) -> 'GSERIALIZED *': + text = wkt.dumps(geom) + if get_srid(geom) > 0: + text = f'SRID={get_srid(geom)};{text}' gs = pgis_geometry_in(text, -1) return gs -def geography_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': - if isinstance(geom, pg.Geometry): - text = geom.wkt - # if geom.has_srid(): - # text = f'SRID={geom.srid};{text}' - elif isinstance(geom, BaseGeometry): - text = wkt.dumps(geom) - if get_srid(geom) > 0: - text = f'SRID={get_srid(geom)};{text}' - else: - raise TypeError('Parameter geom must be either a PostGIS Geometry or a Shapely BaseGeometry') +def geography_to_gserialized(geom: BaseGeometry) -> 'GSERIALIZED *': + text = wkt.dumps(geom) + if get_srid(geom) > 0: + text = f'SRID={get_srid(geom)};{text}' gs = pgis_geography_in(text, -1) return gs @@ -108,22 +92,6 @@ def gserialized_to_shapely_geometry(geom: 'const GSERIALIZED *', precision: int return wkt.loads(gserialized_as_text(geom, precision)) -def intrange_to_intspan(irange: intrange) -> 'Span *': - return intspan_make(irange.lower, irange.upper, irange.lower_inc, irange.upper_inc) - - -def intspan_to_intrange(ispan: 'Span *') -> intrange: - return intrange(intspan_lower(ispan), intspan_upper(ispan), ispan.lower_inc, ispan.upper_inc) - - -def floatrange_to_floatspan(frange: floatrange) -> 'Span *': - return floatspan_make(frange.lower, frange.upper, frange.lower_inc, frange.upper_inc) - - -def floatspan_to_floatrange(fspan: 'Span *') -> floatrange: - return floatrange(floatspan_lower(fspan), floatspan_upper(fspan), fspan.lower_inc, fspan.upper_inc) - - def as_tinstant(temporal: 'Temporal *') -> 'TInstant *': return _ffi.cast('TInstant *', temporal) @@ -135,8 +103,6 @@ def as_tsequence(temporal: 'Temporal *') -> 'TSequence *': def as_tsequenceset(temporal: 'Temporal *') -> 'TSequenceSet *': return _ffi.cast('TSequenceSet *', temporal) - # ----------------------------------------------------------------------------- # ----------------------End of manually-defined functions---------------------- # ----------------------------------------------------------------------------- - diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 2bdad684..de6818ca 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -3,12 +3,10 @@ import _meos_cffi from .errors import raise_meos_exception -import postgis as pg import shapely.geometry as spg from dateutil.parser import parse -from shapely import wkt, wkb, get_srid +from shapely import wkt, get_srid from shapely.geometry.base import BaseGeometry -from spans.types import floatrange, intrange _ffi = _meos_cffi.ffi _lib = _meos_cffi.lib @@ -63,39 +61,25 @@ def interval_to_timedelta(interval: Any) -> timedelta: return timedelta(days=interval.day, microseconds=interval.time) -def geo_to_gserialized(geom: Union[pg.Geometry, BaseGeometry], geodetic: bool) -> 'GSERIALIZED *': +def geo_to_gserialized(geom: BaseGeometry, geodetic: bool) -> 'GSERIALIZED *': if geodetic: return geography_to_gserialized(geom) else: return geometry_to_gserialized(geom) -def geometry_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': - if isinstance(geom, pg.Geometry): - text = geom.wkt - # if geom.has_srid(): - # text = f'SRID={geom.srid};{text}' - elif isinstance(geom, BaseGeometry): - text = wkt.dumps(geom) - if get_srid(geom) > 0: - text = f'SRID={get_srid(geom)};{text}' - else: - raise TypeError('Parameter geom must be either a PostGIS Geometry or a Shapely BaseGeometry') +def geometry_to_gserialized(geom: BaseGeometry) -> 'GSERIALIZED *': + text = wkt.dumps(geom) + if get_srid(geom) > 0: + text = f'SRID={get_srid(geom)};{text}' gs = pgis_geometry_in(text, -1) return gs -def geography_to_gserialized(geom: Union[pg.Geometry, BaseGeometry]) -> 'GSERIALIZED *': - if isinstance(geom, pg.Geometry): - text = geom.wkt - # if geom.has_srid(): - # text = f'SRID={geom.srid};{text}' - elif isinstance(geom, BaseGeometry): - text = wkt.dumps(geom) - if get_srid(geom) > 0: - text = f'SRID={get_srid(geom)};{text}' - else: - raise TypeError('Parameter geom must be either a PostGIS Geometry or a Shapely BaseGeometry') +def geography_to_gserialized(geom: BaseGeometry) -> 'GSERIALIZED *': + text = wkt.dumps(geom) + if get_srid(geom) > 0: + text = f'SRID={get_srid(geom)};{text}' gs = pgis_geography_in(text, -1) return gs @@ -108,22 +92,6 @@ def gserialized_to_shapely_geometry(geom: 'const GSERIALIZED *', precision: int return wkt.loads(gserialized_as_text(geom, precision)) -def intrange_to_intspan(irange: intrange) -> 'Span *': - return intspan_make(irange.lower, irange.upper, irange.lower_inc, irange.upper_inc) - - -def intspan_to_intrange(ispan: 'Span *') -> intrange: - return intrange(intspan_lower(ispan), intspan_upper(ispan), ispan.lower_inc, ispan.upper_inc) - - -def floatrange_to_floatspan(frange: floatrange) -> 'Span *': - return floatspan_make(frange.lower, frange.upper, frange.lower_inc, frange.upper_inc) - - -def floatspan_to_floatrange(fspan: 'Span *') -> floatrange: - return floatrange(floatspan_lower(fspan), floatspan_upper(fspan), fspan.lower_inc, fspan.upper_inc) - - def as_tinstant(temporal: 'Temporal *') -> 'TInstant *': return _ffi.cast('TInstant *', temporal) @@ -135,11 +103,9 @@ def as_tsequence(temporal: 'Temporal *') -> 'TSequence *': def as_tsequenceset(temporal: 'Temporal *') -> 'TSequenceSet *': return _ffi.cast('TSequenceSet *', temporal) - # ----------------------------------------------------------------------------- # ----------------------End of manually-defined functions---------------------- # ----------------------------------------------------------------------------- - def lwpoint_make(srid: 'int32_t', hasz: int, hasm: int, p: 'const POINT4D *') -> 'LWPOINT *': srid_converted = _ffi.cast('int32_t', srid) p_converted = _ffi.cast('const POINT4D *', p) diff --git a/pymeos_cffi/pyproject.toml b/pymeos_cffi/pyproject.toml index 8a74b572..d51fb66f 100644 --- a/pymeos_cffi/pyproject.toml +++ b/pymeos_cffi/pyproject.toml @@ -7,7 +7,7 @@ py-modules = [] [project] name = 'pymeos_cffi' -version = '1.1.0-alpha.4' +version = '1.1.0-alpha.5' authors = [ { name = 'Victor Divi', email = 'vdiviloper@gmail.com' } ] @@ -37,8 +37,6 @@ requires-python = '>=3.7' dependencies = [ 'cffi', 'python-dateutil', - 'spans', - 'postgis', 'shapely' ] From c5a01e069d95b429ce06d725e5d97d8f20ec2b8c Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 2 Oct 2023 15:32:37 +0200 Subject: [PATCH 075/101] Remove postgis support from PyMEOS --- pymeos/pymeos/__init__.py | 2 +- pymeos/pymeos/boxes/stbox.py | 69 ++++----- pymeos/pymeos/collections/__init__.py | 2 +- pymeos/pymeos/collections/geo/__init__.py | 4 +- pymeos/pymeos/main/tpoint.py | 178 ++++++++++------------ pymeos/pyproject.toml | 1 - 6 files changed, 121 insertions(+), 135 deletions(-) diff --git a/pymeos/pymeos/__init__.py b/pymeos/pymeos/__init__.py index 48dbb6fb..39827a3a 100644 --- a/pymeos/pymeos/__init__.py +++ b/pymeos/pymeos/__init__.py @@ -26,7 +26,7 @@ 'TextSet', 'IntSet', 'IntSpan', 'IntSpanSet', 'FloatSet', 'FloatSpan', 'FloatSpanSet', - 'GeometrySet', 'GeographySet', + 'GeoSet', 'GeometrySet', 'GeographySet', # extras 'TInterpolation', # aggregators diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index 893e0237..27347c44 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -2,7 +2,6 @@ from typing import Optional, Union, List, TYPE_CHECKING, get_args -import postgis as pg import shapely.geometry.base as shp from pymeos_cffi import * @@ -10,8 +9,6 @@ from ..temporal import Temporal from ..collections import * -Geometry = Union[pg.Geometry, shp.BaseGeometry] - if TYPE_CHECKING: from .box import Box @@ -42,10 +39,10 @@ class STBox: _mobilitydb_name = 'stbox' - def _get_box(self, other: Union[Geometry, STBox, Temporal, Time], + def _get_box(self, other: Union[shp.BaseGeometry, STBox, Temporal, Time], allow_space_only: bool = True, allow_time_only: bool = False) -> STBox: - if allow_space_only and isinstance(other, get_args(Geometry)): + if allow_space_only and isinstance(other, shp.BaseGeometry): other_box = geo_to_stbox(geo_to_gserialized(other, self.geodetic())) elif isinstance(other, STBox): other_box = other._inner @@ -152,12 +149,12 @@ def from_hexwkb(hexwkb: str) -> STBox: return STBox(_inner=result) @staticmethod - def from_geometry(geom: Geometry, geodetic: bool = False) -> STBox: + def from_geometry(geom: shp.BaseGeometry, geodetic: bool = False) -> STBox: """ - Returns a `STBox` from a `Geometry`. + Returns a `STBox` from a `shp.BaseGeometry`. Args: - geom: A `Geometry` instance. + geom: A `shp.BaseGeometry` instance. geodetic: Whether to create a geodetic or geometric `STBox`. Returns: @@ -197,13 +194,13 @@ def from_time(time: Time) -> STBox: return STBox(_inner=result) @staticmethod - def from_geometry_time(geometry: Geometry, time: Union[datetime, Period], + def from_geometry_time(geometry: shp.BaseGeometry, time: Union[datetime, Period], geodetic: bool = False) -> STBox: """ Returns a `STBox` from a space and time dimension. Args: - geometry: A `Geometry` instance representing the space dimension. + geometry: A `shp.BaseGeometry` instance representing the space dimension. time: A `Time` instance representing the time dimension. geodetic: Whether to create a geodetic or geometric `STBox`. @@ -240,17 +237,17 @@ def from_tpoint(temporal: TPoint) -> STBox: return STBox(_inner=tpoint_to_stbox(temporal._inner)) @staticmethod - def from_expanding_bounding_box(value: Union[Geometry, TPoint, STBox], + def from_expanding_bounding_box(value: Union[shp.BaseGeometry, TPoint, STBox], expansion: float, geodetic: Optional[bool] = False) -> STBox: """ - Returns a `STBox` from a `Geometry`, `TPoint` or `STBox` instance, + Returns a `STBox` from a `shp.BaseGeometry`, `TPoint` or `STBox` instance, expanding its bounding box by the given amount. Args: - value: A `Geometry`, `TPoint` or `STBox` instance. + value: A `shp.BaseGeometry`, `TPoint` or `STBox` instance. expansion: The amount to expand the bounding box. geodetic: Whether to create a geodetic or geometric `STBox`. - Only used when value is a `Geometry` instance. + Only used when value is a `shp.BaseGeometry` instance. Returns: A new :class:`STBox` instance. @@ -258,7 +255,7 @@ def from_expanding_bounding_box(value: Union[Geometry, TPoint, STBox], MEOS Functions: geo_expand_space, tpoint_expand_space, stbox_expand_space """ - if isinstance(value, get_args(Geometry)): + if isinstance(value, shp.BaseGeometry): gs = geo_to_gserialized(value, geodetic) result = geo_expand_space(gs, expansion) elif isinstance(value, TPoint): @@ -743,7 +740,7 @@ def __mul__(self, other): return self.intersection(other) # ------------------------- Topological Operations ------------------------ - def is_adjacent(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: + def is_adjacent(self, other: Union[shp.BaseGeometry, STBox, Temporal, Time]) -> bool: """ Returns whether ``self`` and `other` are adjacent. Two spatiotemporal boxes are adjacent if they share n dimensions and the intersection is @@ -764,7 +761,7 @@ def is_adjacent(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: allow_time_only=True)) def is_contained_in(self, - container: Union[Geometry, STBox, Temporal, Time]) -> bool: + container: Union[shp.BaseGeometry, STBox, Temporal, Time]) -> bool: """ Returns whether ``self`` is contained in `container`. Note that for `TPoint` instances, the bounding box of the temporal point is used. @@ -782,7 +779,7 @@ def is_contained_in(self, return contained_stbox_stbox(self._inner, self._get_box(container, allow_time_only=True)) - def contains(self, content: Union[Geometry, STBox, Temporal, Time]) -> bool: + def contains(self, content: Union[shp.BaseGeometry, STBox, Temporal, Time]) -> bool: """ Returns whether ``self`` contains `content`. Note that for `TPoint` instances, the bounding box of the temporal point is used. @@ -819,7 +816,7 @@ def __contains__(self, item): """ return self.contains(item) - def overlaps(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: + def overlaps(self, other: Union[shp.BaseGeometry, STBox, Temporal, Time]) -> bool: """ Returns whether ``self`` overlaps `other`. Note that for `TPoint` instances, the bounding box of the temporal point is used. @@ -836,7 +833,7 @@ def overlaps(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: return overlaps_stbox_stbox(self._inner, self._get_box(other, allow_time_only=True)) - def is_same(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: + def is_same(self, other: Union[shp.BaseGeometry, STBox, Temporal, Time]) -> bool: """ Returns whether ``self`` is the same as `other`. Note that for `TPoint` instances, the bounding box of the temporal point is used. @@ -854,7 +851,7 @@ def is_same(self, other: Union[Geometry, STBox, Temporal, Time]) -> bool: allow_time_only=True)) # ------------------------- Position Operations --------------------------- - def is_left(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_left(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is strictly to the left of `other`. Checks the X dimension. @@ -871,7 +868,7 @@ def is_left(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return left_stbox_stbox(self._inner, self._get_box(other)) - def is_over_or_left(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_over_or_left(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is to the left `other` allowing for overlap. That is, ``self`` does not extend to the right of `other`. Checks the @@ -889,7 +886,7 @@ def is_over_or_left(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return overleft_stbox_stbox(self._inner, self._get_box(other)) - def is_right(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_right(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is strictly to the right of `other`. Checks the X dimension. @@ -906,7 +903,7 @@ def is_right(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return right_stbox_stbox(self._inner, self._get_box(other)) - def is_over_or_right(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_over_or_right(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is to the right of `other` allowing for overlap. That is, ``self`` does not extend to the left of `other`. @@ -924,7 +921,7 @@ def is_over_or_right(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return overright_stbox_stbox(self._inner, self._get_box(other)) - def is_below(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_below(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is strictly below `other`. Checks the Y dimension. @@ -941,7 +938,7 @@ def is_below(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return below_stbox_stbox(self._inner, self._get_box(other)) - def is_over_or_below(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_over_or_below(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is below `other` allowing for overlap. That is, ``self`` does not extend above `other`. Checks the Y dimension. @@ -958,7 +955,7 @@ def is_over_or_below(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return overbelow_stbox_stbox(self._inner, self._get_box(other)) - def is_above(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_above(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is strictly above `other`. Checks the Y dimension. @@ -975,7 +972,7 @@ def is_above(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return above_stbox_stbox(self._inner, self._get_box(other)) - def is_over_or_above(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_over_or_above(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is above `other` allowing for overlap. That is, ``self`` does not extend below `other`. @@ -993,7 +990,7 @@ def is_over_or_above(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return overabove_stbox_stbox(self._inner, self._get_box(other)) - def is_front(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_front(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is strictly in front of `other`. Checks the Z dimension. @@ -1010,7 +1007,7 @@ def is_front(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return front_stbox_stbox(self._inner, self._get_box(other)) - def is_over_or_front(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_over_or_front(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is in front of `other` allowing for overlap. That is, ``self`` does not extend behind `other`. @@ -1028,7 +1025,7 @@ def is_over_or_front(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return overfront_stbox_stbox(self._inner, self._get_box(other)) - def is_behind(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_behind(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is strictly behind `other`. Checks the Z dimension. @@ -1045,7 +1042,7 @@ def is_behind(self, other: Union[Geometry, STBox, TPoint]) -> bool: """ return back_stbox_stbox(self._inner, self._get_box(other)) - def is_over_or_behind(self, other: Union[Geometry, STBox, TPoint]) -> bool: + def is_over_or_behind(self, other: Union[shp.BaseGeometry, STBox, TPoint]) -> bool: """ Returns whether ``self`` is behind `other` allowing for overlap. That is, ``self`` does not extend in front of `other`. @@ -1122,7 +1119,7 @@ def is_over_or_after(self, other: Union[Box, Temporal, Time]) -> bool: return self.to_period().is_over_or_after(other) # ------------------------- Distance Operations --------------------------- - def nearest_approach_distance(self, other: Union[Geometry, STBox, TPoint]) \ + def nearest_approach_distance(self, other: Union[shp.BaseGeometry, STBox, TPoint]) \ -> float: """ Returns the distance between the nearest points of ``self`` and `other`. @@ -1137,7 +1134,7 @@ def nearest_approach_distance(self, other: Union[Geometry, STBox, TPoint]) \ MEOS Functions: nad_stbox_geo, nad_stbox_stbox """ - if isinstance(other, get_args(Geometry)): + if isinstance(other, shp.BaseGeometry): gs = geo_to_gserialized(other, self.geodetic()) return nad_stbox_geo(self._inner, gs) elif isinstance(other, STBox): @@ -1217,7 +1214,7 @@ def quad_split(self) -> Union[List[List[STBox]], List[List[List[STBox]]]]: def tile(self, size: Optional[float] = None, duration: Optional[Union[timedelta, str]] = None, - origin: Optional[Geometry] = None, + origin: Optional[shp.BaseGeometry] = None, start: Union[datetime, str, None] = None) -> \ List[List[List[List[STBox]]]]: """ @@ -1270,7 +1267,7 @@ def tile(self, size: Optional[float] = None, def tile_flat(self, size: float, duration: Optional[Union[timedelta, str]] = None, - origin: Optional[Geometry] = None, + origin: Optional[shp.BaseGeometry] = None, start: Union[datetime, str, None] = None) -> List[STBox]: """ Returns a flat list of `STBox` instances representing the tiles of diff --git a/pymeos/pymeos/collections/__init__.py b/pymeos/pymeos/collections/__init__.py index 076fbb1f..de7c1c7c 100644 --- a/pymeos/pymeos/collections/__init__.py +++ b/pymeos/pymeos/collections/__init__.py @@ -9,7 +9,7 @@ 'Set', 'Span', 'SpanSet', 'Time', 'TimestampSet', 'Period', 'PeriodSet', 'datetime', 'timedelta', 'TextSet', - 'GeometrySet', 'GeographySet', + 'GeoSet', 'GeometrySet', 'GeographySet', 'IntSet', 'IntSpan', 'IntSpanSet', 'FloatSet', 'FloatSpan', 'FloatSpanSet' ] diff --git a/pymeos/pymeos/collections/geo/__init__.py b/pymeos/pymeos/collections/geo/__init__.py index e24207f2..30b84136 100644 --- a/pymeos/pymeos/collections/geo/__init__.py +++ b/pymeos/pymeos/collections/geo/__init__.py @@ -1,3 +1,3 @@ -from .geoset import GeometrySet, GeographySet +from .geoset import GeoSet, GeometrySet, GeographySet -__all__ = ['GeometrySet', 'GeographySet'] +__all__ = ['GeoSet', 'GeometrySet', 'GeographySet'] diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 9235a958..e7a7ab05 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -4,7 +4,6 @@ from functools import reduce from typing import Optional, List, TYPE_CHECKING, Set, Tuple, Union, TypeVar, Type, overload -import postgis as pg import shapely.geometry as shp import shapely.geometry.base as shpb from geopandas import GeoDataFrame @@ -297,7 +296,7 @@ def is_simple(self) -> bool: """ return tpoint_is_simple(self._inner) - def bearing(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint]) -> TFloat: + def bearing(self, other: Union[shpb.BaseGeometry, TPoint]) -> TFloat: """ Returns the temporal bearing between the temporal point and `other`. @@ -310,7 +309,7 @@ def bearing(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint]) -> TFloa MEOS Functions: bearing_tpoint_point, bearing_tpoint_tpoint """ - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = bearing_tpoint_point(self._inner, gs, False) elif isinstance(other, TPoint): @@ -441,8 +440,7 @@ def expand(self, other: Union[int, float]) -> STBox: return STBox(_inner=result) # ------------------------- Restrictions ---------------------------------- - def at(self, - other: Union[pg.Geometry, List[pg.Geometry], shpb.BaseGeometry, List[shpb.BaseGeometry], STBox, Time]) -> TG: + def at(self, other: Union[shpb.BaseGeometry, GeoSet, STBox, Time]) -> TG: """ Returns a new temporal object with the values of `self` restricted to `other`. @@ -453,26 +451,22 @@ def at(self, A new :TPoint: with the values of `self` restricted to `other`. MEOS Functions: - tpoint_at_geometry, tpoint_at_stbox, + tpoint_at_value, tpoint_at_stbox, temporal_at_values, temporal_at_timestamp, temporal_at_timestampset, temporal_at_period, temporal_at_periodset """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = tpoint_at_value(self._inner, gs) - elif isinstance(other, list): - gss = [geo_to_gserialized(gm, isinstance(self, TGeogPoint)) for gm in other] - results = [tpoint_at_value(self._inner, gs) for gs in gss] - result = temporal_merge_array(results, len(results)) + elif isinstance(other, GeoSet): + result = temporal_at_values(self._inner, other._inner) elif isinstance(other, STBox): result = tpoint_at_stbox(self._inner, other._inner, True) else: return super().at(other) return Temporal._factory(result) - def minus(self, - other: Union[pg.Geometry, List[pg.Geometry], shpb.BaseGeometry, List[shpb.BaseGeometry], STBox, Time] - ) -> TG: + def minus(self, other: Union[shpb.BaseGeometry, GeoSet, STBox, Time]) -> TG: """ Returns a new temporal object with the values of `self` restricted to the complement of `other`. @@ -483,16 +477,15 @@ def minus(self, A new :TPoint: with the values of `self` restricted to the complement of `other`. MEOS Functions: - tpoint_minus_geometry, tpoint_minus_stbox, + tpoint_minus_value, tpoint_minus_stbox, temporal_minus_values, temporal_minus_timestamp, temporal_minus_timestampset, temporal_minus_period, temporal_minus_periodset """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = tpoint_minus_value(self._inner, gs) - elif isinstance(other, list): - gss = [geo_to_gserialized(gm, isinstance(self, TGeogPoint)) for gm in other] - result = reduce(tpoint_minus_value, gss, self._inner) + elif isinstance(other, GeoSet): + result = temporal_minus_values(self._inner, other._inner) elif isinstance(other, STBox): result = tpoint_minus_stbox(self._inner, other._inner, True) else: @@ -681,7 +674,7 @@ def is_over_or_behind(self, other: Union[Temporal, Box]) -> bool: return self.bounding_box().is_over_or_behind(other) # ------------------------- Ever Spatial Relationships -------------------- - def is_ever_contained_in(self, container: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> bool: + def is_ever_contained_in(self, container: Union[shpb.BaseGeometry, STBox]) -> bool: """ Returns whether the temporal point is ever contained by `container`. @@ -695,7 +688,7 @@ def is_ever_contained_in(self, container: Union[pg.Geometry, shpb.BaseGeometry, econtains_geo_tpoint """ from ..boxes import STBox - if isinstance(container, pg.Geometry) or isinstance(container, shpb.BaseGeometry): + if isinstance(container, shpb.BaseGeometry): gs = geo_to_gserialized(container, isinstance(self, TGeogPoint)) result = econtains_geo_tpoint(gs, self._inner) elif isinstance(container, STBox): @@ -704,7 +697,7 @@ def is_ever_contained_in(self, container: Union[pg.Geometry, shpb.BaseGeometry, raise TypeError(f'Operation not supported with type {container.__class__}') return result == 1 - def is_ever_disjoint(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, STBox]) -> bool: + def is_ever_disjoint(self, other: Union[shpb.BaseGeometry, TPoint, STBox]) -> bool: """ Returns whether the temporal point is ever disjoint from `other`. @@ -718,7 +711,7 @@ def is_ever_disjoint(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, edisjoint_tpoint_geo, edisjoint_tpoint_tpoint """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = edisjoint_tpoint_geo(self._inner, gs) elif isinstance(other, STBox): @@ -729,7 +722,7 @@ def is_ever_disjoint(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, raise TypeError(f'Operation not supported with type {other.__class__}') return result == 1 - def is_ever_within_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, STBox], + def is_ever_within_distance(self, other: Union[shpb.BaseGeometry, TPoint, STBox], distance: float) -> bool: """ Returns whether the temporal point is ever within `distance` of `other`. @@ -745,7 +738,7 @@ def is_ever_within_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, T edwithin_tpoint_geo, edwithin_tpoint_tpoint """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = edwithin_tpoint_geo(self._inner, gs, distance) elif isinstance(other, STBox): @@ -756,7 +749,7 @@ def is_ever_within_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, T raise TypeError(f'Operation not supported with type {other.__class__}') return result == 1 - def ever_intersects(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, STBox]) -> bool: + def ever_intersects(self, other: Union[shpb.BaseGeometry, TPoint, STBox]) -> bool: """ Returns whether the temporal point ever intersects `other`. @@ -770,7 +763,7 @@ def ever_intersects(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, S eintersects_tpoint_geo, eintersects_tpoint_tpoint """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = eintersects_tpoint_geo(self._inner, gs) elif isinstance(other, STBox): @@ -781,7 +774,7 @@ def ever_intersects(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, S raise TypeError(f'Operation not supported with type {other.__class__}') return result == 1 - def ever_touches(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> bool: + def ever_touches(self, other: Union[shpb.BaseGeometry, STBox]) -> bool: """ Returns whether the temporal point ever touches `other`. @@ -795,7 +788,7 @@ def ever_touches(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> b etouches_tpoint_geo """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = etouches_tpoint_geo(self._inner, gs) elif isinstance(other, STBox): @@ -805,7 +798,7 @@ def ever_touches(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> b return result == 1 # ------------------------- Temporal Spatial Relationships ---------------- - def is_spatially_contained_in(self, container: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBool: + def is_spatially_contained_in(self, container: Union[shpb.BaseGeometry, STBox]) -> TBool: """ Returns a new temporal boolean indicating whether the temporal point is contained by `container`. @@ -819,7 +812,7 @@ def is_spatially_contained_in(self, container: Union[pg.Geometry, shpb.BaseGeome tcontains_geo_tpoint """ from ..boxes import STBox - if isinstance(container, pg.Geometry) or isinstance(container, shpb.BaseGeometry): + if isinstance(container, shpb.BaseGeometry): gs = geo_to_gserialized(container, isinstance(self, TGeogPoint)) result = tcontains_geo_tpoint(gs, self._inner, False, False) elif isinstance(container, STBox): @@ -829,7 +822,7 @@ def is_spatially_contained_in(self, container: Union[pg.Geometry, shpb.BaseGeome raise TypeError(f'Operation not supported with type {container.__class__}') return Temporal._factory(result) - def disjoint(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBool: + def disjoint(self, other: Union[shpb.BaseGeometry, STBox]) -> TBool: """ Returns a new temporal boolean indicating whether the temporal point intersects `other`. @@ -843,7 +836,7 @@ def disjoint(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBool tintersects_tpoint_geo """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = tdisjoint_tpoint_geo(self._inner, gs, False, False) elif isinstance(other, STBox): @@ -852,7 +845,7 @@ def disjoint(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBool raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) - def within_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, STBox], distance: float) -> TBool: + def within_distance(self, other: Union[shpb.BaseGeometry, TPoint, STBox], distance: float) -> TBool: """ Returns a new temporal boolean indicating whether the temporal point is within `distance` of `other`. @@ -867,7 +860,7 @@ def within_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, S tdwithin_tpoint_geo, tdwithin_tpoint_tpoint """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = tdwithin_tpoint_geo(self._inner, gs, distance, False, False) elif isinstance(other, STBox): @@ -878,7 +871,7 @@ def within_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, S raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) - def intersects(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBool: + def intersects(self, other: Union[shpb.BaseGeometry, STBox]) -> TBool: """ Returns a new temporal boolean indicating whether the temporal point intersects `other`. @@ -892,7 +885,7 @@ def intersects(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBo tintersects_tpoint_geo """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = tintersects_tpoint_geo(self._inner, gs, False, False) elif isinstance(other, STBox): @@ -901,7 +894,7 @@ def intersects(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBo raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) - def touches(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBool: + def touches(self, other: Union[shpb.BaseGeometry, STBox]) -> TBool: """ Returns a new temporal boolean indicating whether the temporal point touches `other`. @@ -915,7 +908,7 @@ def touches(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBool: ttouches_tpoint_geo """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = ttouches_tpoint_geo(self._inner, gs, False, False) elif isinstance(other, STBox): @@ -925,7 +918,7 @@ def touches(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox]) -> TBool: return Temporal._factory(result) # ------------------------- Distance Operations --------------------------- - def distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, STBox]) -> TFloat: + def distance(self, other: Union[shpb.BaseGeometry, TPoint, STBox]) -> TFloat: """ Returns the temporal distance between the temporal point and `other`. @@ -939,7 +932,7 @@ def distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, STBox]) distance_tpoint_point, distance_tpoint_tpoint """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = distance_tpoint_point(self._inner, gs) elif isinstance(other, STBox): @@ -950,7 +943,7 @@ def distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint, STBox]) raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) - def nearest_approach_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, STBox, TPoint]) -> float: + def nearest_approach_distance(self, other: Union[shpb.BaseGeometry, STBox, TPoint]) -> float: """ Returns the nearest approach distance between the temporal point and `other`. @@ -964,7 +957,7 @@ def nearest_approach_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, nad_tpoint_geo, nad_tpoint_stbox, nad_tpoint_tpoint """ from ..boxes import STBox - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) return nad_tpoint_geo(self._inner, gs) elif isinstance(other, STBox): @@ -974,7 +967,7 @@ def nearest_approach_distance(self, other: Union[pg.Geometry, shpb.BaseGeometry, else: raise TypeError(f'Operation not supported with type {other.__class__}') - def nearest_approach_instant(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint]) -> TI: + def nearest_approach_instant(self, other: Union[shpb.BaseGeometry, TPoint]) -> TI: """ Returns the nearest approach instant between the temporal point and `other`. @@ -987,7 +980,7 @@ def nearest_approach_instant(self, other: Union[pg.Geometry, shpb.BaseGeometry, MEOS Functions: nai_tpoint_geo, nai_tpoint_tpoint """ - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = nai_tpoint_geo(self._inner, gs) elif isinstance(other, TPoint): @@ -996,7 +989,7 @@ def nearest_approach_instant(self, other: Union[pg.Geometry, shpb.BaseGeometry, raise TypeError(f'Operation not supported with type {other.__class__}') return Temporal._factory(result) - def shortest_line(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint]) -> shpb.BaseGeometry: + def shortest_line(self, other: Union[shpb.BaseGeometry, TPoint]) -> shpb.BaseGeometry: """ Returns the shortest line between the temporal point and `other`. @@ -1010,7 +1003,7 @@ def shortest_line(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint]) -> MEOS Functions: shortestline_tpoint_geo, shortestline_tpoint_tpoint """ - if isinstance(other, pg.Geometry) or isinstance(other, shpb.BaseGeometry): + if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) result = shortestline_tpoint_geo(self._inner, gs) elif isinstance(other, TPoint): @@ -1021,7 +1014,7 @@ def shortest_line(self, other: Union[pg.Geometry, shpb.BaseGeometry, TPoint]) -> # ------------------------- Tiling Operations ----------------------------- def tile(self, size: float, duration: Optional[Union[timedelta, str]] = None, - origin: Optional[Union[shpb.BaseGeometry, pg.Geometry]] = None, + origin: Optional[shpb.BaseGeometry] = None, start: Union[datetime, str, None] = None) -> List[List[List[List[TG]]]]: """ Split the temporal point into segments following the tiling of the @@ -1052,7 +1045,7 @@ def tile(self, size: float, duration: Optional[Union[timedelta, str]] = None, for z_dim in y_dim] for y_dim in x_dim] for x_dim in tiles] def tile_flat(self, size: float, duration: Optional[Union[timedelta, str]] = None, - origin: Optional[Union[shpb.BaseGeometry, pg.Geometry]] = None, + origin: Optional[shpb.BaseGeometry] = None, start: Union[datetime, str, None] = None) -> List[TG]: """ Split the temporal point into segments following the tiling of the @@ -1083,8 +1076,8 @@ def tile_flat(self, size: float, duration: Optional[Union[timedelta, str]] = Non # ------------------------- Split Operations ------------------------------ def space_split(self, xsize: float, ysize: Optional[float] = None, - zsize: Optional[float] = None, origin: Optional[Geometry] = None, - bitmatrix: Optional[bool] = False) -> List[Temporal]: + zsize: Optional[float] = None, origin: Optional[shpb.BaseGeometry] = None, + bitmatrix: Optional[bool] = False) -> List[Temporal]: """ Splits `self` into fragments with respect to space buckets @@ -1108,16 +1101,16 @@ def space_split(self, xsize: float, ysize: Optional[float] = None, if isinstance(self, TGeogPoint) \ else pgis_geometry_in('Point(0 0 0)', -1) fragments, values, count = tpoint_space_split(self._inner, - xsize, ysz, zsz, gs, bitmatrix) + xsize, ysz, zsz, gs, bitmatrix) from ..factory import _TemporalFactory return [_TemporalFactory.create_temporal(fragments[i]) for i in \ - range(count)] + range(count)] def space_time_split(self, xsize: float, duration: Union[str, timedelta], - ysize: Optional[float] = None, zsize: Optional[float] = None, - origin: Optional[Geometry] = None, - time_start: Optional[Union[str, datetime]] = None, - bitmatrix: Optional[bool] = False) -> List[Temporal]: + ysize: Optional[float] = None, zsize: Optional[float] = None, + origin: Optional[shpb.BaseGeometry] = None, + time_start: Optional[Union[str, datetime]] = None, + bitmatrix: Optional[bool] = False) -> List[Temporal]: """ Splits `self` into fragments with respect to space and period buckets. @@ -1153,7 +1146,7 @@ def space_time_split(self, xsize: float, duration: Union[str, timedelta], if isinstance(time_start, datetime) \ else pg_timestamptz_in(time_start, -1) fragments, points, times, count = tpoint_space_time_split(self._inner, - xsize, ysz, zsz, dt, gs, st, bitmatrix) + xsize, ysz, zsz, dt, gs, st, bitmatrix) return [Temporal._factory(fragments[i]) for i in range(count)] @@ -1282,7 +1275,7 @@ class TGeomPoint(TPoint['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', # ------------------------- Output ---------------------------------------- @staticmethod - def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], + def from_base_temporal(value: shpb.BaseGeometry, base: Temporal) -> TGeomPoint: """ Creates a temporal geometric point from a base geometry and the time @@ -1304,24 +1297,24 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + def from_base_time(value: shpb.BaseGeometry, base: datetime) -> TGeomPointInst: ... @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + def from_base_time(value: shpb.BaseGeometry, base: Union[TimestampSet, Period]) -> TGeomPointSeq: ... @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + def from_base_time(value: shpb.BaseGeometry, base: PeriodSet) -> TGeomPointSeqSet: ... @staticmethod - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Time, + def from_base_time(value: shpb.BaseGeometry, base: Time, interpolation: TInterpolation = None) -> TGeomPoint: """ Creates a temporal geometric point from a base geometry and a time value. @@ -1398,7 +1391,7 @@ def to_dataframe(self) -> GeoDataFrame: return GeoDataFrame(data, crs=self.srid()).set_index(keys=['time']) # ------------------------- Ever and Always Comparisons ------------------- - def always_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def always_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is always equal to `value`. @@ -1414,7 +1407,7 @@ def always_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: gs = geometry_to_gserialized(value) return tpoint_always_eq(self._inner, gs) - def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def always_not_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is always different to `value`. @@ -1430,7 +1423,7 @@ def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool gs = geometry_to_gserialized(value) return not tpoint_ever_eq(self._inner, gs) - def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def ever_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is ever equal to `value`. @@ -1446,7 +1439,7 @@ def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: gs = geometry_to_gserialized(value) return tpoint_ever_eq(self._inner, gs) - def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def ever_not_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is ever different to `value`. @@ -1462,7 +1455,7 @@ def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: gs = geometry_to_gserialized(value) return not tpoint_always_eq(self._inner, gs) - def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def never_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is never equal to `value`. @@ -1478,7 +1471,7 @@ def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: gs = geometry_to_gserialized(value) return not tpoint_ever_eq(self._inner, gs) - def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def never_not_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is never different to `value`. @@ -1495,7 +1488,7 @@ def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: return tpoint_always_eq(self._inner, gs) # ------------------------- Temporal Comparisons -------------------------- - def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: + def temporal_equal(self, other: Union[shp.Point, Temporal]) -> TBool: """ Returns the temporal equality relation between `self` and `other`. @@ -1508,15 +1501,14 @@ def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: MEOS Functions: teq_tpoint_point, teq_temporal_temporal """ - if isinstance(other, pg.Point) or isinstance(other, shp.Point): + if isinstance(other, shp.Point): gs = geometry_to_gserialized(other) result = teq_tpoint_point(self._inner, gs) else: return super().temporal_equal(other) return Temporal._factory(result) - def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) \ - -> Temporal: + def temporal_not_equal(self, other: Union[shp.Point, Temporal]) -> Temporal: """ Returns the temporal inequality relation between `self` and `other`. @@ -1529,7 +1521,7 @@ def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) \ MEOS Functions: tne_tpoint_point, tne_temporal_temporal """ - if isinstance(other, pg.Point) or isinstance(other, shp.Point): + if isinstance(other, shp.Point): gs = geometry_to_gserialized(other) result = tne_tpoint_point(self._inner, gs) else: @@ -1577,7 +1569,7 @@ class TGeogPoint(TPoint['TGeogPoint', 'TGeogPointInst', 'TGeogPointSeq', # ------------------------- Output ---------------------------------------- @staticmethod - def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], + def from_base_temporal(value: shpb.BaseGeometry, base: Temporal) -> TGeogPoint: """ Creates a temporal geographic point from a base geometry and the time @@ -1599,24 +1591,24 @@ def from_base_temporal(value: Union[pg.Geometry, shpb.BaseGeometry], @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + def from_base_time(value: shpb.BaseGeometry, base: datetime) -> TGeogPointInst: ... @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + def from_base_time(value: shpb.BaseGeometry, base: Union[TimestampSet, Period]) -> TGeogPointSeq: ... @staticmethod @overload - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], + def from_base_time(value: shpb.BaseGeometry, base: PeriodSet) -> TGeogPointSeqSet: ... @staticmethod - def from_base_time(value: Union[pg.Geometry, shpb.BaseGeometry], base: Time, + def from_base_time(value: shpb.BaseGeometry, base: Time, interpolation: TInterpolation = None) -> TGeogPoint: """ Creates a temporal geographic point from a base geometry and a time object. @@ -1663,7 +1655,7 @@ def to_geometric(self) -> TGeomPoint: return Temporal._factory(result) # ------------------------- Ever and Always Comparisons ------------------- - def always_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def always_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is always equal to `value`. @@ -1679,7 +1671,7 @@ def always_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: gs = geography_to_gserialized(value) return tpoint_always_eq(self._inner, gs) - def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def always_not_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is always different to `value`. @@ -1695,7 +1687,7 @@ def always_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool gs = geography_to_gserialized(value) return not tpoint_ever_eq(self._inner, gs) - def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def ever_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is ever equal to `value`. @@ -1711,7 +1703,7 @@ def ever_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: gs = geography_to_gserialized(value) return tpoint_ever_eq(self._inner, gs) - def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def ever_not_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is ever different to `value`. @@ -1727,7 +1719,7 @@ def ever_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: gs = geography_to_gserialized(value) return not tpoint_always_eq(self._inner, gs) - def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def never_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is never equal to `value`. @@ -1743,7 +1735,7 @@ def never_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: gs = geography_to_gserialized(value) return not tpoint_ever_eq(self._inner, gs) - def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: + def never_not_equal(self, value: shpb.BaseGeometry) -> bool: """ Returns whether `self` is never different to `value`. @@ -1760,7 +1752,7 @@ def never_not_equal(self, value: Union[pg.Geometry, shpb.BaseGeometry]) -> bool: return tpoint_always_eq(self._inner, gs) # ------------------------- Temporal Comparisons -------------------------- - def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: + def temporal_equal(self, other: Union[shp.Point, Temporal]) -> TBool: """ Returns the temporal equality relation between `self` and `other`. @@ -1773,14 +1765,14 @@ def temporal_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: MEOS Functions: teq_tpoint_point, teq_temporal_temporal """ - if isinstance(other, pg.Point) or isinstance(other, shp.Point): + if isinstance(other, shp.Point): gs = geography_to_gserialized(other) result = teq_tpoint_point(self._inner, gs) else: return super().temporal_equal(other) return Temporal._factory(result) - def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBool: + def temporal_not_equal(self, other: Union[shp.Point, Temporal]) -> TBool: """ Returns the temporal inequality relation between `self` and `other`. @@ -1793,7 +1785,7 @@ def temporal_not_equal(self, other: Union[pg.Point, shp.Point, Temporal]) -> TBo MEOS Functions: tne_tpoint_point, tne_temporal_temporal """ - if isinstance(other, pg.Point) or isinstance(other, shp.Point): + if isinstance(other, shp.Point): gs = geography_to_gserialized(other) result = tne_tpoint_point(self._inner, gs) else: @@ -1828,8 +1820,7 @@ def read_from_cursor(value, _=None): raise Exception("ERROR: Could not parse temporal point value") -class TGeomPointInst(TPointInst['TGeomPoint', 'TGeomPointInst', -'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): +class TGeomPointInst(TPointInst['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', 'TGeomPointSeqSet'], TGeomPoint): """ Class for representing temporal geometric points at a single instant. """ @@ -1837,7 +1828,7 @@ class TGeomPointInst(TPointInst['TGeomPoint', 'TGeomPointInst', _cast_function = lambda x: None def __init__(self, string: Optional[str] = None, *, - point: Optional[Union[str, pg.Point, shp.Point]] = None, + point: Optional[Union[str, shp.Point]] = None, timestamp: Optional[Union[str, datetime]] = None, srid: Optional[int] = 0, _inner=None) -> None: super().__init__(string=string, value=point, timestamp=timestamp, @@ -1855,8 +1846,7 @@ class TGeogPointInst(TPointInst['TGeogPoint', 'TGeogPointInst', _cast_function = lambda x: None def __init__(self, string: Optional[str] = None, *, - point: Optional[Union[str, pg.Point, shp.Point, - Tuple[float, float]]] = None, + point: Optional[Union[str, shp.Point, Tuple[float, float]]] = None, timestamp: Optional[Union[str, datetime]] = None, srid: Optional[int] = 4326, _inner=None) -> None: super().__init__(string=string, value=point, timestamp=timestamp, diff --git a/pymeos/pyproject.toml b/pymeos/pyproject.toml index 62034919..122eb4ba 100644 --- a/pymeos/pyproject.toml +++ b/pymeos/pyproject.toml @@ -36,7 +36,6 @@ requires-python = '>=3.7' dependencies = [ 'pymeos-cffi >=1.1.0a4', 'python-dateutil', - 'postgis', 'shapely', 'geopandas' ] From c008f00729cd8f2e944ea7b9854e0b24ab5b0644 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 2 Oct 2023 15:37:49 +0200 Subject: [PATCH 076/101] Make GeoPandas an optional dependency (since it has many dependencies and is only used in an output function of TPoints) --- pymeos/pymeos/main/tpoint.py | 18 +++++++++++++++--- pymeos/pyproject.toml | 5 ++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index e7a7ab05..8cd36d2a 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -6,7 +6,6 @@ import shapely.geometry as shp import shapely.geometry.base as shpb -from geopandas import GeoDataFrame from pymeos_cffi import * from .tbool import TBool @@ -16,6 +15,17 @@ if TYPE_CHECKING: from ..boxes import STBox, Box + from geopandas import GeoDataFrame + + +def import_geopandas(): + try: + import geopandas as gpd + return gpd + except ImportError: + print('Geopandas not found. Please install geopandas to use this function.') + raise + TG = TypeVar('TG', bound='TPoint') TI = TypeVar('TI', bound='TPointInst') @@ -1225,6 +1235,7 @@ def to_dataframe(self, precision: int = 15) -> GeoDataFrame: A new :class:`GeoDataFrame` representing the temporal point sequence set. """ + gpd = import_geopandas() sequences = self.sequences() data = { 'sequence': [i + 1 for i, seq in enumerate(sequences) for _ in @@ -1233,7 +1244,7 @@ def to_dataframe(self, precision: int = 15) -> GeoDataFrame: 'geometry': [v for seq in sequences for v in seq.values(precision=precision)] } - return GeoDataFrame(data, crs=self.srid()).set_index(keys=['sequence', 'time']) + return gpd.GeoDataFrame(data, crs=self.srid()).set_index(keys=['sequence', 'time']) def x(self) -> TFloatSeqSet: return super().x() @@ -1384,11 +1395,12 @@ def to_dataframe(self) -> GeoDataFrame: Returns: A new :class:`GeoDataFrame` representing the trajectory. """ + gpd = import_geopandas() data = { 'time': self.timestamps(), 'geometry': [i.value() for i in self.instants()] } - return GeoDataFrame(data, crs=self.srid()).set_index(keys=['time']) + return gpd.GeoDataFrame(data, crs=self.srid()).set_index(keys=['time']) # ------------------------- Ever and Always Comparisons ------------------- def always_equal(self, value: shpb.BaseGeometry) -> bool: diff --git a/pymeos/pyproject.toml b/pymeos/pyproject.toml index 122eb4ba..0f097add 100644 --- a/pymeos/pyproject.toml +++ b/pymeos/pyproject.toml @@ -37,7 +37,6 @@ dependencies = [ 'pymeos-cffi >=1.1.0a4', 'python-dateutil', 'shapely', - 'geopandas' ] [project.optional-dependencies] @@ -57,6 +56,10 @@ plot = [ 'matplotlib' ] +pandas = [ + 'geopandas' +] + [project.urls] 'Homepage' = 'https://github.com/MobilityDB/PyMEOS/pymeos' 'Bug Tracker' = 'https://github.com/MobilityDB/PyMEOS/issues' From cdce8bb4829720dbf5617c0ec5bcfca9b26c77dc Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 3 Oct 2023 18:35:39 +0200 Subject: [PATCH 077/101] Move to_shapely_geometry from TGeomPoint to TPoint --- pymeos/pymeos/main/tpoint.py | 44 ++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 8cd36d2a..52456cc3 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -109,6 +109,23 @@ def as_geojson(self, option: int = 1, precision: int = 15, srs: Optional[str] = """ return gserialized_as_geojson(tpoint_trajectory(self._inner), option, precision, srs) + def to_shapely_geometry(self, precision: int = 15) -> shpb.BaseGeometry: + """ + Returns the trajectory of the temporal point as a Shapely geometry. + + Args: + precision: The precision of the returned geometry. + + Returns: + A new :class:`~shapely.geometry.base.BaseGeometry` representing the + trajectory. + + MEOS Functions: + gserialized_to_shapely_geometry + """ + return gserialized_to_shapely_geometry(tpoint_trajectory(self._inner), + precision) + # ------------------------- Accessors ------------------------------------- def bounding_box(self) -> STBox: """ @@ -1371,23 +1388,6 @@ def to_geographic(self) -> TGeogPoint: result = tgeompoint_to_tgeogpoint(self._inner) return Temporal._factory(result) - def to_shapely_geometry(self, precision: int = 15) -> shpb.BaseGeometry: - """ - Returns the trajectory of the temporal point as a Shapely geometry. - - Args: - precision: The precision of the returned geometry. - - Returns: - A new :class:`~shapely.geometry.base.BaseGeometry` representing the - trajectory. - - MEOS Functions: - gserialized_to_shapely_geometry - """ - return gserialized_to_shapely_geometry(tpoint_trajectory(self._inner), - precision) - def to_dataframe(self) -> GeoDataFrame: """ Returns the trajectory of the temporal point as a GeoPandas DataFrame. @@ -1840,13 +1840,17 @@ class TGeomPointInst(TPointInst['TGeomPoint', 'TGeomPointInst', 'TGeomPointSeq', _cast_function = lambda x: None def __init__(self, string: Optional[str] = None, *, - point: Optional[Union[str, shp.Point]] = None, + point: Optional[Union[str, shp.Point, Tuple[float, float], Tuple[float, float, float]]] = None, timestamp: Optional[Union[str, datetime]] = None, srid: Optional[int] = 0, _inner=None) -> None: super().__init__(string=string, value=point, timestamp=timestamp, _inner=_inner) if self._inner is None: - self._inner = tgeompoint_in(f"SRID={srid};{point}@{timestamp}") + if isinstance(point, tuple): + p = f'POINT({" ".join(str(x) for x in point)})' + else: + p = f'{point}' + self._inner = tgeompoint_in(f"SRID={srid};{p}@{timestamp}") class TGeogPointInst(TPointInst['TGeogPoint', 'TGeogPointInst', @@ -1858,7 +1862,7 @@ class TGeogPointInst(TPointInst['TGeogPoint', 'TGeogPointInst', _cast_function = lambda x: None def __init__(self, string: Optional[str] = None, *, - point: Optional[Union[str, shp.Point, Tuple[float, float]]] = None, + point: Optional[Union[str, shp.Point, Tuple[float, float], Tuple[float, float, float]]] = None, timestamp: Optional[Union[str, datetime]] = None, srid: Optional[int] = 4326, _inner=None) -> None: super().__init__(string=string, value=point, timestamp=timestamp, From 91be2af10145741f72cfb74924d3d2da11566fbf Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 3 Oct 2023 18:35:48 +0200 Subject: [PATCH 078/101] Typo --- pymeos/pymeos/temporal/temporal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymeos/pymeos/temporal/temporal.py b/pymeos/pymeos/temporal/temporal.py index 64a8b9d2..26ca9eb3 100644 --- a/pymeos/pymeos/temporal/temporal.py +++ b/pymeos/pymeos/temporal/temporal.py @@ -742,7 +742,7 @@ def delete(self, other: Time, connect: bool = True) -> TG: Args: other: :class:`Time` object to remove from `self` - connect: wether to connect the potential gaps generated by the + connect: whether to connect the potential gaps generated by the deletions. MEOS Functions: From cf3e36946c3168e78e645720a3a0aca477670e6c Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 3 Oct 2023 18:36:04 +0200 Subject: [PATCH 079/101] Export MEOS exceptions in PyMEOS package --- pymeos/pymeos/__init__.py | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pymeos/pymeos/__init__.py b/pymeos/pymeos/__init__.py index 39827a3a..d5593ee3 100644 --- a/pymeos/pymeos/__init__.py +++ b/pymeos/pymeos/__init__.py @@ -4,6 +4,29 @@ from .meos_init import * from .temporal import * from .collections import * +from pymeos_cffi import \ + MeosException, \ + MeosInternalError, \ + MeosArgumentError, \ + MeosIoError, \ + MeosInternalTypeError, \ + MeosValueOutOfRangeError, \ + MeosDivisionByZeroError, \ + MeosMemoryAllocError, \ + MeosAggregationError, \ + MeosDirectoryError, \ + MeosFileError, \ + MeosInvalidArgError, \ + MeosInvalidArgTypeError, \ + MeosInvalidArgValueError, \ + MeosMfJsonInputError, \ + MeosMfJsonOutputError, \ + MeosTextInputError, \ + MeosTextOutputError, \ + MeosWkbInputError, \ + MeosWkbOutputError, \ + MeosGeoJsonInputError, \ + MeosGeoJsonOutputError __version__ = '1.1.3a1' __all__ = [ @@ -39,4 +62,27 @@ 'TemporalTextMaxAggregator', 'TemporalTextMinAggregator', 'TemporalPointExtentAggregator', 'TimeInstantaneousUnionAggregator', 'TimeContinuousUnionAggregator', + # exceptions + 'MeosException', + 'MeosInternalError', + 'MeosArgumentError', + 'MeosIoError', + 'MeosInternalTypeError', + 'MeosValueOutOfRangeError', + 'MeosDivisionByZeroError', + 'MeosMemoryAllocError', + 'MeosAggregationError', + 'MeosDirectoryError', + 'MeosFileError', + 'MeosInvalidArgError', + 'MeosInvalidArgTypeError', + 'MeosInvalidArgValueError', + 'MeosMfJsonInputError', + 'MeosMfJsonOutputError', + 'MeosTextInputError', + 'MeosTextOutputError', + 'MeosWkbInputError', + 'MeosWkbOutputError', + 'MeosGeoJsonInputError', + 'MeosGeoJsonOutputError', ] From d2f17033a673b645bba40d2ecdaab8aa63a49a10 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 3 Oct 2023 19:55:01 +0200 Subject: [PATCH 080/101] Add strid to converted shapely geometries --- .../builder/templates/functions.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/pymeos_cffi/pymeos_cffi/builder/templates/functions.py b/pymeos_cffi/pymeos_cffi/builder/templates/functions.py index 0faffd00..93947266 100644 --- a/pymeos_cffi/pymeos_cffi/builder/templates/functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/templates/functions.py @@ -5,7 +5,7 @@ from .errors import raise_meos_exception import shapely.geometry as spg from dateutil.parser import parse -from shapely import wkt, get_srid +from shapely import wkt, get_srid, set_srid from shapely.geometry.base import BaseGeometry _ffi = _meos_cffi.ffi @@ -84,12 +84,22 @@ def geography_to_gserialized(geom: BaseGeometry) -> 'GSERIALIZED *': return gs -def gserialized_to_shapely_point(geom: 'const GSERIALIZED *', precision: int = 6) -> spg.Point: - return wkt.loads(gserialized_as_text(geom, precision)) +def gserialized_to_shapely_point(geom: 'const GSERIALIZED *', precision: int = 15) -> spg.Point: + text = gserialized_as_text(geom, precision) + geometry = wkt.loads(text) + srid = lwgeom_get_srid(geom) + if srid > 0: + geometry = set_srid(geometry, srid) + return geometry -def gserialized_to_shapely_geometry(geom: 'const GSERIALIZED *', precision: int = 6) -> BaseGeometry: - return wkt.loads(gserialized_as_text(geom, precision)) +def gserialized_to_shapely_geometry(geom: 'const GSERIALIZED *', precision: int = 15) -> BaseGeometry: + text = gserialized_as_text(geom, precision) + geometry = wkt.loads(text) + srid = lwgeom_get_srid(geom) + if srid > 0: + geometry = set_srid(geometry, srid) + return geometry def as_tinstant(temporal: 'Temporal *') -> 'TInstant *': From f49215c10a659e48093a381c084070ba42863e54 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 3 Oct 2023 19:55:26 +0200 Subject: [PATCH 081/101] Add better exception representation --- pymeos_cffi/pymeos_cffi/errors.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pymeos_cffi/pymeos_cffi/errors.py b/pymeos_cffi/pymeos_cffi/errors.py index fedacca0..975229b2 100644 --- a/pymeos_cffi/pymeos_cffi/errors.py +++ b/pymeos_cffi/pymeos_cffi/errors.py @@ -32,8 +32,12 @@ class MeosException(Exception): def __init__(self, code: int, message: str): super().__init__(message) + self.message = message self.code = code + def __str__(self): + return f'{self.__class__.__name__} ({self.code}): {self.message}' + class MeosInternalError(MeosException): """Superclass for internal errors.""" From dab2fd3d5744eed70a23c15a03799fb0116a41eb Mon Sep 17 00:00:00 2001 From: Diviloper Date: Tue, 3 Oct 2023 23:32:53 +0200 Subject: [PATCH 082/101] Refactor hardcoded paths in building scripts --- .../pymeos_cffi/builder/build_header.py | 6 +++--- .../builder/build_pymeos_functions.py | 18 +++++++++++------ pymeos_cffi/pymeos_cffi/functions.py | 20 ++++++++++++++----- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/pymeos_cffi/pymeos_cffi/builder/build_header.py b/pymeos_cffi/pymeos_cffi/builder/build_header.py index 38e3e1a0..2b0a30c1 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_header.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_header.py @@ -26,7 +26,7 @@ def remove_if_not_defined(m): return content -def main(header_path, so_path=None): +def main(header_path, so_path=None, destination_path='pymeos_cffi/builder/meos.h'): with open(header_path, 'r') as f: content = f.read() # Remove comments @@ -45,12 +45,12 @@ def main(header_path, so_path=None): # Add error handler content += '\n\nextern "Python" void py_error_handler(int, int, char*);' - with open('pymeos_cffi/builder/meos.h', 'w') as f: + with open(destination_path, 'w') as f: f.write(content) if __name__ == '__main__': if len(sys.argv) > 1: - main(sys.argv[1], sys.argv[2]) + main(*sys.argv[1:]) else: main('/usr/local/include/meos.h', '/usr/local/lib/libmeos.so') diff --git a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py index 19744cd7..104227b7 100644 --- a/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/build_pymeos_functions.py @@ -1,4 +1,6 @@ +import os.path import re +import sys from re import RegexFlag from typing import List, Optional @@ -209,8 +211,8 @@ def check_modifiers(functions: List[str]) -> None: print(f'Nullable Parameter defined for non-existent function {func} ({param})') -def main(): - with open('pymeos_cffi/builder/meos.h') as f: +def main(header_path='pymeos_cffi/builder/meos.h'): + with open(header_path) as f: content = f.read() # Regex lines: # 1st line: Match beginning of function with optional "extern", "static" and "inline" @@ -225,10 +227,14 @@ def main(): r'\((?P[\w\s,\*]*)\);' matches = re.finditer(f_regex, ''.join(content.splitlines()), flags=RegexFlag.MULTILINE) - with open('pymeos_cffi/builder/templates/functions.py') as f: + template_path = os.path.join(os.path.dirname(__file__), 'templates/functions.py') + with open(template_path) as f: base = f.read() - with open('pymeos_cffi/functions.py', 'w+') as file: + functions_path = os.path.join(os.path.dirname(__file__), '../functions.py') + init_path = os.path.join(os.path.dirname(__file__), '../__init__.py') + + with open(functions_path, 'w+') as file: file.write(base) for match in matches: named = match.groupdict() @@ -244,7 +250,7 @@ def main(): file.write('\n\n\n') functions = [] - with open('pymeos_cffi/functions.py', 'r') as funcs, open('pymeos_cffi/__init__.py', 'w+') as init: + with open(functions_path, 'r') as funcs, open(init_path, 'w+') as init: content = funcs.read() matches = list(re.finditer(r'def (\w+)\(', content)) init.write('from .functions import *\n\n') @@ -481,4 +487,4 @@ def build_function_string(function_name: str, return_type: ReturnType, parameter if __name__ == '__main__': - main() + main(*sys.argv[1:]) diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index de6818ca..0da167a3 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -5,7 +5,7 @@ from .errors import raise_meos_exception import shapely.geometry as spg from dateutil.parser import parse -from shapely import wkt, get_srid +from shapely import wkt, get_srid, set_srid from shapely.geometry.base import BaseGeometry _ffi = _meos_cffi.ffi @@ -84,12 +84,22 @@ def geography_to_gserialized(geom: BaseGeometry) -> 'GSERIALIZED *': return gs -def gserialized_to_shapely_point(geom: 'const GSERIALIZED *', precision: int = 6) -> spg.Point: - return wkt.loads(gserialized_as_text(geom, precision)) +def gserialized_to_shapely_point(geom: 'const GSERIALIZED *', precision: int = 15) -> spg.Point: + text = gserialized_as_text(geom, precision) + geometry = wkt.loads(text) + srid = lwgeom_get_srid(geom) + if srid > 0: + geometry = set_srid(geometry, srid) + return geometry -def gserialized_to_shapely_geometry(geom: 'const GSERIALIZED *', precision: int = 6) -> BaseGeometry: - return wkt.loads(gserialized_as_text(geom, precision)) +def gserialized_to_shapely_geometry(geom: 'const GSERIALIZED *', precision: int = 15) -> BaseGeometry: + text = gserialized_as_text(geom, precision) + geometry = wkt.loads(text) + srid = lwgeom_get_srid(geom) + if srid > 0: + geometry = set_srid(geometry, srid) + return geometry def as_tinstant(temporal: 'Temporal *') -> 'TInstant *': From 2b631666d38e47a5b4e61df12a49fbdb4173b11d Mon Sep 17 00:00:00 2001 From: Diviloper Date: Wed, 4 Oct 2023 17:36:45 +0200 Subject: [PATCH 083/101] Use MEOS error enum values instead of copied one. --- pymeos_cffi/pymeos_cffi/builder/meos.h | 41 +++++++++++ pymeos_cffi/pymeos_cffi/errors.py | 94 +++++++------------------- pymeos_cffi/pyproject.toml | 2 +- 3 files changed, 68 insertions(+), 69 deletions(-) diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 3ec71d54..084b6c2f 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -729,6 +729,47 @@ typedef struct SkipListElem *elems; } SkipList; +/***************************************************************************** + * Error codes + *****************************************************************************/ + +typedef enum +{ + MEOS_SUCCESS = 0, + + MEOS_ERR_INTERNAL_ERROR = 1, + MEOS_ERR_INTERNAL_TYPE_ERROR = 2, + MEOS_ERR_VALUE_OUT_OF_RANGE = 3, + MEOS_ERR_DIVISION_BY_ZERO = 4, + MEOS_ERR_MEMORY_ALLOC_ERROR = 5, + MEOS_ERR_AGGREGATION_ERROR = 6, + MEOS_ERR_DIRECTORY_ERROR = 7, + MEOS_ERR_FILE_ERROR = 8, + + MEOS_ERR_INVALID_ARG = 10, + MEOS_ERR_INVALID_ARG_TYPE = 11, + MEOS_ERR_INVALID_ARG_VALUE = 12, + + MEOS_ERR_MFJSON_INPUT = 20, + MEOS_ERR_MFJSON_OUTPUT = 21, + MEOS_ERR_TEXT_INPUT = 22, + MEOS_ERR_TEXT_OUTPUT = 23, + MEOS_ERR_WKB_INPUT = 24, + MEOS_ERR_WKB_OUTPUT = 25, + MEOS_ERR_GEOJSON_INPUT = 26, + MEOS_ERR_GEOJSON_OUTPUT = 27, + +} errorCode; + +extern void meos_error(int errlevel, int errcode, char *format, ...); + + + + + + + + /***************************************************************************** * Initialization of the MEOS library *****************************************************************************/ diff --git a/pymeos_cffi/pymeos_cffi/errors.py b/pymeos_cffi/pymeos_cffi/errors.py index 975229b2..b3896b6b 100644 --- a/pymeos_cffi/pymeos_cffi/errors.py +++ b/pymeos_cffi/pymeos_cffi/errors.py @@ -1,30 +1,4 @@ -from enum import IntEnum - - -class MEOSCode(IntEnum): - MEOS_SUCCESS = 0, # Successful operation - - MEOS_ERR_INTERNAL_ERROR = 1, # Unspecified internal error - MEOS_ERR_INTERNAL_TYPE_ERROR = 2, # Internal type error - MEOS_ERR_VALUE_OUT_OF_RANGE = 3, # Internal out of range error - MEOS_ERR_DIVISION_BY_ZERO = 4, # Internal division by zero error - MEOS_ERR_MEMORY_ALLOC_ERROR = 5, # Internal malloc error - MEOS_ERR_AGGREGATION_ERROR = 6, # Internal aggregation error - MEOS_ERR_DIRECTORY_ERROR = 7, # Internal directory error - MEOS_ERR_FILE_ERROR = 8, # Internal file error - - MEOS_ERR_INVALID_ARG = 10, # Invalid argument - MEOS_ERR_INVALID_ARG_TYPE = 11, # Invalid argument type - MEOS_ERR_INVALID_ARG_VALUE = 12, # Invalid argument value - - MEOS_ERR_MFJSON_INPUT = 20, # MFJSON input error - MEOS_ERR_MFJSON_OUTPUT = 21, # MFJSON output error - MEOS_ERR_TEXT_INPUT = 22, # Text input error - MEOS_ERR_TEXT_OUTPUT = 23, # Text output error - MEOS_ERR_WKB_INPUT = 24, # WKB input error - MEOS_ERR_WKB_OUTPUT = 25, # WKB output error - MEOS_ERR_GEOJSON_INPUT = 26, # GEOJSON input error - MEOS_ERR_GEOJSON_OUTPUT = 27, # GEOJSON output error +from _meos_cffi import lib as _lib class MeosException(Exception): @@ -144,45 +118,29 @@ class MeosGeoJsonOutputError(MeosIoError): pass +_exception_map = { + _lib.MEOS_ERR_INTERNAL_ERROR: MeosInternalError, + _lib.MEOS_ERR_INTERNAL_TYPE_ERROR: MeosInternalTypeError, + _lib.MEOS_ERR_VALUE_OUT_OF_RANGE: MeosValueOutOfRangeError, + _lib.MEOS_ERR_DIVISION_BY_ZERO: MeosDivisionByZeroError, + _lib.MEOS_ERR_MEMORY_ALLOC_ERROR: MeosMemoryAllocError, + _lib.MEOS_ERR_AGGREGATION_ERROR: MeosAggregationError, + _lib.MEOS_ERR_DIRECTORY_ERROR: MeosDirectoryError, + _lib.MEOS_ERR_FILE_ERROR: MeosFileError, + _lib.MEOS_ERR_INVALID_ARG: MeosInvalidArgError, + _lib.MEOS_ERR_INVALID_ARG_TYPE: MeosInvalidArgTypeError, + _lib.MEOS_ERR_INVALID_ARG_VALUE: MeosInvalidArgValueError, + _lib.MEOS_ERR_MFJSON_INPUT: MeosMfJsonInputError, + _lib.MEOS_ERR_MFJSON_OUTPUT: MeosMfJsonOutputError, + _lib.MEOS_ERR_TEXT_INPUT: MeosTextInputError, + _lib.MEOS_ERR_TEXT_OUTPUT: MeosTextOutputError, + _lib.MEOS_ERR_WKB_INPUT: MeosWkbInputError, + _lib.MEOS_ERR_WKB_OUTPUT: MeosWkbOutputError, + _lib.MEOS_ERR_GEOJSON_INPUT: MeosGeoJsonInputError, + _lib.MEOS_ERR_GEOJSON_OUTPUT: MeosGeoJsonOutputError, +} + + def raise_meos_exception(level: int, code: int, message: str): - if code == MEOSCode.MEOS_ERR_INTERNAL_ERROR: - raise MeosInternalError(code, message) - elif code == MEOSCode.MEOS_ERR_INTERNAL_TYPE_ERROR: - raise MeosInternalTypeError(code, message) - elif code == MEOSCode.MEOS_ERR_VALUE_OUT_OF_RANGE: - raise MeosValueOutOfRangeError(code, message) - elif code == MEOSCode.MEOS_ERR_DIVISION_BY_ZERO: - raise MeosDivisionByZeroError(code, message) - elif code == MEOSCode.MEOS_ERR_MEMORY_ALLOC_ERROR: - raise MeosMemoryAllocError(code, message) - elif code == MEOSCode.MEOS_ERR_AGGREGATION_ERROR: - raise MeosAggregationError(code, message) - elif code == MEOSCode.MEOS_ERR_DIRECTORY_ERROR: - raise MeosDirectoryError(code, message) - elif code == MEOSCode.MEOS_ERR_FILE_ERROR: - raise MeosFileError(code, message) - elif code == MEOSCode.MEOS_ERR_INVALID_ARG: - raise MeosInvalidArgError(code, message) - elif code == MEOSCode.MEOS_ERR_INVALID_ARG_TYPE: - raise MeosInvalidArgTypeError(code, message) - elif code == MEOSCode.MEOS_ERR_INVALID_ARG_VALUE: - raise MeosInvalidArgValueError(code, message) - elif code == MEOSCode.MEOS_ERR_MFJSON_INPUT: - raise MeosMfJsonInputError(code, message) - elif code == MEOSCode.MEOS_ERR_MFJSON_OUTPUT: - raise MeosMfJsonOutputError(code, message) - elif code == MEOSCode.MEOS_ERR_TEXT_INPUT: - raise MeosTextInputError(code, message) - elif code == MEOSCode.MEOS_ERR_TEXT_OUTPUT: - raise MeosTextOutputError(code, message) - elif code == MEOSCode.MEOS_ERR_WKB_INPUT: - raise MeosWkbInputError(code, message) - elif code == MEOSCode.MEOS_ERR_WKB_OUTPUT: - raise MeosWkbOutputError(code, message) - elif code == MEOSCode.MEOS_ERR_GEOJSON_INPUT: - raise MeosGeoJsonInputError(code, message) - elif code == MEOSCode.MEOS_ERR_GEOJSON_OUTPUT: - raise MeosGeoJsonOutputError(code, message) - else: - raise MeosException(code, f'{message}\nThe MEOS error code ({code}) has not been recognized. ' - f'Please, report this to the MEOS developers.') + exception_class = _exception_map.get(code, MeosException) + raise exception_class(code, message) diff --git a/pymeos_cffi/pyproject.toml b/pymeos_cffi/pyproject.toml index d51fb66f..378408cf 100644 --- a/pymeos_cffi/pyproject.toml +++ b/pymeos_cffi/pyproject.toml @@ -7,7 +7,7 @@ py-modules = [] [project] name = 'pymeos_cffi' -version = '1.1.0-alpha.5' +version = '1.1.0-alpha.6' authors = [ { name = 'Victor Divi', email = 'vdiviloper@gmail.com' } ] From 291d8598cb1588a4e3fd686301fbac3677490ba9 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 5 Oct 2023 18:49:11 +0200 Subject: [PATCH 084/101] Move documentation to root level to have joint docs with pymeos, pymeos_cffi and examples. --- .gitignore | 1 + {pymeos/docs => docs}/Makefile | 0 {pymeos/docs => docs}/conf.py | 7 ++++--- {doc => docs}/images/PyMEOS Layer Architecture.png | Bin {doc => docs}/images/PyMEOS Logo.png | Bin {doc => docs}/images/meos-logo.png | Bin {pymeos/docs => docs}/index.rst | 7 +++++-- {pymeos/docs => docs}/make.bat | 0 pymeos/docs/pymeos.rst => docs/pymeos/index.rst | 2 +- {pymeos/docs => docs/pymeos}/pymeos.aggregators.rst | 0 {pymeos/docs => docs/pymeos}/pymeos.boxes.rst | 0 .../pymeos/pymeos.collections.rst | 10 +++++----- {pymeos/docs => docs/pymeos}/pymeos.db.rst | 0 {pymeos/docs => docs/pymeos}/pymeos.main.rst | 0 {pymeos/docs => docs/pymeos}/pymeos.meos_init.rst | 0 {pymeos/docs => docs/pymeos}/pymeos.plotters.rst | 0 {pymeos/docs => docs/pymeos}/pymeos.temporal.rst | 0 docs/pymeos_cffi/index.rst | 6 ++++++ pymeos/pyproject.toml | 4 ++-- pymeos_cffi/README.md | 2 +- 20 files changed, 25 insertions(+), 14 deletions(-) rename {pymeos/docs => docs}/Makefile (100%) rename {pymeos/docs => docs}/conf.py (92%) rename {doc => docs}/images/PyMEOS Layer Architecture.png (100%) rename {doc => docs}/images/PyMEOS Logo.png (100%) rename {doc => docs}/images/meos-logo.png (100%) rename {pymeos/docs => docs}/index.rst (81%) rename {pymeos/docs => docs}/make.bat (100%) rename pymeos/docs/pymeos.rst => docs/pymeos/index.rst (89%) rename {pymeos/docs => docs/pymeos}/pymeos.aggregators.rst (100%) rename {pymeos/docs => docs/pymeos}/pymeos.boxes.rst (100%) rename pymeos/docs/pymeos.time.rst => docs/pymeos/pymeos.collections.rst (65%) rename {pymeos/docs => docs/pymeos}/pymeos.db.rst (100%) rename {pymeos/docs => docs/pymeos}/pymeos.main.rst (100%) rename {pymeos/docs => docs/pymeos}/pymeos.meos_init.rst (100%) rename {pymeos/docs => docs/pymeos}/pymeos.plotters.rst (100%) rename {pymeos/docs => docs/pymeos}/pymeos.temporal.rst (100%) create mode 100644 docs/pymeos_cffi/index.rst diff --git a/.gitignore b/.gitignore index e5451fee..1b94b1d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea Pipfile.lock *-checkpoint.ipynb +docs/_build \ No newline at end of file diff --git a/pymeos/docs/Makefile b/docs/Makefile similarity index 100% rename from pymeos/docs/Makefile rename to docs/Makefile diff --git a/pymeos/docs/conf.py b/docs/conf.py similarity index 92% rename from pymeos/docs/conf.py rename to docs/conf.py index 25b030ba..fe043103 100644 --- a/pymeos/docs/conf.py +++ b/docs/conf.py @@ -9,13 +9,15 @@ project = 'PyMEOS' copyright = '2023, Víctor Diví' author = 'Víctor Diví' -release = '1.1.2' +release = '1.1.3a5' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration import os import sys -sys.path.insert(0, os.path.abspath('..')) + +sys.path.insert(0, os.path.abspath('../pymeos_cffi')) +sys.path.insert(0, os.path.abspath('../pymeos')) extensions = [ 'sphinx.ext.autodoc', @@ -36,7 +38,6 @@ 'python': ('https://docs.python.org/3', None) } - # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output diff --git a/doc/images/PyMEOS Layer Architecture.png b/docs/images/PyMEOS Layer Architecture.png similarity index 100% rename from doc/images/PyMEOS Layer Architecture.png rename to docs/images/PyMEOS Layer Architecture.png diff --git a/doc/images/PyMEOS Logo.png b/docs/images/PyMEOS Logo.png similarity index 100% rename from doc/images/PyMEOS Logo.png rename to docs/images/PyMEOS Logo.png diff --git a/doc/images/meos-logo.png b/docs/images/meos-logo.png similarity index 100% rename from doc/images/meos-logo.png rename to docs/images/meos-logo.png diff --git a/pymeos/docs/index.rst b/docs/index.rst similarity index 81% rename from pymeos/docs/index.rst rename to docs/index.rst index e7afd067..cb9ea613 100644 --- a/pymeos/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,5 @@ .. PyMEOS documentation master file, created by - sphinx-quickstart on Fri Feb 10 11:27:17 2023. + sphinx-quickstart on Thu Oct 5 18:26:38 2023. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. @@ -10,7 +10,10 @@ Welcome to PyMEOS's documentation! :maxdepth: 2 :caption: Contents: - pymeos + pymeos/index + pymeos_cffi/index + + Indices and tables ================== diff --git a/pymeos/docs/make.bat b/docs/make.bat similarity index 100% rename from pymeos/docs/make.bat rename to docs/make.bat diff --git a/pymeos/docs/pymeos.rst b/docs/pymeos/index.rst similarity index 89% rename from pymeos/docs/pymeos.rst rename to docs/pymeos/index.rst index 6cc0ea25..0a99dda8 100644 --- a/pymeos/docs/pymeos.rst +++ b/docs/pymeos/index.rst @@ -5,7 +5,7 @@ PyMEOS package :maxdepth: 4 pymeos.meos_init - pymeos.time + pymeos.collections pymeos.temporal pymeos.main pymeos.boxes diff --git a/pymeos/docs/pymeos.aggregators.rst b/docs/pymeos/pymeos.aggregators.rst similarity index 100% rename from pymeos/docs/pymeos.aggregators.rst rename to docs/pymeos/pymeos.aggregators.rst diff --git a/pymeos/docs/pymeos.boxes.rst b/docs/pymeos/pymeos.boxes.rst similarity index 100% rename from pymeos/docs/pymeos.boxes.rst rename to docs/pymeos/pymeos.boxes.rst diff --git a/pymeos/docs/pymeos.time.rst b/docs/pymeos/pymeos.collections.rst similarity index 65% rename from pymeos/docs/pymeos.time.rst rename to docs/pymeos/pymeos.collections.rst index 3a7b8e58..a4372f51 100644 --- a/pymeos/docs/pymeos.time.rst +++ b/docs/pymeos/pymeos.collections.rst @@ -1,10 +1,10 @@ -Time +Collections =================== Time ----------------------- -.. automodule:: pymeos.time.time +.. automodule:: pymeos.collections.time.time :members: :undoc-members: :show-inheritance: @@ -12,7 +12,7 @@ Time Period ------------------------- -.. automodule:: pymeos.time.period +.. automodule:: pymeos.collections.time.period :members: :undoc-members: :show-inheritance: @@ -20,7 +20,7 @@ Period PeriodSet ---------------------------- -.. automodule:: pymeos.time.periodset +.. automodule:: pymeos.collections.time.periodset :members: :undoc-members: :show-inheritance: @@ -28,7 +28,7 @@ PeriodSet TimestampSet ------------------------------- -.. automodule:: pymeos.time.timestampset +.. automodule:: pymeos.collections.time.timestampset :members: :undoc-members: :show-inheritance: diff --git a/pymeos/docs/pymeos.db.rst b/docs/pymeos/pymeos.db.rst similarity index 100% rename from pymeos/docs/pymeos.db.rst rename to docs/pymeos/pymeos.db.rst diff --git a/pymeos/docs/pymeos.main.rst b/docs/pymeos/pymeos.main.rst similarity index 100% rename from pymeos/docs/pymeos.main.rst rename to docs/pymeos/pymeos.main.rst diff --git a/pymeos/docs/pymeos.meos_init.rst b/docs/pymeos/pymeos.meos_init.rst similarity index 100% rename from pymeos/docs/pymeos.meos_init.rst rename to docs/pymeos/pymeos.meos_init.rst diff --git a/pymeos/docs/pymeos.plotters.rst b/docs/pymeos/pymeos.plotters.rst similarity index 100% rename from pymeos/docs/pymeos.plotters.rst rename to docs/pymeos/pymeos.plotters.rst diff --git a/pymeos/docs/pymeos.temporal.rst b/docs/pymeos/pymeos.temporal.rst similarity index 100% rename from pymeos/docs/pymeos.temporal.rst rename to docs/pymeos/pymeos.temporal.rst diff --git a/docs/pymeos_cffi/index.rst b/docs/pymeos_cffi/index.rst new file mode 100644 index 00000000..5e79c6f8 --- /dev/null +++ b/docs/pymeos_cffi/index.rst @@ -0,0 +1,6 @@ +PyMEOS CFFI package +============== + +.. toctree:: + :maxdepth: 4 + diff --git a/pymeos/pyproject.toml b/pymeos/pyproject.toml index 0f097add..1c4d696e 100644 --- a/pymeos/pyproject.toml +++ b/pymeos/pyproject.toml @@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta' [project] name = 'pymeos' -version = '1.1.3-alpha.4' +version = '1.1.3-alpha.5' authors = [ { name = 'Victor Divi', email = 'vdiviloper@gmail.com' }, { name = 'Zhicheng Luo', email = 'zhicheng.luo@ulb.be' }, @@ -34,7 +34,7 @@ license = {file = 'LICENSE'} requires-python = '>=3.7' dependencies = [ - 'pymeos-cffi >=1.1.0a4', + 'pymeos-cffi ==1.1.0a5', 'python-dateutil', 'shapely', ] diff --git a/pymeos_cffi/README.md b/pymeos_cffi/README.md index 18759190..16f8dc7c 100644 --- a/pymeos_cffi/README.md +++ b/pymeos_cffi/README.md @@ -1,6 +1,6 @@ # PyMEOS CFFI -![MEOS Logo](../doc/images/meos-logo.png) +![MEOS Logo](../docs/images/meos-logo.png) [MEOS (Mobility Engine, Open Source)](https://www.libmeos.org/) is a C library which enables the manipulation of temporal and spatio-temporal data based on [MobilityDB](https://mobilitydb.com/)'s data types and functions. From d31dba6720b32f080e4efdc456c212029a3b7e9d Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 5 Oct 2023 19:18:28 +0200 Subject: [PATCH 085/101] Update rdt yaml --- .readthedocs.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index e69de29b..2ac56eee 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -0,0 +1,7 @@ +version: 2 +build: + os: ubuntu-20.04 + tools: + python: 3.10 +sphinx: + configuration: docs/conf.py \ No newline at end of file From 72c5cc0d80a12826ba79fd6f6ec1763306c56e01 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 5 Oct 2023 19:20:00 +0200 Subject: [PATCH 086/101] Update rdt yaml --- .readthedocs.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 2ac56eee..6b131a63 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,7 +1,7 @@ version: 2 build: - os: ubuntu-20.04 + os: "ubuntu-20.04" tools: - python: 3.10 + python: "3.10" sphinx: configuration: docs/conf.py \ No newline at end of file From 164c92115a386aac889bcf72a3c6d8ed4b1ccc9d Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 5 Oct 2023 19:28:04 +0200 Subject: [PATCH 087/101] Update rtd conf --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index fe043103..14803894 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,6 +23,7 @@ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'sphinx.ext.intersphinx' + 'sphinx_rtd_theme', ] templates_path = ['_templates'] From d5772ba2f8fa5d0f74390e6ece54567a200b6860 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 5 Oct 2023 19:29:41 +0200 Subject: [PATCH 088/101] Update rtd conf --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 14803894..83c28824 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', - 'sphinx.ext.intersphinx' + 'sphinx.ext.intersphinx', 'sphinx_rtd_theme', ] From 371bb6711d930ce8019fe1648595ac8ac644fb71 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 5 Oct 2023 19:40:56 +0200 Subject: [PATCH 089/101] Add docs requirements --- docs/requirements.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..b1ff250f --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,5 @@ +python-dateutil +shapely +cffi +sphinx +sphinx_rtd_theme \ No newline at end of file From 7f3403a5d02c0f88d25284218dd9223b46f2e1c4 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 5 Oct 2023 19:44:36 +0200 Subject: [PATCH 090/101] Add docs yml --- .readthedocs.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 6b131a63..2f9055ba 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -3,5 +3,10 @@ build: os: "ubuntu-20.04" tools: python: "3.10" + +python: + install: + - requirements: docs/requirements.txt + sphinx: configuration: docs/conf.py \ No newline at end of file From 36f3405187f88050052f14583a3168dac5b40c0d Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 5 Oct 2023 20:21:26 +0200 Subject: [PATCH 091/101] Add docs reqs --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index b1ff250f..b6a3d9d1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ python-dateutil shapely -cffi +pymeos_cffi==1.1.0a5 sphinx sphinx_rtd_theme \ No newline at end of file From c2a4e4175e44632461dd828f602e4259f301e461 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 5 Oct 2023 20:45:11 +0200 Subject: [PATCH 092/101] Pump version --- docs/requirements.txt | 2 +- pymeos/pyproject.toml | 4 ++-- pymeos_cffi/docker/Dockerfile | 3 ++- pymeos_cffi/docker/MEOS.Dockerfile | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index b6a3d9d1..1130c582 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ python-dateutil shapely -pymeos_cffi==1.1.0a5 +pymeos_cffi==1.1.0a6 sphinx sphinx_rtd_theme \ No newline at end of file diff --git a/pymeos/pyproject.toml b/pymeos/pyproject.toml index 1c4d696e..e7f93e38 100644 --- a/pymeos/pyproject.toml +++ b/pymeos/pyproject.toml @@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta' [project] name = 'pymeos' -version = '1.1.3-alpha.5' +version = '1.1.3-alpha.6-1' authors = [ { name = 'Victor Divi', email = 'vdiviloper@gmail.com' }, { name = 'Zhicheng Luo', email = 'zhicheng.luo@ulb.be' }, @@ -34,7 +34,7 @@ license = {file = 'LICENSE'} requires-python = '>=3.7' dependencies = [ - 'pymeos-cffi ==1.1.0a5', + 'pymeos-cffi ==1.1.0a6', 'python-dateutil', 'shapely', ] diff --git a/pymeos_cffi/docker/Dockerfile b/pymeos_cffi/docker/Dockerfile index 2e9a5b82..803f440d 100644 --- a/pymeos_cffi/docker/Dockerfile +++ b/pymeos_cffi/docker/Dockerfile @@ -2,7 +2,7 @@ FROM pymeos/meos:latest WORKDIR MobilityDB RUN git fetch -RUN git checkout develop +RUN git checkout errmsg RUN git pull @@ -17,6 +17,7 @@ RUN sed -i -e 's/\r$//' /build_wheels.sh RUN chmod +x /build_wheels.sh RUN rm -rf /opt/python/cp36-cp36m +RUN rm -rf /opt/python/cp312-cp312 RUN mkdir /wheelhouse_int ENV LD_LIBRARY_PATH=/usr/lib64;/lib64 diff --git a/pymeos_cffi/docker/MEOS.Dockerfile b/pymeos_cffi/docker/MEOS.Dockerfile index 4b58088e..c80def5c 100644 --- a/pymeos_cffi/docker/MEOS.Dockerfile +++ b/pymeos_cffi/docker/MEOS.Dockerfile @@ -3,4 +3,4 @@ FROM quay.io/pypa/manylinux2014_x86_64 RUN yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm RUN yum -y update RUN yum -y install gcc gcc-c++ make cmake postgresql13-devel proj-devel json-c-devel geos39-devel gsl-devel -RUN git clone https://github.com/MobilityDB/MobilityDB +RUN git clone https://github.com/estebanzimanyi/MobilityDB From 453a8f9de24cd5679999b61abebe9ea03f5d82c4 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Thu, 5 Oct 2023 20:50:12 +0200 Subject: [PATCH 093/101] Add missing requirements --- docs/requirements.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index 1130c582..bf960dfa 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,10 @@ python-dateutil shapely pymeos_cffi==1.1.0a6 +asyncpg +psycopg +psycopg2 +matplotlib +geopandas sphinx sphinx_rtd_theme \ No newline at end of file From fbd236cb3bbbb79d2feb73d5ecf67c97174433e9 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Fri, 6 Oct 2023 12:03:55 +0200 Subject: [PATCH 094/101] Add debug flag and print in error handler --- pymeos_cffi/pymeos_cffi/__init__.py | 5 +++ pymeos_cffi/pymeos_cffi/builder/meos.h | 8 ++--- .../builder/templates/functions.py | 10 ++++++ pymeos_cffi/pymeos_cffi/functions.py | 34 +++++++++++++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/pymeos_cffi/pymeos_cffi/__init__.py b/pymeos_cffi/pymeos_cffi/__init__.py index 4d335512..f35edcd8 100644 --- a/pymeos_cffi/pymeos_cffi/__init__.py +++ b/pymeos_cffi/pymeos_cffi/__init__.py @@ -27,6 +27,7 @@ 'MeosGeoJsonInputError', 'MeosGeoJsonOutputError', # Functions + 'meos_set_debug', 'py_error_handler', 'create_pointer', 'get_address', @@ -52,6 +53,10 @@ 'lwpoint_get_m', 'lwgeom_has_z', 'lwgeom_has_m', + 'meos_errno', + 'meos_errno_set', + 'meos_errno_restore', + 'meos_errno_reset', 'meos_initialize', 'meos_finalize', 'bool_in', diff --git a/pymeos_cffi/pymeos_cffi/builder/meos.h b/pymeos_cffi/pymeos_cffi/builder/meos.h index 084b6c2f..89e08bec 100644 --- a/pymeos_cffi/pymeos_cffi/builder/meos.h +++ b/pymeos_cffi/pymeos_cffi/builder/meos.h @@ -765,10 +765,10 @@ extern void meos_error(int errlevel, int errcode, char *format, ...); - - - - +extern int meos_errno(void); +extern int meos_errno_set(int err); +extern int meos_errno_restore(int err); +extern int meos_errno_reset(void); /***************************************************************************** * Initialization of the MEOS library diff --git a/pymeos_cffi/pymeos_cffi/builder/templates/functions.py b/pymeos_cffi/pymeos_cffi/builder/templates/functions.py index 93947266..1167eb28 100644 --- a/pymeos_cffi/pymeos_cffi/builder/templates/functions.py +++ b/pymeos_cffi/pymeos_cffi/builder/templates/functions.py @@ -1,3 +1,4 @@ +import os from datetime import datetime, timedelta from typing import Any, Tuple, Optional, List, Union @@ -15,6 +16,13 @@ _error_level: Optional[int] = None _error_message: Optional[str] = None +_debug = os.environ.get('MEOS_DEBUG', '0') == '1' + + +def meos_set_debug(debug: bool) -> None: + global _debug + _debug = debug + def _check_error() -> None: global _error, _error_level, _error_message @@ -34,6 +42,8 @@ def py_error_handler(error_level, error_code, error_msg): _error = error_code _error_level = error_level _error_message = _ffi.string(error_msg).decode('utf-8') + if _debug: + print(f'ERROR Handler called: Level: {_error} | Code: {_error_level} | Message: {_error_message}') def create_pointer(object: 'Any', type: str) -> 'Any *': diff --git a/pymeos_cffi/pymeos_cffi/functions.py b/pymeos_cffi/pymeos_cffi/functions.py index 0da167a3..9937d985 100644 --- a/pymeos_cffi/pymeos_cffi/functions.py +++ b/pymeos_cffi/pymeos_cffi/functions.py @@ -1,3 +1,4 @@ +import os from datetime import datetime, timedelta from typing import Any, Tuple, Optional, List, Union @@ -15,6 +16,13 @@ _error_level: Optional[int] = None _error_message: Optional[str] = None +_debug = os.environ.get('MEOS_DEBUG', '0') == '1' + + +def meos_set_debug(debug: bool) -> None: + global _debug + _debug = debug + def _check_error() -> None: global _error, _error_level, _error_message @@ -34,6 +42,8 @@ def py_error_handler(error_level, error_code, error_msg): _error = error_code _error_level = error_level _error_message = _ffi.string(error_msg).decode('utf-8') + if _debug: + print(f'ERROR Handler called: Level: {_error} | Code: {_error_level} | Message: {_error_message}') def create_pointer(object: 'Any', type: str) -> 'Any *': @@ -188,6 +198,30 @@ def lwgeom_has_m(geom: 'const LWGEOM *') -> 'int': return result if result != _ffi.NULL else None +def meos_errno() -> 'int': + result = _lib.meos_errno() + _check_error() + return result if result != _ffi.NULL else None + + +def meos_errno_set(err: int) -> 'int': + result = _lib.meos_errno_set(err) + _check_error() + return result if result != _ffi.NULL else None + + +def meos_errno_restore(err: int) -> 'int': + result = _lib.meos_errno_restore(err) + _check_error() + return result if result != _ffi.NULL else None + + +def meos_errno_reset() -> 'int': + result = _lib.meos_errno_reset() + _check_error() + return result if result != _ffi.NULL else None + + def meos_initialize(tz_str: "Optional[str]") -> None: tz_str_converted = tz_str.encode('utf-8') if tz_str is not None else _ffi.NULL _lib.meos_initialize(tz_str_converted, _lib.py_error_handler) From 18228a81d1c34fd96fc52210b81b68eb2396ae31 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Fri, 6 Oct 2023 12:10:08 +0200 Subject: [PATCH 095/101] Add debug capabilities to PyMEOS init functions --- pymeos/pymeos/meos_init.py | 19 +++++++++++++++++-- pymeos_cffi/pyproject.toml | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/pymeos/pymeos/meos_init.py b/pymeos/pymeos/meos_init.py index 2a4e0677..32c5da68 100644 --- a/pymeos/pymeos/meos_init.py +++ b/pymeos/pymeos/meos_init.py @@ -1,20 +1,35 @@ from typing import Optional -from pymeos_cffi import meos_initialize, meos_finalize +from pymeos_cffi import meos_initialize, meos_finalize, meos_set_debug -def pymeos_initialize(timezone: Optional[str] = None) -> None: +def pymeos_initialize(timezone: Optional[str] = None, debug: bool = False) -> None: """ Initializes the underlying MEOS platform. Must be called before any other PyMEOS function. Args: timezone: :class:`str` indicating the desired timezone to be used. Defaults to system timezone. + debug: :class:`bool` indicating whether debug mode should be enabled. Defaults to False. MEOS Functions: meos_initialize """ meos_initialize(timezone) + meos_set_debug(debug) + + +def pymeos_set_debug(debug: bool) -> None: + """ + Sets the debug mode of the underlying MEOS platform. + + Args: + debug: :class:`bool` indicating whether debug mode should be enabled. + + MEOS Functions: + meos_set_debug + """ + meos_set_debug(debug) def pymeos_finalize() -> None: diff --git a/pymeos_cffi/pyproject.toml b/pymeos_cffi/pyproject.toml index 378408cf..67a2b511 100644 --- a/pymeos_cffi/pyproject.toml +++ b/pymeos_cffi/pyproject.toml @@ -7,7 +7,7 @@ py-modules = [] [project] name = 'pymeos_cffi' -version = '1.1.0-alpha.6' +version = '1.1.0-alpha.7' authors = [ { name = 'Victor Divi', email = 'vdiviloper@gmail.com' } ] From 526db2cb717282fabcfb12ff882edc76d233a641 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Sat, 7 Oct 2023 17:01:39 +0200 Subject: [PATCH 096/101] Update collection documentation --- docs/pymeos/index.rst | 2 +- docs/pymeos/pymeos.collections.base.rst | 26 +++++++++++ docs/pymeos/pymeos.collections.numeric.rst | 53 ++++++++++++++++++++++ docs/pymeos/pymeos.collections.rst | 35 +++----------- docs/pymeos/pymeos.collections.spatial.rst | 29 ++++++++++++ docs/pymeos/pymeos.collections.text.rst | 10 ++++ docs/pymeos/pymeos.collections.time.rst | 34 ++++++++++++++ pymeos/pymeos/boxes/stbox.py | 6 +-- pymeos/pymeos/boxes/tbox.py | 6 +-- pymeos/pymeos/collections/geo/geoset.py | 21 +++++++-- pymeos/pymeos/main/tfloat.py | 6 +-- pymeos/pymeos/main/tpoint.py | 4 +- pymeos/tests/boxes/stbox_test.py | 2 +- pymeos/tests/boxes/tbox_test.py | 2 +- pymeos/tests/main/tfloat_test.py | 2 +- pymeos/tests/main/tgeogpoint_test.py | 2 +- pymeos/tests/main/tgeompoint_test.py | 2 +- 17 files changed, 193 insertions(+), 49 deletions(-) create mode 100644 docs/pymeos/pymeos.collections.base.rst create mode 100644 docs/pymeos/pymeos.collections.numeric.rst create mode 100644 docs/pymeos/pymeos.collections.spatial.rst create mode 100644 docs/pymeos/pymeos.collections.text.rst create mode 100644 docs/pymeos/pymeos.collections.time.rst diff --git a/docs/pymeos/index.rst b/docs/pymeos/index.rst index 0a99dda8..c83ee9bc 100644 --- a/docs/pymeos/index.rst +++ b/docs/pymeos/index.rst @@ -1,4 +1,4 @@ -PyMEOS package +PyMEOS API Reference ============== .. toctree:: diff --git a/docs/pymeos/pymeos.collections.base.rst b/docs/pymeos/pymeos.collections.base.rst new file mode 100644 index 00000000..b5fb3c04 --- /dev/null +++ b/docs/pymeos/pymeos.collections.base.rst @@ -0,0 +1,26 @@ +Base Abstract Collections +=================== + +Set +------------------------------- + +.. automodule:: pymeos.collections.base.set + :members: + :undoc-members: + :show-inheritance: + +Span +------------------------- + +.. automodule:: pymeos.collections.base.span + :members: + :undoc-members: + :show-inheritance: + +SpanSet +---------------------------- + +.. automodule:: pymeos.collections.base.spanset + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pymeos/pymeos.collections.numeric.rst b/docs/pymeos/pymeos.collections.numeric.rst new file mode 100644 index 00000000..b4eadd0b --- /dev/null +++ b/docs/pymeos/pymeos.collections.numeric.rst @@ -0,0 +1,53 @@ +Numeric Collections +=================== + +IntSet +----------------------- + +.. automodule:: pymeos.collections.number.intset + :members: + :undoc-members: + :show-inheritance: + + +FloatSet +----------------------- + +.. automodule:: pymeos.collections.number.floatset + :members: + :undoc-members: + :show-inheritance: + +IntSpan +----------------------- + +.. automodule:: pymeos.collections.number.intspan + :members: + :undoc-members: + :show-inheritance: + + +FloatSpan +----------------------- + +.. automodule:: pymeos.collections.number.floatspan + :members: + :undoc-members: + :show-inheritance: + +IntSpanSet +----------------------- + +.. automodule:: pymeos.collections.number.intspanset + :members: + :undoc-members: + :show-inheritance: + + +FloatSpanSet +----------------------- + +.. automodule:: pymeos.collections.number.floatspanset + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pymeos/pymeos.collections.rst b/docs/pymeos/pymeos.collections.rst index a4372f51..9e6ac529 100644 --- a/docs/pymeos/pymeos.collections.rst +++ b/docs/pymeos/pymeos.collections.rst @@ -1,34 +1,13 @@ Collections =================== -Time ------------------------ -.. automodule:: pymeos.collections.time.time - :members: - :undoc-members: - :show-inheritance: +.. toctree:: + :maxdepth: 2 -Period -------------------------- + pymeos.collections.base + pymeos.collections.time + pymeos.collections.spatial + pymeos.collections.numeric + pymeos.collections.text -.. automodule:: pymeos.collections.time.period - :members: - :undoc-members: - :show-inheritance: - -PeriodSet ----------------------------- - -.. automodule:: pymeos.collections.time.periodset - :members: - :undoc-members: - :show-inheritance: - -TimestampSet -------------------------------- - -.. automodule:: pymeos.collections.time.timestampset - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/pymeos/pymeos.collections.spatial.rst b/docs/pymeos/pymeos.collections.spatial.rst new file mode 100644 index 00000000..f66cf3d0 --- /dev/null +++ b/docs/pymeos/pymeos.collections.spatial.rst @@ -0,0 +1,29 @@ +Spatial Collections +=================== + + +GeoSet +----------------------- + +.. autoclass:: pymeos.collections.geo.geoset.GeoSet + :members: + :undoc-members: + :show-inheritance: + + +GeometrySet +----------------------- + +.. autoclass:: pymeos.collections.geo.geoset.GeometrySet + :members: + :undoc-members: + :show-inheritance: + + +GeographySet +----------------------- + +.. autoclass:: pymeos.collections.geo.geoset.GeographySet + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pymeos/pymeos.collections.text.rst b/docs/pymeos/pymeos.collections.text.rst new file mode 100644 index 00000000..c975e7cc --- /dev/null +++ b/docs/pymeos/pymeos.collections.text.rst @@ -0,0 +1,10 @@ +Text Collections +=================== + +TextSet +------------------------------- + +.. automodule:: pymeos.collections.text.textset + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pymeos/pymeos.collections.time.rst b/docs/pymeos/pymeos.collections.time.rst new file mode 100644 index 00000000..0bdcf7fa --- /dev/null +++ b/docs/pymeos/pymeos.collections.time.rst @@ -0,0 +1,34 @@ +Time Collections +=================== + +Time +----------------------- + +.. automodule:: pymeos.collections.time.time + :members: + :undoc-members: + :show-inheritance: + +TimestampSet +------------------------------- + +.. automodule:: pymeos.collections.time.timestampset + :members: + :undoc-members: + :show-inheritance: + +Period +------------------------- + +.. automodule:: pymeos.collections.time.period + :members: + :undoc-members: + :show-inheritance: + +PeriodSet +---------------------------- + +.. automodule:: pymeos.collections.time.periodset + :members: + :undoc-members: + :show-inheritance: diff --git a/pymeos/pymeos/boxes/stbox.py b/pymeos/pymeos/boxes/stbox.py index 27347c44..80c7508e 100644 --- a/pymeos/pymeos/boxes/stbox.py +++ b/pymeos/pymeos/boxes/stbox.py @@ -655,12 +655,12 @@ def shift_scale_time(self, shift: Optional[timedelta] = None, ) return STBox(_inner=result) - def round(self, maxdd : Optional[int] = 0) -> STBox: + def round(self, max_decimals : Optional[int] = 0) -> STBox: """ Returns `self` rounded to the given number of decimal digits. Args: - maxdd: Maximum number of decimal digits. + max_decimals: Maximum number of decimal digits. Returns: A new :class:`STBox` instance @@ -669,7 +669,7 @@ def round(self, maxdd : Optional[int] = 0) -> STBox: stbox_round """ new_inner = stbox_copy(self._inner) - stbox_round(new_inner, maxdd) + stbox_round(new_inner, max_decimals) return STBox(_inner=new_inner) # ------------------------- Set Operations -------------------------------- diff --git a/pymeos/pymeos/boxes/tbox.py b/pymeos/pymeos/boxes/tbox.py index ce99b7c9..b9e8d7fa 100644 --- a/pymeos/pymeos/boxes/tbox.py +++ b/pymeos/pymeos/boxes/tbox.py @@ -621,12 +621,12 @@ def shift_scale_time(self, shift: Optional[timedelta] = None, ) return TBox(_inner=result) - def round(self, maxdd: int = 0) -> TBox: + def round(self, max_decimals: int = 0) -> TBox: """ Returns `self` rounded to the given number of decimal digits. Args: - maxdd: Maximum number of decimal digits. + max_decimals: Maximum number of decimal digits. Returns: A new :class:`TBox` instance @@ -635,7 +635,7 @@ def round(self, maxdd: int = 0) -> TBox: tbox_round """ new_inner = tbox_copy(self._inner) - tbox_round(new_inner, maxdd) + tbox_round(new_inner, max_decimals) return TBox(_inner=new_inner) # ------------------------- Set Operations -------------------------------- diff --git a/pymeos/pymeos/collections/geo/geoset.py b/pymeos/pymeos/collections/geo/geoset.py index 46a5995a..8f2c0d13 100644 --- a/pymeos/pymeos/collections/geo/geoset.py +++ b/pymeos/pymeos/collections/geo/geoset.py @@ -1,7 +1,7 @@ from __future__ import annotations from abc import ABC -from typing import List, overload, Optional, Union +from typing import List, overload, Optional, Union, TypeVar import shapely as shp from pymeos_cffi import geoset_start_value, gserialized_to_shapely_geometry, geoset_end_value, geoset_value_n, \ @@ -12,6 +12,8 @@ from ..base import Set +Self = TypeVar('Self', bound='GeoSet') + class GeoSet(Set[shp.Geometry], ABC): __slots__ = ['_inner'] @@ -171,7 +173,7 @@ def contains(self, content: Union[GeoSet, str]) -> bool: # ------------------------- Set Operations -------------------------------- @overload - def intersection(self, other: str) -> Optional[str]: + def intersection(self, other: shp.Geometry) -> Optional[shp.Geometry]: ... @overload @@ -268,7 +270,19 @@ def union(self, other: Union[GeoSet, shp.Geometry]) -> GeoSet: # ------------------------- Transformations ------------------------------------ - def round(self, max_decimals): + def round(self: Self, max_decimals: int) -> Self: + """ + Rounds the coordinate values to a number of decimal places. + + Args: + max_decimals: The number of decimal places to use for the coordinates. + + Returns: + A new :class:`GeoSet` object of the same subtype of ``self``. + + MEOS Functions: + tpoint_roundgeoset_round + """ return self.__class__(_inner=geoset_round(self._inner, max_decimals)) @@ -282,6 +296,5 @@ class GeometrySet(GeoSet): class GeographySet(GeoSet): _mobilitydb_name = 'geogset' - _parse_function = geogset_in _parse_value_function = lambda x: pgis_geography_in(x, -1) if isinstance(x, str) else geography_to_gserialized(x) diff --git a/pymeos/pymeos/main/tfloat.py b/pymeos/pymeos/main/tfloat.py index 740e84da..eb0a32bc 100644 --- a/pymeos/pymeos/main/tfloat.py +++ b/pymeos/pymeos/main/tfloat.py @@ -783,12 +783,12 @@ def to_radians(self) -> TFloat: from ..factory import _TemporalFactory return _TemporalFactory.create_temporal(tfloat_radians(self._inner)) - def round(self, maxdd: int = 0) -> TFloat: + def round(self, max_decimals: int = 0) -> TFloat: """ Returns `self` rounded to the given number of decimal digits. Args: - maxdd: Maximum number of decimal digits. + max_decimals: Maximum number of decimal digits. Returns: A new :class:`TFloat` instance. @@ -798,7 +798,7 @@ def round(self, maxdd: int = 0) -> TFloat: """ from ..factory import _TemporalFactory return _TemporalFactory.create_temporal(tfloat_round(self._inner, - maxdd)) + max_decimals)) # ------------------------- Split Operations ------------------------------ def value_split(self, size: float, start: Optional[float] = 0) -> \ diff --git a/pymeos/pymeos/main/tpoint.py b/pymeos/pymeos/main/tpoint.py index 52456cc3..44d44093 100644 --- a/pymeos/pymeos/main/tpoint.py +++ b/pymeos/pymeos/main/tpoint.py @@ -420,7 +420,7 @@ def set_srid(self: Self, srid: int) -> Self: return self.__class__(_inner=tpoint_set_srid(self._inner, srid)) # ------------------------- Transformations ------------------------------- - def round(self, maxdd: int = 0) -> TPoint: + def round(self, max_decimals: int = 0) -> TPoint: """ Round the coordinate values to a number of decimal places. @@ -430,7 +430,7 @@ def round(self, maxdd: int = 0) -> TPoint: MEOS Functions: tpoint_round """ - result = tpoint_round(self._inner, maxdd) + result = tpoint_round(self._inner, max_decimals) return Temporal._factory(result) def make_simple(self) -> List[TPoint]: diff --git a/pymeos/tests/boxes/stbox_test.py b/pymeos/tests/boxes/stbox_test.py index acad9f3d..beef4d7a 100644 --- a/pymeos/tests/boxes/stbox_test.py +++ b/pymeos/tests/boxes/stbox_test.py @@ -609,7 +609,7 @@ def test_shift_scale_time(self): ids=['STBox X', 'STBox Z', 'STBox XT', 'STBox ZT'] ) def test_round(self, stbox, expected): - assert stbox.round(maxdd=2) + assert stbox.round(max_decimals=2) class TestSTBoxTopologicalFunctions(TestSTBox): diff --git a/pymeos/tests/boxes/tbox_test.py b/pymeos/tests/boxes/tbox_test.py index abc4fccc..e208223e 100644 --- a/pymeos/tests/boxes/tbox_test.py +++ b/pymeos/tests/boxes/tbox_test.py @@ -461,7 +461,7 @@ def test_shift_scale_time(self): ids=['TBoxFloat X', 'TBoxFloat XT'] ) def test_round(self, tbox, expected): - assert tbox.round(maxdd=2) + assert tbox.round(max_decimals=2) class TestTBoxTopologicalFunctions(TestTBox): diff --git a/pymeos/tests/main/tfloat_test.py b/pymeos/tests/main/tfloat_test.py index bb0515cd..16dd1c7c 100644 --- a/pymeos/tests/main/tfloat_test.py +++ b/pymeos/tests/main/tfloat_test.py @@ -1155,7 +1155,7 @@ def test_stops(self, tfloat, delta, expected): ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_round(self, temporal, expected): - assert temporal.round(maxdd=2) + assert temporal.round(max_decimals=2) class TestTFloatModifications(TestTFloat): diff --git a/pymeos/tests/main/tgeogpoint_test.py b/pymeos/tests/main/tgeogpoint_test.py index 1e98c00d..ac6663e1 100644 --- a/pymeos/tests/main/tgeogpoint_test.py +++ b/pymeos/tests/main/tgeogpoint_test.py @@ -1263,7 +1263,7 @@ def test_stops(self, tpoint, delta, expected): ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_round(self, temporal, expected): - assert temporal.round(maxdd=2) + assert temporal.round(max_decimals=2) class TestTGeogPointModifications(TestTGeogPoint): diff --git a/pymeos/tests/main/tgeompoint_test.py b/pymeos/tests/main/tgeompoint_test.py index 87ab082c..9b095d0d 100644 --- a/pymeos/tests/main/tgeompoint_test.py +++ b/pymeos/tests/main/tgeompoint_test.py @@ -1315,7 +1315,7 @@ def test_stops(self, tpoint, delta, expected): ids=['Instant', 'Discrete Sequence', 'Sequence', 'SequenceSet'] ) def test_round(self, temporal, expected): - assert temporal.round(maxdd=2) + assert temporal.round(max_decimals=2) @pytest.mark.parametrize( 'temporal, expected', From f69181268f002d7ab481f5f1d1937d401a5239c9 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Sat, 7 Oct 2023 17:10:23 +0200 Subject: [PATCH 097/101] Remove MEOS function from doc in pymeos_set_debug --- pymeos/pymeos/meos_init.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pymeos/pymeos/meos_init.py b/pymeos/pymeos/meos_init.py index 32c5da68..1999949e 100644 --- a/pymeos/pymeos/meos_init.py +++ b/pymeos/pymeos/meos_init.py @@ -25,9 +25,6 @@ def pymeos_set_debug(debug: bool) -> None: Args: debug: :class:`bool` indicating whether debug mode should be enabled. - - MEOS Functions: - meos_set_debug """ meos_set_debug(debug) From 12b93d78feff1f6b2535b4c8ce6f1c13159306d8 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Sat, 7 Oct 2023 23:14:59 +0200 Subject: [PATCH 098/101] Update structure and theme --- docs/conf.py | 3 +-- docs/index.rst | 19 +++++++++++++++---- docs/pymeos/index.rst | 15 --------------- docs/pymeos_cffi/index.rst | 6 ------ docs/requirements.txt | 2 +- .../api}/pymeos.aggregators.rst | 0 docs/{pymeos => src/api}/pymeos.boxes.rst | 0 .../api}/pymeos.collections.base.rst | 0 .../api}/pymeos.collections.numeric.rst | 0 .../api}/pymeos.collections.rst | 0 .../api}/pymeos.collections.spatial.rst | 0 .../api}/pymeos.collections.text.rst | 0 .../api}/pymeos.collections.time.rst | 0 docs/{pymeos => src/api}/pymeos.db.rst | 0 docs/{pymeos => src/api}/pymeos.main.rst | 0 docs/{pymeos => src/api}/pymeos.meos_init.rst | 0 docs/{pymeos => src/api}/pymeos.plotters.rst | 0 docs/{pymeos => src/api}/pymeos.temporal.rst | 0 docs/src/examples.rst | 3 +++ docs/src/installation.rst | 3 +++ docs/src/manual.rst | 3 +++ 21 files changed, 26 insertions(+), 28 deletions(-) delete mode 100644 docs/pymeos/index.rst delete mode 100644 docs/pymeos_cffi/index.rst rename docs/{pymeos => src/api}/pymeos.aggregators.rst (100%) rename docs/{pymeos => src/api}/pymeos.boxes.rst (100%) rename docs/{pymeos => src/api}/pymeos.collections.base.rst (100%) rename docs/{pymeos => src/api}/pymeos.collections.numeric.rst (100%) rename docs/{pymeos => src/api}/pymeos.collections.rst (100%) rename docs/{pymeos => src/api}/pymeos.collections.spatial.rst (100%) rename docs/{pymeos => src/api}/pymeos.collections.text.rst (100%) rename docs/{pymeos => src/api}/pymeos.collections.time.rst (100%) rename docs/{pymeos => src/api}/pymeos.db.rst (100%) rename docs/{pymeos => src/api}/pymeos.main.rst (100%) rename docs/{pymeos => src/api}/pymeos.meos_init.rst (100%) rename docs/{pymeos => src/api}/pymeos.plotters.rst (100%) rename docs/{pymeos => src/api}/pymeos.temporal.rst (100%) create mode 100644 docs/src/examples.rst create mode 100644 docs/src/installation.rst create mode 100644 docs/src/manual.rst diff --git a/docs/conf.py b/docs/conf.py index 83c28824..9e0df283 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,6 @@ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'sphinx.ext.intersphinx', - 'sphinx_rtd_theme', ] templates_path = ['_templates'] @@ -42,5 +41,5 @@ # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'sphinx_rtd_theme' +html_theme = 'sphinx_book_theme' html_static_path = ['_static'] diff --git a/docs/index.rst b/docs/index.rst index cb9ea613..43752837 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,13 +7,24 @@ Welcome to PyMEOS's documentation! ================================== .. toctree:: - :maxdepth: 2 - :caption: Contents: + :caption: User Guide - pymeos/index - pymeos_cffi/index + src/installation + src/manual + src/examples +.. toctree:: + :caption: API Reference + + src/api/pymeos.meos_init + src/api/pymeos.collections + src/api/pymeos.temporal + src/api/pymeos.main + src/api/pymeos.boxes + src/api/pymeos.aggregators + src/api/pymeos.plotters + src/api/pymeos.db Indices and tables ================== diff --git a/docs/pymeos/index.rst b/docs/pymeos/index.rst deleted file mode 100644 index c83ee9bc..00000000 --- a/docs/pymeos/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -PyMEOS API Reference -============== - -.. toctree:: - :maxdepth: 4 - - pymeos.meos_init - pymeos.collections - pymeos.temporal - pymeos.main - pymeos.boxes - pymeos.aggregators - pymeos.plotters - pymeos.db - diff --git a/docs/pymeos_cffi/index.rst b/docs/pymeos_cffi/index.rst deleted file mode 100644 index 5e79c6f8..00000000 --- a/docs/pymeos_cffi/index.rst +++ /dev/null @@ -1,6 +0,0 @@ -PyMEOS CFFI package -============== - -.. toctree:: - :maxdepth: 4 - diff --git a/docs/requirements.txt b/docs/requirements.txt index bf960dfa..1961cdf1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -7,4 +7,4 @@ psycopg2 matplotlib geopandas sphinx -sphinx_rtd_theme \ No newline at end of file +sphinx-book-theme \ No newline at end of file diff --git a/docs/pymeos/pymeos.aggregators.rst b/docs/src/api/pymeos.aggregators.rst similarity index 100% rename from docs/pymeos/pymeos.aggregators.rst rename to docs/src/api/pymeos.aggregators.rst diff --git a/docs/pymeos/pymeos.boxes.rst b/docs/src/api/pymeos.boxes.rst similarity index 100% rename from docs/pymeos/pymeos.boxes.rst rename to docs/src/api/pymeos.boxes.rst diff --git a/docs/pymeos/pymeos.collections.base.rst b/docs/src/api/pymeos.collections.base.rst similarity index 100% rename from docs/pymeos/pymeos.collections.base.rst rename to docs/src/api/pymeos.collections.base.rst diff --git a/docs/pymeos/pymeos.collections.numeric.rst b/docs/src/api/pymeos.collections.numeric.rst similarity index 100% rename from docs/pymeos/pymeos.collections.numeric.rst rename to docs/src/api/pymeos.collections.numeric.rst diff --git a/docs/pymeos/pymeos.collections.rst b/docs/src/api/pymeos.collections.rst similarity index 100% rename from docs/pymeos/pymeos.collections.rst rename to docs/src/api/pymeos.collections.rst diff --git a/docs/pymeos/pymeos.collections.spatial.rst b/docs/src/api/pymeos.collections.spatial.rst similarity index 100% rename from docs/pymeos/pymeos.collections.spatial.rst rename to docs/src/api/pymeos.collections.spatial.rst diff --git a/docs/pymeos/pymeos.collections.text.rst b/docs/src/api/pymeos.collections.text.rst similarity index 100% rename from docs/pymeos/pymeos.collections.text.rst rename to docs/src/api/pymeos.collections.text.rst diff --git a/docs/pymeos/pymeos.collections.time.rst b/docs/src/api/pymeos.collections.time.rst similarity index 100% rename from docs/pymeos/pymeos.collections.time.rst rename to docs/src/api/pymeos.collections.time.rst diff --git a/docs/pymeos/pymeos.db.rst b/docs/src/api/pymeos.db.rst similarity index 100% rename from docs/pymeos/pymeos.db.rst rename to docs/src/api/pymeos.db.rst diff --git a/docs/pymeos/pymeos.main.rst b/docs/src/api/pymeos.main.rst similarity index 100% rename from docs/pymeos/pymeos.main.rst rename to docs/src/api/pymeos.main.rst diff --git a/docs/pymeos/pymeos.meos_init.rst b/docs/src/api/pymeos.meos_init.rst similarity index 100% rename from docs/pymeos/pymeos.meos_init.rst rename to docs/src/api/pymeos.meos_init.rst diff --git a/docs/pymeos/pymeos.plotters.rst b/docs/src/api/pymeos.plotters.rst similarity index 100% rename from docs/pymeos/pymeos.plotters.rst rename to docs/src/api/pymeos.plotters.rst diff --git a/docs/pymeos/pymeos.temporal.rst b/docs/src/api/pymeos.temporal.rst similarity index 100% rename from docs/pymeos/pymeos.temporal.rst rename to docs/src/api/pymeos.temporal.rst diff --git a/docs/src/examples.rst b/docs/src/examples.rst new file mode 100644 index 00000000..60f6da35 --- /dev/null +++ b/docs/src/examples.rst @@ -0,0 +1,3 @@ +Examples +==================== + diff --git a/docs/src/installation.rst b/docs/src/installation.rst new file mode 100644 index 00000000..d4a81b85 --- /dev/null +++ b/docs/src/installation.rst @@ -0,0 +1,3 @@ +Installation +==================== + diff --git a/docs/src/manual.rst b/docs/src/manual.rst new file mode 100644 index 00000000..ca550896 --- /dev/null +++ b/docs/src/manual.rst @@ -0,0 +1,3 @@ +User Manual +==================== + From 0a4d16f0859db7469fcdfdc40d97a574a9c12837 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 9 Oct 2023 20:13:27 +0200 Subject: [PATCH 099/101] Add installation instructions --- docs/src/installation.rst | 79 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/docs/src/installation.rst b/docs/src/installation.rst index d4a81b85..f32f6777 100644 --- a/docs/src/installation.rst +++ b/docs/src/installation.rst @@ -1,3 +1,80 @@ Installation -==================== +============ +Built distributions +------------------- + +Built distributions don't require compiling PyMEOS, PyMEOS CFFI or MEOS, +and can be installed using ``pip``. + +Installation from PyPI +^^^^^^^^^^^^^^^^^^^^^^ + +PyMEOS and PyMEOS CFFI are available as binary distributions (wheel) for Linux platforms on +`PyPI `__. The distributions include the most recent version of MEOS available at the +time of the PyMEOS release. Install the binary wheel with pip as follows:: + + $ pip install pymeos + +Installation using conda (incoming, not available yet) +^^^^^^^^^^^^^^^^^^^^^^^^ + +PyMEOS is available on the conda-forge channel. Install as follows:: + + $ conda install pymeos --channel conda-forge + + +Installation from source with custom MEOS library +------------------------------------------------ + +If you want to use a specific MEOS version or a MEOS distribution that is +already present on your system, you can compile the PyMEOS packages from source yourself, +by directing pip to ignore the binary wheels. + +Note that only PyMEOS CFFI will need to be compiled from sources, +since PyMEOS is a pure Python package and doesn't interact with MEOS directly. + +First, make sure that MEOS is installed in your system. You can install it following the instructions +in the `MEOS documentation `__. + +Then, install PyMEOS CFFI from source:: + + $ pip install pymeos-cffi --no-binary pymeos-cffi + $ pip install pymeos + + +Updating the PyMEOS CFFI wrapper for custom MEOS library +^^^^^^^^^^^^^^^^^^^^^^^^ + +If your MEOS library API doesn't match the one used by the PyMEOS CFFI wrapper, it will crash. You can fix this +by updating the header file used by PyMEOS CFFI to match your MEOS version. To do so, you will need to recompile it +using the builder scripts provided in the ``pymeos_cffi`` package. + +First, you will need to get the source code of PyMEOS CFFI. You can do so by downloading the source distribution +from PyPI, or by cloning the repository from GitHub:: + + $ git clone git@github.com:MobilityDB/PyMEOS.git + $ cd PyMEOS/pymeos_cffi + +Then, you will need to run the header builder script, which will generate a new header file based on the MEOS +version installed in your system. The script accepts two parameters, a path to the MEOS header file, and a path to your +MEOS library:: + + $ python3 ./pymeos_cffi/builder/build_header.py + +If no parameters are passed, the script will use the default header file and library path:: + + $ python3 ./pymeos_cffi/builder/build_header.py /usr/local/include/meos.h /usr/local/lib/libmeos.so + +The second parameter is optional and is used to remove any function defined in the header file not exposed by the +library. If omitted, this step will not be performed. + +Then, you have to generate the PyMEOS CFFI wrapper functions using the functions builder script:: + + $ python3 ./pymeos_cffi/builder/build_pymeos_functions.py + +This will update the ``functions.py`` file that contains all the python functions exposed by the library. + +Finally, you can install the updated PyMEOS CFFI package:: + + $ pip install . From 41a0fd7cbc48bdb0cf727ce610a1c7f5c66e3c5d Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 9 Oct 2023 20:41:21 +0200 Subject: [PATCH 100/101] Update main page --- docs/index.rst | 36 ++++++++++++++++++++++++++++++++++-- docs/src/examples.rst | 1 + docs/src/manual.rst | 1 + 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 43752837..2a8c6ec6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,11 +3,42 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to PyMEOS's documentation! -================================== +======= +PyMEOS +======= + +`MEOS (Mobility Engine, Open Source) `__ is a +C library which enables the manipulation of temporal and spatio-temporal +data based on `MobilityDB `__\ ’s data types +and functions. + +PyMEOS is a library built on top of MEOS that provides all of its +functionality wrapped in a set of Python classes. + +Requirements +============ + +PyMEOS 1.1 requires + +* Python >=3.7 +* MEOS >=1.1 + +Installing PyMEOS +================== + +We recommend installing PyMEOS using one of the available built +distributions using ``pip``: + +.. code-block:: console + + $ pip install pymeos + +See the `installation documentation `__ +for more details and advanced installation instructions. .. toctree:: :caption: User Guide + :hidden: src/installation src/manual @@ -16,6 +47,7 @@ Welcome to PyMEOS's documentation! .. toctree:: :caption: API Reference + :hidden: src/api/pymeos.meos_init src/api/pymeos.collections diff --git a/docs/src/examples.rst b/docs/src/examples.rst index 60f6da35..4d4cd89c 100644 --- a/docs/src/examples.rst +++ b/docs/src/examples.rst @@ -1,3 +1,4 @@ Examples ==================== +Section under construction \ No newline at end of file diff --git a/docs/src/manual.rst b/docs/src/manual.rst index ca550896..dbf763c3 100644 --- a/docs/src/manual.rst +++ b/docs/src/manual.rst @@ -1,3 +1,4 @@ User Manual ==================== +Section under construction \ No newline at end of file From 48b416a987afc356aa744e410ba7e6bea63b4415 Mon Sep 17 00:00:00 2001 From: Diviloper Date: Mon, 9 Oct 2023 20:47:14 +0200 Subject: [PATCH 101/101] Add links to demos in Examples page --- docs/src/examples.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/examples.rst b/docs/src/examples.rst index 4d4cd89c..74ca9e77 100644 --- a/docs/src/examples.rst +++ b/docs/src/examples.rst @@ -1,4 +1,6 @@ Examples ==================== -Section under construction \ No newline at end of file +Section under construction. However, you can check the existing examples available in the +`PyMEOS repository `__ and the +`PyMEOS-Demo repository `__. \ No newline at end of file