diff --git a/pymeos/temporal/tsequenceset.py b/pymeos/temporal/tsequenceset.py index b9213130..3d407a99 100644 --- a/pymeos/temporal/tsequenceset.py +++ b/pymeos/temporal/tsequenceset.py @@ -1,16 +1,16 @@ from __future__ import annotations from abc import ABC +from datetime import timedelta from typing import Optional, List, Union, Any, TypeVar, Type, TYPE_CHECKING from pymeos_cffi import * -from .temporal import Temporal, import_pandas +from .temporal import Temporal, import_pandas, TInterpolation if TYPE_CHECKING: import pandas as pd - TBase = TypeVar("TBase") TG = TypeVar("TG", bound="Temporal[Any]") TI = TypeVar("TI", bound="TInstant[Any]") @@ -71,6 +71,22 @@ def from_sequences( """ return cls(sequence_list=sequence_list, normalize=normalize) + @classmethod + def from_instants_with_gaps( + cls, + instants: list[TI], + interpolation: TInterpolation, + max_time: timedelta = None, + max_distance: float = 0.0, + ): + instant_inners = [x._inner for x in instants] + interval = timedelta_to_interval(max_time) if max_time is not None else None + return cls( + _inner=tsequenceset_make_gaps( + instant_inners, interpolation, interval, max_distance + ) + ) + # ------------------------- Accessors ------------------------------------- def num_sequences(self) -> int: """ diff --git a/tests/main/tbool_test.py b/tests/main/tbool_test.py index 908a3348..d297e898 100644 --- a/tests/main/tbool_test.py +++ b/tests/main/tbool_test.py @@ -16,6 +16,7 @@ TsTzSpan, TsTzSpanSet, ) +from pymeos_cffi import MeosException from tests.conftest import TestPyMEOS @@ -249,6 +250,63 @@ def test_instant_list_sequence_constructor( assert str(tbs2) == expected assert tbs2.interpolation() == interpolation + @pytest.mark.parametrize( + "params, result", + [ + ( + ( + [ + TBoolInst("True@2000-01-01"), + TBoolInst("False@2000-01-02"), + TBoolInst("False@2000-01-05"), + ], + TInterpolation.STEPWISE, + None, + ), + TBoolSeqSet("{[True@2000-01-01, False@2000-01-02, False@2000-01-05]}"), + ), + ( + ( + [ + TBoolInst("True@2000-01-01"), + TBoolInst("False@2000-01-02"), + TBoolInst("False@2000-01-05"), + ], + TInterpolation.STEPWISE, + timedelta(days=2), + ), + TBoolSeqSet( + "{[True@2000-01-01, False@2000-01-02], [False@2000-01-05]}" + ), + ), + ], + ids=["No Gaps", "With Gaps"], + ) + def test_gaps_constructor(self, params, result): + assert TBoolSeqSet.from_instants_with_gaps(*params) == result + + @pytest.mark.parametrize( + "params", + [ + {"interpolation": TInterpolation.STEPWISE, "max_distance": 2}, + {"interpolation": TInterpolation.LINEAR}, + {"interpolation": TInterpolation.DISCRETE}, + ], + ids=[ + "Max Distance", + "Linear Interpolation", + "Discrete Interpolation", + ], + ) + def test_gaps_constructor_with_invalid_parameters_raises(self, params): + instants = [ + TBoolInst(value=True, timestamp="2000-01-01"), + TBoolInst(value=False, timestamp="2000-01-02"), + TBoolInst(value=False, timestamp="2000-01-03"), + ] + with pytest.raises(MeosException): + TBoolSeqSet.from_instants_with_gaps(instants, **params) + @pytest.mark.parametrize( "temporal", [tbi, tbds, tbs, tbss], diff --git a/tests/main/tfloat_test.py b/tests/main/tfloat_test.py index bf3bc41f..67bf5d93 100644 --- a/tests/main/tfloat_test.py +++ b/tests/main/tfloat_test.py @@ -3,7 +3,7 @@ from operator import not_ import pytest -from pymeos_cffi import MeosInvalidArgValueError +from pymeos_cffi import MeosInvalidArgValueError, MeosException from pymeos import ( TBoolInst, @@ -259,6 +259,150 @@ def test_instant_list_sequence_constructor( assert str(tfs2) == expected assert tfs2.interpolation() == interpolation + @pytest.mark.parametrize( + "params, result", + [ + ( + ( + [ + TFloatInst("1@2000-01-01"), + TFloatInst("5@2000-01-02"), + TFloatInst("6@2000-01-05"), + ], + TInterpolation.STEPWISE, + None, + ), + TFloatSeqSet( + "Interp=Step;{[1@2000-01-01, 5@2000-01-02, 6@2000-01-05]}" + ), + ), + ( + ( + [ + TFloatInst("1@2000-01-01"), + TFloatInst("5@2000-01-02"), + TFloatInst("6@2000-01-05"), + ], + TInterpolation.STEPWISE, + None, + 2.0, + ), + TFloatSeqSet( + "Interp=Step;{[1@2000-01-01], [5@2000-01-02, 6@2000-01-05]}" + ), + ), + ( + ( + [ + TFloatInst("1@2000-01-01"), + TFloatInst("5@2000-01-02"), + TFloatInst("6@2000-01-05"), + ], + TInterpolation.STEPWISE, + timedelta(days=2), + ), + TFloatSeqSet( + "Interp=Step;{[1@2000-01-01, 5@2000-01-02], [6@2000-01-05]}" + ), + ), + ( + ( + [ + TFloatInst("1@2000-01-01"), + TFloatInst("5@2000-01-02"), + TFloatInst("6@2000-01-05"), + ], + TInterpolation.STEPWISE, + timedelta(days=2), + 2.0, + ), + TFloatSeqSet( + "Interp=Step;{[1@2000-01-01], [5@2000-01-02], [6@2000-01-05]}" + ), + ), + ( + ( + [ + TFloatInst("1@2000-01-01"), + TFloatInst("5@2000-01-02"), + TFloatInst("6@2000-01-05"), + ], + TInterpolation.LINEAR, + None, + ), + TFloatSeqSet("{[1@2000-01-01, 5@2000-01-02, 6@2000-01-05]}"), + ), + ( + ( + [ + TFloatInst("1@2000-01-01"), + TFloatInst("5@2000-01-02"), + TFloatInst("6@2000-01-05"), + ], + TInterpolation.LINEAR, + None, + 2.0, + ), + TFloatSeqSet("{[1@2000-01-01], [5@2000-01-02, 6@2000-01-05]}"), + ), + ( + ( + [ + TFloatInst("1@2000-01-01"), + TFloatInst("5@2000-01-02"), + TFloatInst("6@2000-01-05"), + ], + TInterpolation.LINEAR, + timedelta(days=2), + ), + TFloatSeqSet("{[1@2000-01-01, 5@2000-01-02], [6@2000-01-05]}"), + ), + ( + ( + [ + TFloatInst("1@2000-01-01"), + TFloatInst("5@2000-01-02"), + TFloatInst("6@2000-01-05"), + ], + TInterpolation.LINEAR, + timedelta(days=2), + 2.0, + ), + TFloatSeqSet("{[1@2000-01-01], [5@2000-01-02], [6@2000-01-05]}"), + ), + ], + ids=[ + "No Gaps Stepwise", + "Value Gaps Stepwise", + "Time Gaps Stepwise", + "Value and Time Gaps Stepwise", + "No Gaps Linear", + "Value Gaps Linear", + "Time Gaps Linear", + "Value and Time Gaps Linear", + ], + ) + def test_gaps_constructor(self, params, result): + assert TFloatSeqSet.from_instants_with_gaps(*params) == result + + @pytest.mark.parametrize( + "params", + [ + {"interpolation": TInterpolation.DISCRETE}, + ], + ids=[ + "Discrete Interpolation", + ], + ) + def test_gaps_constructor_with_invalid_parameters_raises(self, params): + instants = [ + TFloatInst(value=1, timestamp="2000-01-01"), + TFloatInst(value=5, timestamp="2000-01-02"), + TFloatInst(value=6, timestamp="2000-01-03"), + ] + with pytest.raises(MeosException): + TFloatSeqSet.from_instants_with_gaps(instants, **params) + @pytest.mark.parametrize( "temporal", [tfi, tfds, tfs, tfss, tfsts, tfstss], diff --git a/tests/main/tgeogpoint_test.py b/tests/main/tgeogpoint_test.py index d7cadfcd..451a8295 100644 --- a/tests/main/tgeogpoint_test.py +++ b/tests/main/tgeogpoint_test.py @@ -5,7 +5,7 @@ import numpy as np import pytest import shapely.geometry -from pymeos_cffi import MeosInvalidArgValueError +from pymeos_cffi import MeosInvalidArgValueError, MeosException from shapely import Point, LineString, set_srid from pymeos import ( @@ -297,6 +297,158 @@ def test_instant_list_sequence_constructor( assert str(tps2) == expected assert tps2.interpolation() == interpolation + @pytest.mark.parametrize( + "params, result", + [ + ( + ( + [ + TGeogPointInst("POINT(1 1)@2000-01-01"), + TGeogPointInst("POINT(5 5)@2000-01-02"), + TGeogPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.STEPWISE, + None, + ), + TGeogPointSeqSet( + "Interp=Step;{[POINT(1 1)@2000-01-01, POINT(5 5)@2000-01-02, POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeogPointInst("POINT(1 1)@2000-01-01"), + TGeogPointInst("POINT(5 5)@2000-01-02"), + TGeogPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.STEPWISE, + None, + 200000.0, + ), + TGeogPointSeqSet( + "Interp=Step;{[POINT(1 1)@2000-01-01], [POINT(5 5)@2000-01-02, POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeogPointInst("POINT(1 1)@2000-01-01"), + TGeogPointInst("POINT(5 5)@2000-01-02"), + TGeogPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.STEPWISE, + timedelta(days=2), + ), + TGeogPointSeqSet( + "Interp=Step;{[POINT(1 1)@2000-01-01, POINT(5 5)@2000-01-02], [POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeogPointInst("POINT(1 1)@2000-01-01"), + TGeogPointInst("POINT(5 5)@2000-01-02"), + TGeogPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.STEPWISE, + timedelta(days=2), + 200000.0, + ), + TGeogPointSeqSet( + "Interp=Step;{[POINT(1 1)@2000-01-01], [POINT(5 5)@2000-01-02], [POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeogPointInst("POINT(1 1)@2000-01-01"), + TGeogPointInst("POINT(5 5)@2000-01-02"), + TGeogPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.LINEAR, + None, + ), + TGeogPointSeqSet( + "{[POINT(1 1)@2000-01-01, POINT(5 5)@2000-01-02, POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeogPointInst("POINT(1 1)@2000-01-01"), + TGeogPointInst("POINT(5 5)@2000-01-02"), + TGeogPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.LINEAR, + None, + 200000.0, + ), + TGeogPointSeqSet( + "{[POINT(1 1)@2000-01-01], [POINT(5 5)@2000-01-02, POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeogPointInst("POINT(1 1)@2000-01-01"), + TGeogPointInst("POINT(5 5)@2000-01-02"), + TGeogPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.LINEAR, + timedelta(days=2), + ), + TGeogPointSeqSet( + "{[POINT(1 1)@2000-01-01, POINT(5 5)@2000-01-02], [POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeogPointInst("POINT(1 1)@2000-01-01"), + TGeogPointInst("POINT(5 5)@2000-01-02"), + TGeogPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.LINEAR, + timedelta(days=2), + 200000.0, + ), + TGeogPointSeqSet( + "{[POINT(1 1)@2000-01-01], [POINT(5 5)@2000-01-02], [POINT(6 6)@2000-01-05]}" + ), + ), + ], + ids=[ + "No Gaps Stepwise", + "Value Gaps Stepwise", + "Time Gaps Stepwise", + "Value and Time Gaps Stepwise", + "No Gaps Linear", + "Value Gaps Linear", + "Time Gaps Linear", + "Value and Time Gaps Linear", + ], + ) + def test_gaps_constructor(self, params, result): + assert TGeogPointSeqSet.from_instants_with_gaps(*params) == result + + @pytest.mark.parametrize( + "params", + [ + {"interpolation": TInterpolation.DISCRETE}, + ], + ids=[ + "Discrete Interpolation", + ], + ) + def test_gaps_constructor_with_invalid_parameters_raises(self, params): + instants = [ + TGeogPointInst(point="POINT(1 1)", timestamp="2000-01-01"), + TGeogPointInst(point="POINT(5 5)", timestamp="2000-01-02"), + TGeogPointInst(point="POINT(6 6)", timestamp="2000-01-03"), + ] + with pytest.raises(MeosException): + TGeogPointSeqSet.from_instants_with_gaps(instants, **params) + @pytest.mark.parametrize( "temporal", [tpi, tpds, tps, tpss, tpi3d, tpds3d, tps3d, tpss3d], diff --git a/tests/main/tgeompoint_test.py b/tests/main/tgeompoint_test.py index 5019ceb0..b6e43955 100644 --- a/tests/main/tgeompoint_test.py +++ b/tests/main/tgeompoint_test.py @@ -5,7 +5,7 @@ import pytest import math import numpy as np -from pymeos_cffi import MeosInvalidArgValueError +from pymeos_cffi import MeosInvalidArgValueError, MeosException from shapely import Point, LineString, Polygon, MultiPoint, GeometryCollection from pymeos import ( @@ -289,6 +289,158 @@ def test_instant_list_sequence_constructor( assert str(tps2) == expected assert tps2.interpolation() == interpolation + @pytest.mark.parametrize( + "params, result", + [ + ( + ( + [ + TGeomPointInst("POINT(1 1)@2000-01-01"), + TGeomPointInst("POINT(5 5)@2000-01-02"), + TGeomPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.STEPWISE, + None, + ), + TGeomPointSeqSet( + "Interp=Step;{[POINT(1 1)@2000-01-01, POINT(5 5)@2000-01-02, POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeomPointInst("POINT(1 1)@2000-01-01"), + TGeomPointInst("POINT(5 5)@2000-01-02"), + TGeomPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.STEPWISE, + None, + 2.0, + ), + TGeomPointSeqSet( + "Interp=Step;{[POINT(1 1)@2000-01-01], [POINT(5 5)@2000-01-02, POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeomPointInst("POINT(1 1)@2000-01-01"), + TGeomPointInst("POINT(5 5)@2000-01-02"), + TGeomPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.STEPWISE, + timedelta(days=2), + ), + TGeomPointSeqSet( + "Interp=Step;{[POINT(1 1)@2000-01-01, POINT(5 5)@2000-01-02], [POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeomPointInst("POINT(1 1)@2000-01-01"), + TGeomPointInst("POINT(5 5)@2000-01-02"), + TGeomPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.STEPWISE, + timedelta(days=2), + 2.0, + ), + TGeomPointSeqSet( + "Interp=Step;{[POINT(1 1)@2000-01-01], [POINT(5 5)@2000-01-02], [POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeomPointInst("POINT(1 1)@2000-01-01"), + TGeomPointInst("POINT(5 5)@2000-01-02"), + TGeomPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.LINEAR, + None, + ), + TGeomPointSeqSet( + "{[POINT(1 1)@2000-01-01, POINT(5 5)@2000-01-02, POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeomPointInst("POINT(1 1)@2000-01-01"), + TGeomPointInst("POINT(5 5)@2000-01-02"), + TGeomPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.LINEAR, + None, + 2.0, + ), + TGeomPointSeqSet( + "{[POINT(1 1)@2000-01-01], [POINT(5 5)@2000-01-02, POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeomPointInst("POINT(1 1)@2000-01-01"), + TGeomPointInst("POINT(5 5)@2000-01-02"), + TGeomPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.LINEAR, + timedelta(days=2), + ), + TGeomPointSeqSet( + "{[POINT(1 1)@2000-01-01, POINT(5 5)@2000-01-02], [POINT(6 6)@2000-01-05]}" + ), + ), + ( + ( + [ + TGeomPointInst("POINT(1 1)@2000-01-01"), + TGeomPointInst("POINT(5 5)@2000-01-02"), + TGeomPointInst("POINT(6 6)@2000-01-05"), + ], + TInterpolation.LINEAR, + timedelta(days=2), + 2.0, + ), + TGeomPointSeqSet( + "{[POINT(1 1)@2000-01-01], [POINT(5 5)@2000-01-02], [POINT(6 6)@2000-01-05]}" + ), + ), + ], + ids=[ + "No Gaps Stepwise", + "Value Gaps Stepwise", + "Time Gaps Stepwise", + "Value and Time Gaps Stepwise", + "No Gaps Linear", + "Value Gaps Linear", + "Time Gaps Linear", + "Value and Time Gaps Linear", + ], + ) + def test_gaps_constructor(self, params, result): + assert TGeomPointSeqSet.from_instants_with_gaps(*params) == result + + @pytest.mark.parametrize( + "params", + [ + {"interpolation": TInterpolation.DISCRETE}, + ], + ids=[ + "Discrete Interpolation", + ], + ) + def test_gaps_constructor_with_invalid_parameters_raises(self, params): + instants = [ + TGeomPointInst(point="POINT(1 1)", timestamp="2000-01-01"), + TGeomPointInst(point="POINT(5 5)", timestamp="2000-01-02"), + TGeomPointInst(point="POINT(6 6)", timestamp="2000-01-03"), + ] + with pytest.raises(MeosException): + TGeomPointSeqSet.from_instants_with_gaps(instants, **params) + @pytest.mark.parametrize( "temporal", [tpi, tpds, tps, tpss, tpi3d, tpds3d, tps3d, tpss3d], diff --git a/tests/main/tint_test.py b/tests/main/tint_test.py index bf1cc6b5..29bf4c22 100644 --- a/tests/main/tint_test.py +++ b/tests/main/tint_test.py @@ -3,6 +3,7 @@ from operator import not_ import pytest +from pymeos_cffi import MeosException from pymeos import ( TBoolInst, @@ -251,6 +252,96 @@ def test_instant_list_sequence_constructor( assert str(tis2) == expected assert tis2.interpolation() == interpolation + @pytest.mark.parametrize( + "params, result", + [ + ( + ( + [ + TIntInst("1@2000-01-01"), + TIntInst("5@2000-01-02"), + TIntInst("6@2000-01-05"), + ], + TInterpolation.STEPWISE, + None, + ), + TIntSeqSet("Interp=Step;{[1@2000-01-01, 5@2000-01-02, 6@2000-01-05]}"), + ), + ( + ( + [ + TIntInst("1@2000-01-01"), + TIntInst("5@2000-01-02"), + TIntInst("6@2000-01-05"), + ], + TInterpolation.STEPWISE, + None, + 2.0, + ), + TIntSeqSet( + "Interp=Step;{[1@2000-01-01], [5@2000-01-02, 6@2000-01-05]}" + ), + ), + ( + ( + [ + TIntInst("1@2000-01-01"), + TIntInst("5@2000-01-02"), + TIntInst("6@2000-01-05"), + ], + TInterpolation.STEPWISE, + timedelta(days=2), + ), + TIntSeqSet( + "Interp=Step;{[1@2000-01-01, 5@2000-01-02], [6@2000-01-05]}" + ), + ), + ( + ( + [ + TIntInst("1@2000-01-01"), + TIntInst("5@2000-01-02"), + TIntInst("6@2000-01-05"), + ], + TInterpolation.STEPWISE, + timedelta(days=2), + 2.0, + ), + TIntSeqSet( + "Interp=Step;{[1@2000-01-01], [5@2000-01-02], [6@2000-01-05]}" + ), + ), + ], + ids=[ + "No Gaps Stepwise", + "Value Gaps Stepwise", + "Time Gaps Stepwise", + "Value and Time Gaps Stepwise", + ], + ) + def test_gaps_constructor(self, params, result): + assert TIntSeqSet.from_instants_with_gaps(*params) == result + + @pytest.mark.parametrize( + "params", + [ + {"interpolation": TInterpolation.LINEAR}, + {"interpolation": TInterpolation.DISCRETE}, + ], + ids=[ + "Linear Interpolation", + "Discrete Interpolation", + ], + ) + def test_gaps_constructor_with_invalid_parameters_raises(self, params): + instants = [ + TIntInst(value=1, timestamp="2000-01-01"), + TIntInst(value=5, timestamp="2000-01-02"), + TIntInst(value=6, timestamp="2000-01-03"), + ] + with pytest.raises(MeosException): + TIntSeqSet.from_instants_with_gaps(instants, **params) + @pytest.mark.parametrize( "temporal", [tii, tids, tis, tiss], diff --git a/tests/main/ttext_test.py b/tests/main/ttext_test.py index f41bc756..38b988b8 100644 --- a/tests/main/ttext_test.py +++ b/tests/main/ttext_test.py @@ -2,6 +2,7 @@ from datetime import datetime, timezone, timedelta import pytest +from pymeos_cffi import MeosException from pymeos import ( TBool, @@ -252,6 +253,57 @@ def test_instant_list_sequence_constructor( assert str(tts2) == expected assert tts2.interpolation() == interpolation + @pytest.mark.parametrize( + "params, result", + [ + ( + ( + [ + TTextInst("A@2000-01-01"), + TTextInst("B@2000-01-02"), + TTextInst("C@2000-01-05"), + ], + TInterpolation.STEPWISE, + None, + ), + TTextSeqSet("{[A@2000-01-01, B@2000-01-02, C@2000-01-05]}"), + ), + ( + ( + [ + TTextInst("A@2000-01-01"), + TTextInst("B@2000-01-02"), + TTextInst("C@2000-01-05"), + ], + TInterpolation.STEPWISE, + timedelta(days=2), + ), + TTextSeqSet("{[A@2000-01-01, B@2000-01-02], [C@2000-01-05]}"), + ), + ], + ids=["No Gaps", "With Gaps"], + ) + def test_gaps_constructor(self, params, result): + assert TTextSeqSet.from_instants_with_gaps(*params) == result + + @pytest.mark.parametrize( + "params", + [ + {"interpolation": TInterpolation.STEPWISE, "max_distance": 2}, + {"interpolation": TInterpolation.LINEAR}, + {"interpolation": TInterpolation.DISCRETE}, + ], + ids=["Max Distance", "Linear Interpolation", "Discrete Interpolation"], + ) + def test_gaps_constructor_with_invalid_parameters_raises(self, params): + instants = [ + TTextInst(value="A", timestamp="2000-01-01"), + TTextInst(value="B", timestamp="2000-01-02"), + TTextInst(value="C", timestamp="2000-01-03"), + ] + with pytest.raises(MeosException): + TTextSeqSet.from_instants_with_gaps(instants, **params) + @pytest.mark.parametrize( "temporal", [tti, ttds, tts, ttss],