diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 967c58b0..636ca886 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,8 +23,51 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: + # Derive the MEOS catalog once from the MobilityDB branch the package version + # maps to, through the shared provision-meos action. PyMEOS-CFFI projects its + # cdef header from this catalog, so every test job builds the same surface. + # The catalog parses the headers a libmeos build installs, as PyMEOS-CFFI's + # pr_build does, so it declares no internal header the extension cannot include. + catalog: + name: Derive the MEOS catalog + runs-on: ubuntu-latest + outputs: + meos_branch: ${{ steps.meos_branch.outputs.meos_branch }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Derive MEOS branch from package version + id: meos_branch + shell: bash + run: | + pymeos_version=$(sed -nE 's/^__version__ = "([^"]+)".*/\1/p' pymeos/__init__.py) + major_minor=$(echo "$pymeos_version" | sed -nE 's/^([0-9]+\.[0-9]+).*/\1/p') + candidate="stable-${major_minor}" + if git ls-remote --exit-code https://github.com/MobilityDB/MobilityDB "$candidate" >/dev/null 2>&1; then + meos_branch=$candidate + else + meos_branch="master" + fi + echo "Package version $pymeos_version => MEOS branch $meos_branch" + echo "meos_branch=$meos_branch" >> "$GITHUB_OUTPUT" + + - name: Provision the MEOS catalog + id: provision + uses: MobilityDB/MEOS-API/.github/actions/provision-meos@master + with: + mobilitydb-ref: ${{ steps.meos_branch.outputs.meos_branch }} + build-libmeos: "true" + + - name: Upload the catalog + uses: actions/upload-artifact@v4 + with: + name: meos-idl + path: ${{ steps.provision.outputs.catalog-path }} + test: name: Test PyMEOS - Python ${{ matrix.python-version }} on ${{ matrix.os }} + needs: catalog runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -44,44 +87,69 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Get dependencies from apt (cache) - uses: awalsh128/cache-apt-pkgs-action@v1.4.3 + # The CFFI cdef covers every optional MEOS family, so libmeos is built + # with all of them: on Linux by the provision-meos action, from the same + # MobilityDB branch as the catalog. + - name: Build and install the all-families MEOS (Linux) if: runner.os == 'Linux' + uses: MobilityDB/MEOS-API/.github/actions/provision-meos@master with: - packages: build-essential cmake postgresql-server-dev-16 libproj-dev libjson-c-dev libgsl-dev libgeos-dev - version: 1.1 + mobilitydb-ref: ${{ needs.catalog.outputs.meos_branch }} + build-libmeos: "true" - name: Get dependencies from homebrew (cache) uses: tecolicom/actions-use-homebrew-tools@v1 if: runner.os == 'macOS' with: - tools: cmake libpq proj json-c gsl geos + tools: cmake libpq proj json-c gsl geos h3 - - name: Derive MEOS branch from package version - id: meos_branch - shell: bash + # The raster family links GDAL. Homebrew's GDAL links Apache Arrow for its Arrow and + # Parquet drivers, and pyarrow's bundled Arrow then binds to that copy and fails to load, + # so GDAL is built here without Arrow or Parquet, as MobilityDuck builds its own: core + # drivers only, against Homebrew's PROJ. Its install names are absolute, so libmeos finds + # it without a library path. + - name: Key the GDAL build (macOS) + id: gdal + if: runner.os == 'macOS' run: | - pymeos_version=$(sed -nE 's/^__version__ = "([^"]+)".*/\1/p' pymeos/__init__.py) - major_minor=$(echo "$pymeos_version" | sed -nE 's/^([0-9]+\.[0-9]+).*/\1/p') - candidate="stable-${major_minor}" - if git ls-remote --exit-code https://github.com/MobilityDB/MobilityDB "$candidate" >/dev/null 2>&1; then - meos_branch=$candidate - else - meos_branch="master" - fi - echo "Package version $pymeos_version => MEOS branch $meos_branch" - echo "meos_branch=$meos_branch" >> "$GITHUB_OUTPUT" + echo "version=3.11.4" >> "$GITHUB_OUTPUT" + echo "prefix=$HOME/gdal" >> "$GITHUB_OUTPUT" + echo "proj=$(brew list --versions proj | cut -d' ' -f2)" >> "$GITHUB_OUTPUT" - - name: Fetch MEOS sources + - name: Restore the GDAL build (macOS) + id: gdal-cache + if: runner.os == 'macOS' + uses: actions/cache@v4 + with: + path: ${{ steps.gdal.outputs.prefix }} + key: gdal-${{ steps.gdal.outputs.version }}-proj-${{ steps.gdal.outputs.proj }}-${{ matrix.os }} + + - name: Build GDAL without Arrow (macOS) + if: runner.os == 'macOS' && steps.gdal-cache.outputs.cache-hit != 'true' run: | - git clone --branch ${{ steps.meos_branch.outputs.meos_branch }} --depth 1 https://github.com/MobilityDB/MobilityDB + curl -sSL -o gdal.tar.gz \ + https://github.com/OSGeo/gdal/releases/download/v${{ steps.gdal.outputs.version }}/gdal-${{ steps.gdal.outputs.version }}.tar.gz + tar xzf gdal.tar.gz + cmake -S gdal-${{ steps.gdal.outputs.version }} -B gdal-build -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=${{ steps.gdal.outputs.prefix }} \ + -DCMAKE_INSTALL_NAME_DIR=${{ steps.gdal.outputs.prefix }}/lib \ + -DCMAKE_PREFIX_PATH="$(brew --prefix)" \ + -DBUILD_APPS=OFF -DBUILD_TESTING=OFF -DBUILD_DOCS=OFF -DBUILD_PYTHON_BINDINGS=OFF \ + -DGDAL_BUILD_OPTIONAL_DRIVERS=OFF -DOGR_BUILD_OPTIONAL_DRIVERS=OFF \ + -DGDAL_USE_ARROW=OFF -DGDAL_USE_PARQUET=OFF \ + -DCMAKE_DISABLE_FIND_PACKAGE_Arrow=ON -DCMAKE_DISABLE_FIND_PACKAGE_Parquet=ON + cmake --build gdal-build -j"$(sysctl -n hw.ncpu)" + cmake --install gdal-build - - name: Install MEOS + - name: Build and install the all-families MEOS (macOS) + if: runner.os == 'macOS' run: | + git clone --branch ${{ needs.catalog.outputs.meos_branch }} --depth 1 https://github.com/MobilityDB/MobilityDB mkdir MobilityDB/build cd MobilityDB/build - cmake .. -DMEOS=on -DCMAKE_INSTALL_PREFIX=${{ matrix.ld_prefix }} - make -j + cmake .. -DMEOS=ON -DALL=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${{ matrix.ld_prefix }} \ + -DCMAKE_PREFIX_PATH=${{ steps.gdal.outputs.prefix }} + make -j"$(sysctl -n hw.ncpu)" sudo make install - name: Set up Python ${{ matrix.python-version }} @@ -97,6 +165,12 @@ jobs: git clone --branch ${{ env.BRANCH_NAME }} --depth 1 https://github.com/MobilityDB/PyMEOS-CFFI \ || git clone --branch ${{ github.base_ref || 'master' }} --depth 1 https://github.com/MobilityDB/PyMEOS-CFFI + - name: Download the catalog + uses: actions/download-artifact@v4 + with: + name: meos-idl + path: PyMEOS-CFFI/builder + - name: Install python dependencies run: | python -m pip install --upgrade pip @@ -104,9 +178,11 @@ jobs: pip install -r dev-requirements.txt - name: Install pymeos_cffi + shell: bash run: | cd PyMEOS-CFFI - python ./builder/build_header.py + python ./builder/build_header.py builder/meos-idl.json \ + ${{ matrix.ld_prefix }}/lib/${{ runner.os == 'macOS' && 'libmeos.dylib' || 'libmeos.so' }} builder/meos.h python ./builder/build_pymeos_functions.py pip install . diff --git a/pymeos/__init__.py b/pymeos/__init__.py index 1aa97301..004ad8ab 100644 --- a/pymeos/__init__.py +++ b/pymeos/__init__.py @@ -30,7 +30,7 @@ from .meos_init import * from .temporal import * -__version__ = "1.3.0" +__version__ = "1.4.0-alpha-1" __all__ = [ # initialization "pymeos_initialize", diff --git a/pymeos/aggregators/aggregator.py b/pymeos/aggregators/aggregator.py index be86e04d..96d0dc63 100644 --- a/pymeos/aggregators/aggregator.py +++ b/pymeos/aggregators/aggregator.py @@ -243,12 +243,12 @@ def _add( interval_converted = ( timedelta_to_interval(interval) if isinstance(interval, timedelta) - else pg_interval_in(interval, -1) + else interval_in(interval, -1) if isinstance(interval, str) else None ) origin_converted = ( - datetime_to_timestamptz(origin) if isinstance(origin, datetime) else pg_timestamptz_in(origin, -1) + datetime_to_timestamptz(origin) if isinstance(origin, datetime) else timestamptz_in(origin, -1) ) return cls._add_function(state, temporal._inner, interval_converted, origin_converted) diff --git a/pymeos/aggregators/time_aggregators.py b/pymeos/aggregators/time_aggregators.py index 159503d4..a730ae8e 100644 --- a/pymeos/aggregators/time_aggregators.py +++ b/pymeos/aggregators/time_aggregators.py @@ -4,9 +4,10 @@ datetime_to_timestamptz, set_union_finalfn, set_union_transfn, + span_union_transfn, + spanset_union_finalfn, + spanset_union_transfn, timestamptz_union_transfn, - union_spanset_span, - union_spanset_spanset, ) from ..collections import TsTzSet, TsTzSpan, TsTzSpanSet @@ -43,20 +44,24 @@ class TimeContinuousUnionAggregator(BaseAggregator[TsTzSpan | TsTzSpanSet, TsTzS Temporal union of continuous time objects (:class:`~pymeos.time.tstzspan.TsTzSpan` and :class:`~pymeos.time.tstzspanset.TsTzSpanSet`). + Aggregates as #TimeInstantaneousUnionAggregator does, through the transition and final + functions MEOS provides for the union of spans. + MEOS Functions: - union_spanset_span, union_spanset_spanset + span_union_transfn, spanset_union_transfn, spanset_union_finalfn """ @classmethod def _add(cls, state, temporal): if isinstance(temporal, TsTzSpan): - state = union_spanset_span(state, temporal._inner) + state = span_union_transfn(state, temporal._inner) elif isinstance(temporal, TsTzSpanSet): - state = union_spanset_spanset(state, temporal._inner) + state = spanset_union_transfn(state, temporal._inner) else: cls._error(temporal) return state @classmethod def _finish(cls, state) -> TsTzSpanSet: - return TsTzSpanSet(_inner=state) + result = spanset_union_finalfn(state) + return TsTzSpanSet(_inner=result) diff --git a/pymeos/boxes/stbox.py b/pymeos/boxes/stbox.py index 9d24f7bc..0cfbfd21 100644 --- a/pymeos/boxes/stbox.py +++ b/pymeos/boxes/stbox.py @@ -178,7 +178,7 @@ def from_geometry(geom: shp.BaseGeometry, geodetic: bool = False) -> STBox: A new :class:`STBox` instance. MEOS Functions: - pgis_geometry_in, geo_to_stbox + geom_in, geog_in, geo_to_stbox """ gs = geo_to_gserialized(geom, geodetic) return STBox(_inner=geo_to_stbox(gs)) @@ -1157,15 +1157,15 @@ def nearest_approach_distance(self, other: shp.BaseGeometry | STBox | TPoint) -> ``self`` and ``other``. MEOS Functions: - nad_stbox_geo, nad_stbox_stbox + nad_stbox_geo, nad_stbox_stbox, nad_tgeo_stbox """ if isinstance(other, shp.BaseGeometry): gs = geo_to_gserialized(other, self.geodetic()) - return nad_stbox_geo(self._inner, gs) + return nad_stbox_geo(self._inner, gs, spheroid=True) elif isinstance(other, STBox): - return nad_stbox_stbox(self._inner, other._inner) + return nad_stbox_stbox(self._inner, other._inner, spheroid=True) elif isinstance(other, TPoint): - return nad_tpoint_stbox(other._inner, self._inner) + return nad_tgeo_stbox(other._inner, self._inner, spheroid=True) else: raise TypeError(f"Operation not supported with type {other.__class__}") @@ -1243,69 +1243,102 @@ def quad_split(self) -> list[list[STBox]] | list[list[list[STBox]]]: [STBox(_inner=boxes + i) for i in range(2, 4)], ] - def tile( + def space_tiles( self, - size: float | None = None, - duration: timedelta | str | None = None, - origin: shp.BaseGeometry | None = None, - start: datetime | str | None = None, - border_inc: bool = True, + xsize: float, + ysize: float, + zsize: float, + origin: shp.BaseGeometry, + border_inc: bool, ) -> list[STBox]: """ - Returns a list of `STBox` instances representing the tiles of - ``self``. + Returns the spatial tiles of ``self``. + + The arguments are those of the MEOS function, marshalled as + #TPoint.space_split marshals the origin. 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 used by default is Monday, January 3, 2000. - border_inc: Whether the border is inclusive or not. + xsize: The size of the tiles along the x dimension. + ysize: The size of the tiles along the y dimension; 0 takes ``xsize``. + zsize: The size of the tiles along the z dimension, read only for a + box with Z; 0 takes ``xsize``. + origin: The origin of the spatial tiling. + border_inc: Whether a tile contains its upper border. Returns: A list of `STBox` instances. MEOS Functions: - stbox_tile_list + stbox_space_tiles """ - sz = size or ( - max( - self.xmax() - self.xmin(), - self.ymax() - self.ymin(), - (self.zmax() - self.zmin() if self.has_z() else 0), - ) - + 1 - ) - dt = ( - timedelta_to_interval(duration) - if isinstance(duration, timedelta) - else pg_interval_in(duration, -1) - if isinstance(duration, str) - else None - ) - 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) - 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() else pgis_geometry_in("Point(0 0 0)", -1)) - ) - tiles, count = stbox_space_time_tiles(self._inner, sz, sz, sz, dt, gs, st, border_inc) + gs = geo_to_gserialized(origin, self.geodetic()) + tiles, count = stbox_space_tiles(self._inner, xsize, ysize, zsize, gs, border_inc) + return [STBox(_inner=tiles + i) for i in range(count)] + + def space_time_tiles( + self, + xsize: float, + ysize: float, + zsize: float, + duration: timedelta | str, + origin: shp.BaseGeometry, + start: datetime | str, + border_inc: bool, + ) -> list[STBox]: + """ + Returns the spatiotemporal tiles of ``self``. + + The arguments are those of the MEOS function, marshalled as + #TPoint.space_time_split marshals the duration, the origin and the start. + + Args: + xsize: The size of the tiles along the x dimension. + ysize: The size of the tiles along the y dimension; 0 takes ``xsize``. + zsize: The size of the tiles along the z dimension, read only for a + box with Z; 0 takes ``xsize``. + duration: The duration of the tiles. + origin: The origin of the spatial tiling. + start: The origin of the temporal tiling. + border_inc: Whether a tile contains its upper border. + + Returns: + A list of `STBox` instances. + + MEOS Functions: + stbox_space_time_tiles + """ + dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else interval_in(duration, -1) + gs = geo_to_gserialized(origin, self.geodetic()) + st = datetime_to_timestamptz(start) if isinstance(start, datetime) else timestamptz_in(start, -1) + tiles, count = stbox_space_time_tiles(self._inner, xsize, ysize, zsize, dt, gs, st, border_inc) + return [STBox(_inner=tiles + i) for i in range(count)] + + def time_tiles( + self, + duration: timedelta | str, + start: datetime | str, + border_inc: bool, + ) -> list[STBox]: + """ + Returns the temporal tiles of ``self``. + + The arguments are those of the MEOS function, marshalled as + #TPoint.space_time_split marshals the duration and the start. + + Args: + duration: The duration of the tiles. + start: The origin of the temporal tiling. + border_inc: Whether a tile contains its upper border. + + Returns: + A list of `STBox` instances. + + MEOS Functions: + stbox_time_tiles + """ + dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else interval_in(duration, -1) + st = datetime_to_timestamptz(start) if isinstance(start, datetime) else timestamptz_in(start, -1) + tiles, count = stbox_time_tiles(self._inner, dt, st, border_inc) return [STBox(_inner=tiles + i) for i in range(count)] # ------------------------- Comparisons ----------------------------------- diff --git a/pymeos/boxes/tbox.py b/pymeos/boxes/tbox.py index 336ecca1..bc4b2967 100644 --- a/pymeos/boxes/tbox.py +++ b/pymeos/boxes/tbox.py @@ -1086,16 +1086,16 @@ def tile( MEOS Functions: tintbox_tile_list, tfloabox_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 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)) + else (timestamptz_in(start, -1) if isinstance(start, str) else timestamptz_in("2000-01-03", -1)) ) if self._is_float(): - tiles, count = tfloatbox_value_time_tiles(self._inner, size, dt, origin, st) + tiles, count = tfloatbox_value_time_tiles(self._inner, size, dt, origin, st, border_inc=True) else: - tiles, count = tintbox_value_time_tiles(self._inner, int(size), dt, int(origin), st) + tiles, count = tintbox_value_time_tiles(self._inner, int(size), dt, int(origin), st, border_inc=True) return [TBox(_inner=tiles + c) for c in range(count)] # ------------------------- Comparisons ----------------------------------- diff --git a/pymeos/collections/base/spanset.py b/pymeos/collections/base/spanset.py index 232fb5f3..9b63c0d0 100644 --- a/pymeos/collections/base/spanset.py +++ b/pymeos/collections/base/spanset.py @@ -211,7 +211,7 @@ def spans(self) -> list[Span]: MEOS Functions: spanset_spans """ - return spanset_spanarr(self._inner) + return spanset_spanarr(self._inner)[0] def __hash__(self) -> int: """ diff --git a/pymeos/collections/geo/geoset.py b/pymeos/collections/geo/geoset.py index 5b794d0a..fd3efde4 100644 --- a/pymeos/collections/geo/geoset.py +++ b/pymeos/collections/geo/geoset.py @@ -161,7 +161,7 @@ def elements(self) -> list[shp.Geometry]: MEOS Functions: geoset_values """ - elems = geoset_values(self._inner) + elems = geoset_values(self._inner)[0] return [gserialized_to_shapely_geometry(elems[i]) for i in range(self.num_elements())] def srid(self) -> int: diff --git a/pymeos/collections/number/floatset.py b/pymeos/collections/number/floatset.py index f1332d30..23a77774 100644 --- a/pymeos/collections/number/floatset.py +++ b/pymeos/collections/number/floatset.py @@ -141,7 +141,7 @@ def elements(self) -> list[float]: MEOS Functions: floattset_values """ - elems = floatset_values(self._inner) + elems = floatset_values(self._inner)[0] return [elems[i] for i in range(self.num_elements())] # ------------------------- Transformations ------------------------------------ diff --git a/pymeos/collections/number/intset.py b/pymeos/collections/number/intset.py index a512c7d5..299ad444 100644 --- a/pymeos/collections/number/intset.py +++ b/pymeos/collections/number/intset.py @@ -140,7 +140,7 @@ def elements(self) -> list[int]: MEOS Functions: inttset_values """ - elems = intset_values(self._inner) + elems = intset_values(self._inner)[0] return [elems[i] for i in range(self.num_elements())] # ------------------------- Transformations ------------------------------------ diff --git a/pymeos/collections/text/textset.py b/pymeos/collections/text/textset.py index 0a1caddf..28eb0e8b 100644 --- a/pymeos/collections/text/textset.py +++ b/pymeos/collections/text/textset.py @@ -95,7 +95,7 @@ def element_n(self, n: int): textset_value_n """ super().element_n(n) - return text2cstring(textset_value_n(self._inner, n + 1)[0]) + return text_to_cstring(textset_value_n(self._inner, n + 1)[0]) def elements(self): """ @@ -107,8 +107,8 @@ def elements(self): MEOS Functions: textset_values """ - elems = textset_values(self._inner) - return [text2cstring(elems[i]) for i in range(self.num_elements())] + elems = textset_values(self._inner)[0] + return [text_to_cstring(elems[i]) for i in range(self.num_elements())] # ------------------------- Topological Operations -------------------------------- @@ -224,7 +224,7 @@ def subtract_from(self, other: str) -> str | None: :meth:`minus` """ result = minus_text_set(other, self._inner) - return text2cstring(result[0]) if result is not None else None + return text_to_cstring(result[0]) if result is not None else None def union(self, other: TextSet | str) -> TextSet: """ diff --git a/pymeos/collections/time/dateset.py b/pymeos/collections/time/dateset.py index 0675eeb2..29e64672 100644 --- a/pymeos/collections/time/dateset.py +++ b/pymeos/collections/time/dateset.py @@ -8,6 +8,7 @@ before_set_date, contains_set_date, date_adt_to_date, + date_in, date_to_date_adt, dateset_end_value, dateset_in, @@ -26,7 +27,6 @@ minus_set_set, overafter_set_date, overbefore_set_date, - pg_date_in, union_set_date, union_set_set, ) @@ -63,7 +63,7 @@ class DateSet(Set[date], TimeCollection[date]): _mobilitydb_name = "dateset" _parse_function = dateset_in - _parse_value_function = lambda x: pg_date_in(x) if isinstance(x, str) else date_to_date_adt(x) + _parse_value_function = lambda x: date_in(x) if isinstance(x, str) else date_to_date_adt(x) _make_function = dateset_make # ------------------------- Constructors ---------------------------------- @@ -162,7 +162,7 @@ def elements(self) -> list[date]: MEOS Functions: dateset_values """ - tss = dateset_values(self._inner) + tss = dateset_values(self._inner)[0] return [date_adt_to_date(tss[i]) for i in range(self.num_elements())] # ------------------------- Transformations ------------------------------- diff --git a/pymeos/collections/time/datespan.py b/pymeos/collections/time/datespan.py index 934e2529..45f24c3b 100644 --- a/pymeos/collections/time/datespan.py +++ b/pymeos/collections/time/datespan.py @@ -9,6 +9,7 @@ before_span_date, contains_span_date, date_adt_to_date, + date_in, date_to_date_adt, datespan_duration, datespan_in, @@ -30,7 +31,6 @@ minus_span_spanset, overafter_span_date, overbefore_span_date, - pg_date_in, union_span_date, ) @@ -69,7 +69,7 @@ class DateSpan(Span[date], TimeCollection[date]): _mobilitydb_name = "datespan" _parse_function = datespan_in - _parse_value_function = lambda x: pg_date_in(x) if isinstance(x, str) else date_to_date_adt(x) + _parse_value_function = lambda x: date_in(x) if isinstance(x, str) else date_to_date_adt(x) _make_function = datespan_make # ------------------------- Constructors ---------------------------------- diff --git a/pymeos/collections/time/tstzset.py b/pymeos/collections/time/tstzset.py index 545b885e..08d0fa15 100644 --- a/pymeos/collections/time/tstzset.py +++ b/pymeos/collections/time/tstzset.py @@ -39,7 +39,7 @@ class TsTzSet(Set[datetime], TimeCollection[datetime]): _mobilitydb_name = "tstzset" _parse_function = tstzset_in - _parse_value_function = lambda x: pg_timestamptz_in(x, -1) if isinstance(x, str) else datetime_to_timestamptz(x) + _parse_value_function = lambda x: timestamptz_in(x, -1) if isinstance(x, str) else datetime_to_timestamptz(x) _make_function = tstzset_make # ------------------------- Constructors ---------------------------------- @@ -116,7 +116,7 @@ def elements(self) -> list[datetime]: MEOS Functions: set_timestamptzs """ - tss = tstzset_values(self._inner) + tss = tstzset_values(self._inner)[0] return [timestamptz_to_datetime(tss[i]) for i in range(self.num_elements())] # ------------------------- Transformations ------------------------------- @@ -512,9 +512,9 @@ def distance(self, other: Time | Temporal | Box) -> timedelta: from .tstzspanset import TsTzSpanSet if isinstance(other, datetime): - return timedelta(seconds=distance_set_timestamptz(self._inner, datetime_to_timestamptz(other))) + return interval_to_timedelta(distance_set_timestamptz(self._inner, datetime_to_timestamptz(other))) elif isinstance(other, TsTzSet): - return timedelta(seconds=distance_tstzset_tstzset(self._inner, other._inner)) + return interval_to_timedelta(distance_tstzset_tstzset(self._inner, other._inner)) elif isinstance(other, (TsTzSpan, TsTzSpanSet)): return self.to_spanset().distance(other) elif isinstance(other, (Temporal, get_args(Box))): diff --git a/pymeos/collections/time/tstzspan.py b/pymeos/collections/time/tstzspan.py index 966e5c98..62c8b5d9 100644 --- a/pymeos/collections/time/tstzspan.py +++ b/pymeos/collections/time/tstzspan.py @@ -41,7 +41,7 @@ class TsTzSpan(Span[datetime], TimeCollection[datetime]): _mobilitydb_name = "tstzspan" _parse_function = tstzspan_in - _parse_value_function = lambda x: pg_timestamptz_in(x, -1) if isinstance(x, str) else datetime_to_timestamptz(x) + _parse_value_function = lambda x: timestamptz_in(x, -1) if isinstance(x, str) else datetime_to_timestamptz(x) _make_function = tstzspan_make # ------------------------- Constructors ---------------------------------- @@ -505,13 +505,13 @@ def distance(self, other: Time | Box | Temporal) -> timedelta: from .tstzspanset import TsTzSpanSet if isinstance(other, datetime): - return timedelta(seconds=distance_span_timestamptz(self._inner, datetime_to_timestamptz(other))) + return interval_to_timedelta(distance_span_timestamptz(self._inner, datetime_to_timestamptz(other))) elif isinstance(other, TsTzSet): return self.distance(other.to_spanset()) elif isinstance(other, TsTzSpan): - return timedelta(seconds=distance_tstzspan_tstzspan(self._inner, other._inner)) + return interval_to_timedelta(distance_tstzspan_tstzspan(self._inner, other._inner)) elif isinstance(other, TsTzSpanSet): - return timedelta(seconds=distance_tstzspanset_tstzspan(other._inner, self._inner)) + return interval_to_timedelta(distance_tstzspanset_tstzspan(other._inner, self._inner)) elif isinstance(other, Temporal): return self.distance(other.tstzspan()) elif isinstance(other, get_args(Box)): diff --git a/pymeos/collections/time/tstzspanset.py b/pymeos/collections/time/tstzspanset.py index 9e5f10fb..0b824080 100644 --- a/pymeos/collections/time/tstzspanset.py +++ b/pymeos/collections/time/tstzspanset.py @@ -631,13 +631,13 @@ def distance(self, other: Time | Box | Temporal) -> timedelta: from .tstzspan import TsTzSpan if isinstance(other, datetime): - return timedelta(seconds=distance_spanset_timestamptz(self._inner, datetime_to_timestamptz(other))) + return interval_to_timedelta(distance_spanset_timestamptz(self._inner, datetime_to_timestamptz(other))) elif isinstance(other, TsTzSet): return self.distance(other.to_spanset()) elif isinstance(other, TsTzSpan): - return timedelta(seconds=distance_tstzspanset_tstzspan(self._inner, other._inner)) + return interval_to_timedelta(distance_tstzspanset_tstzspan(self._inner, other._inner)) elif isinstance(other, TsTzSpanSet): - return timedelta(seconds=distance_tstzspanset_tstzspanset(self._inner, other._inner)) + return interval_to_timedelta(distance_tstzspanset_tstzspanset(self._inner, other._inner)) if isinstance(other, Temporal): return self.distance(other.tstzspan()) elif isinstance(other, get_args(Box)): diff --git a/pymeos/main/tfloat.py b/pymeos/main/tfloat.py index 242b3513..acf659da 100644 --- a/pymeos/main/tfloat.py +++ b/pymeos/main/tfloat.py @@ -896,7 +896,7 @@ def value_split(self, size: float, start: float | None = 0) -> list[Temporal]: MEOS Functions: tfloat_value_split """ - fragments, _, count = tfloat_value_split(self._inner, size, start) + fragments, _, count = tfloat_value_split(self._inner, size, start, border_inc=True) return [Temporal._factory(fragments[i]) for i in range(count)] def value_time_split( @@ -924,15 +924,15 @@ def value_time_split( tfloat_value_time_split """ if time_start is None: - st = pg_timestamptz_in("2000-01-03", -1) + st = 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) + else timestamptz_in(time_start, -1) ) - dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else pg_interval_in(duration, -1) - fragments, _, _, count = tfloat_value_time_split(self._inner, value_size, dt, value_start, st) + dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else interval_in(duration, -1) + fragments, _, _, count = tfloat_value_time_split(self._inner, value_size, dt, value_start, st, border_inc=True) return [Temporal._factory(fragments[i]) for i in range(count)] # ------------------------- Database Operations --------------------------- diff --git a/pymeos/main/tint.py b/pymeos/main/tint.py index 658e457e..7d218c3b 100644 --- a/pymeos/main/tint.py +++ b/pymeos/main/tint.py @@ -833,7 +833,7 @@ def value_split(self, size: int, start: int | None = 0) -> list[TInt]: MEOS Functions: tint_value_split """ - fragments, values, count = tint_value_split(self._inner, size, start) + fragments, values, count = tint_value_split(self._inner, size, start, border_inc=True) return [Temporal._factory(fragments[i]) for i in range(count)] def value_time_split( @@ -861,15 +861,15 @@ def value_time_split( tint_value_time_split """ if time_start is None: - st = pg_timestamptz_in("2000-01-03", -1) + st = 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) + else timestamptz_in(time_start, -1) ) - dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else pg_interval_in(duration, -1) - tiles, _, _, count = tint_value_time_split(self._inner, value_size, dt, value_start, st) + dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else interval_in(duration, -1) + tiles, _, _, count = tint_value_time_split(self._inner, value_size, dt, value_start, st, border_inc=True) return [Temporal._factory(tiles[i]) for i in range(count)] # ------------------------- Database Operations --------------------------- diff --git a/pymeos/main/tnumber.py b/pymeos/main/tnumber.py index c8e33e42..854db1c9 100644 --- a/pymeos/main/tnumber.py +++ b/pymeos/main/tnumber.py @@ -401,17 +401,17 @@ def mul(self, other: int | float | TNumber) -> TNumber: A new temporal object of the same subtype as `self`. MEOS Functions: - mult_tint_int, mult_tfloat_float, mult_tnumber_tnumber + mul_tint_int, mul_tfloat_float, mul_tnumber_tnumber """ from .tfloat import TFloat from .tint import TInt if isinstance(self, TInt) and isinstance(other, int): - result = mult_tint_int(self._inner, other) + result = mul_tint_int(self._inner, other) elif isinstance(self, TFloat) and isinstance(other, (int, float)): - result = mult_tfloat_float(self._inner, float(other)) + result = mul_tfloat_float(self._inner, float(other)) elif isinstance(other, TNumber): - result = mult_tnumber_tnumber(self._inner, other._inner) + result = mul_tnumber_tnumber(self._inner, other._inner) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) @@ -428,15 +428,15 @@ def rmul(self, other: int | float) -> TNumber: A new temporal object of the same subtype as `self`. MEOS Functions: - mult_int_tint, mult_float_tfloat + mul_int_tint, mul_float_tfloat """ from .tfloat import TFloat from .tint import TInt if isinstance(self, TInt) and isinstance(other, int): - result = mult_int_tint(other, self._inner) + result = mul_int_tint(other, self._inner) elif isinstance(self, TFloat) and isinstance(other, (int, float)): - result = mult_float_tfloat(float(other), self._inner) + result = mul_float_tfloat(float(other), self._inner) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) @@ -566,7 +566,7 @@ def __mul__(self, other): A new temporal object of the same subtype as `self`. MEOS Functions: - mult_tint_int, mult_tfloat_float, mult_tnumber_tnumber + mul_tint_int, mul_tfloat_float, mul_tnumber_tnumber """ return self.mul(other) @@ -582,7 +582,7 @@ def __rmul__(self, other): A new temporal object of the same subtype as `self`. MEOS Functions: - mult_int_tint, mult_float_tfloat + mul_int_tint, mul_float_tfloat """ return self.rmul(other) @@ -660,14 +660,17 @@ def distance(self, other: int | float | TNumber) -> TFloat: A :class:`TFloat` with the distance between `self` and `other`. MEOS Functions: - distance_tfloat_float, distance_tnumber_tnumber + tdistance_tint_int, tdistance_tfloat_float, tdistance_tnumber_tnumber """ - if isinstance(other, int): - result = distance_tfloat_float(self._inner, float(other)) - elif isinstance(other, float): - result = distance_tfloat_float(self._inner, other) + from .tfloat import TFloat + from .tint import TInt + + if isinstance(self, TInt) and isinstance(other, int): + result = tdistance_tint_int(self._inner, other) + elif isinstance(self, TFloat) and isinstance(other, (int, float)): + result = tdistance_tfloat_float(self._inner, float(other)) elif isinstance(other, TNumber): - result = distance_tnumber_tnumber(self._inner, other._inner) + result = tdistance_tnumber_tnumber(self._inner, other._inner) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) diff --git a/pymeos/main/tpoint.py b/pymeos/main/tpoint.py index 9ae1441b..fb5e2ffe 100644 --- a/pymeos/main/tpoint.py +++ b/pymeos/main/tpoint.py @@ -235,7 +235,7 @@ def length(self) -> float: MEOS Functions: tpoint_length """ - return tpoint_length(self._inner) + return tpoint_length(self._inner, spheroid=True) def cumulative_length(self) -> TFloat: """ @@ -247,7 +247,7 @@ def cumulative_length(self) -> TFloat: MEOS Functions: tpoint_cumulative_length """ - result = tpoint_cumulative_length(self._inner) + result = tpoint_cumulative_length(self._inner, spheroid=True) return Temporal._factory(result) def speed(self) -> TFloat: @@ -260,7 +260,7 @@ def speed(self) -> TFloat: MEOS Functions: tpoint_speed """ - result = tpoint_speed(self._inner) + result = tpoint_speed(self._inner, spheroid=True) return Temporal._factory(result) def x(self) -> TF: @@ -787,11 +787,11 @@ def is_ever_within_distance(self, other: shpb.BaseGeometry | TPoint | STBox, dis if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = edwithin_tgeo_geo(self._inner, gs, distance) + result = edwithin_tgeo_geo(self._inner, gs, distance, spheroid=True) elif isinstance(other, STBox): - result = edwithin_tgeo_geo(self._inner, stbox_to_geo(other._inner), distance) + result = edwithin_tgeo_geo(self._inner, stbox_to_geo(other._inner), distance, spheroid=True) elif isinstance(other, TPoint): - result = edwithin_tgeo_tgeo(self._inner, other._inner, distance) + result = edwithin_tgeo_tgeo(self._inner, other._inner, distance, spheroid=True) else: raise TypeError(f"Operation not supported with type {other.__class__}") return result == 1 @@ -866,10 +866,10 @@ def is_spatially_contained_in(self, container: shpb.BaseGeometry | STBox) -> TBo if isinstance(container, shpb.BaseGeometry): gs = geo_to_gserialized(container, isinstance(self, TGeogPoint)) - result = tcontains_geo_tgeo(gs, self._inner, False, False) + result = tcontains_geo_tgeo(gs, self._inner) elif isinstance(container, STBox): gs = stbox_to_geo(container._inner) - result = tcontains_geo_tgeo(gs, self._inner, False, False) + result = tcontains_geo_tgeo(gs, self._inner) else: raise TypeError(f"Operation not supported with type {container.__class__}") return Temporal._factory(result) @@ -892,11 +892,11 @@ def distance(self, other: shpb.BaseGeometry | TPoint | STBox) -> TFloat: if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tdistance_tgeo_geo(self._inner, gs) + result = tdistance_tgeo_geo(self._inner, gs, spheroid=True) elif isinstance(other, STBox): - result = tdistance_tgeo_geo(self._inner, stbox_to_geo(other._inner)) + result = tdistance_tgeo_geo(self._inner, stbox_to_geo(other._inner), spheroid=True) elif isinstance(other, TPoint): - result = tdistance_tgeo_tgeo(self._inner, other._inner) + result = tdistance_tgeo_tgeo(self._inner, other._inner, spheroid=True) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) @@ -918,11 +918,11 @@ def nearest_approach_distance(self, other: shpb.BaseGeometry | STBox | TPoint) - if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - return nad_tgeo_geo(self._inner, gs) + return nad_tgeo_geo(self._inner, gs, spheroid=True) elif isinstance(other, STBox): - return nad_tgeo_stbox(self._inner, other._inner) + return nad_tgeo_stbox(self._inner, other._inner, spheroid=True) elif isinstance(other, TPoint): - return nad_tgeo_tgeo(self._inner, other._inner) + return nad_tgeo_tgeo(self._inner, other._inner, spheroid=True) else: raise TypeError(f"Operation not supported with type {other.__class__}") @@ -941,9 +941,9 @@ def nearest_approach_instant(self, other: shpb.BaseGeometry | TPoint) -> TI: """ if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = nai_tgeo_geo(self._inner, gs) + result = nai_tgeo_geo(self._inner, gs, spheroid=True) elif isinstance(other, TPoint): - result = nai_tgeo_tgeo(self._inner, other._inner) + result = nai_tgeo_tgeo(self._inner, other._inner, spheroid=True) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) @@ -964,54 +964,13 @@ def shortest_line(self, other: shpb.BaseGeometry | TPoint) -> shpb.BaseGeometry: """ if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = shortestline_tgeo_geo(self._inner, gs) + result = shortestline_tgeo_geo(self._inner, gs, spheroid=True) elif isinstance(other, TPoint): - result = shortestline_tgeo_tgeo(self._inner, other._inner) + result = shortestline_tgeo_tgeo(self._inner, other._inner, spheroid=True) else: raise TypeError(f"Operation not supported with type {other.__class__}") return gserialized_to_shapely_geometry(result, 10) - # ------------------------- Tiling Operations ----------------------------- - def tile( - self, - size: float, - duration: timedelta | str | None = None, - origin: shpb.BaseGeometry | None = None, - start: datetime | str | None = None, - remove_empty: bool | None = False, - ) -> list[TG]: - """ - 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 used by default is Monday, January 3, 2000. - remove_empty: If True, remove the tiles that are empty. - - Returns: - A list of :class:`TPoint` objects. - - See Also: - :meth:`STBox.tile` - """ - from ..boxes import STBox - - bbox = STBox.from_tpoint(self) - tiles = bbox.tile(size, duration, origin, start) - if remove_empty: - return [x for x in (self.at(tile) for tile in tiles) if x] - else: - return [self.at(tile) for tile in tiles] - # ------------------------- Split Operations ------------------------------ def space_split( self, @@ -1086,19 +1045,19 @@ def space_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) + dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else interval_in(duration, -1) gs = ( geo_to_gserialized(origin, isinstance(self, TGeogPoint)) if origin is not None else (geog_in("Point(0 0 0)", -1) if isinstance(self, TGeogPoint) else geom_in("Point(0 0 0)", -1)) ) if time_start is None: - st = pg_timestamptz_in("2000-01-03", -1) + st = 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) + else timestamptz_in(time_start, -1) ) fragments, points, times, count = tgeo_space_time_split( self._inner, xsize, ysz, zsz, dt, gs, st, bitmatrix, include_border @@ -1365,11 +1324,11 @@ def disjoint(self, other: shpb.BaseGeometry | STBox | TPoint) -> TBool: if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tdisjoint_tgeo_geo(self._inner, gs, False, False) + result = tdisjoint_tgeo_geo(self._inner, gs) elif isinstance(other, STBox): - result = tdisjoint_tgeo_geo(self._inner, stbox_to_geo(other._inner), False, False) + result = tdisjoint_tgeo_geo(self._inner, stbox_to_geo(other._inner)) elif isinstance(other, TPoint): - result = tdisjoint_tgeo_tgeo(self._inner, other._inner, False, False) + result = tdisjoint_tgeo_tgeo(self._inner, other._inner) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) @@ -1391,11 +1350,11 @@ def intersects(self, other: shpb.BaseGeometry | STBox | TPoint) -> TBool: if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tintersects_tgeo_geo(self._inner, gs, False, False) + result = tintersects_tgeo_geo(self._inner, gs) elif isinstance(other, STBox): - result = tintersects_tgeo_geo(self._inner, stbox_to_geo(other._inner), False, False) + result = tintersects_tgeo_geo(self._inner, stbox_to_geo(other._inner)) elif isinstance(other, TPoint): - result = tintersects_tgeo_tgeo(self._inner, other._inner, False, False) + result = tintersects_tgeo_tgeo(self._inner, other._inner) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) @@ -1417,11 +1376,11 @@ def touches(self, other: shpb.BaseGeometry | STBox | TGeomPoint) -> TBool: if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = ttouches_tgeo_geo(self._inner, gs, False, False) + result = ttouches_tgeo_geo(self._inner, gs) elif isinstance(other, STBox): - result = ttouches_tgeo_geo(self._inner, stbox_to_geo(other._inner), False, False) + result = ttouches_tgeo_geo(self._inner, stbox_to_geo(other._inner)) elif isinstance(other, TGeomPoint): - result = ttouches_tgeo_tgeo(self._inner, other._inner, False, False) + result = ttouches_tgeo_tgeo(self._inner, other._inner) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) @@ -1444,11 +1403,11 @@ def within_distance(self, other: shpb.BaseGeometry | TGeomPoint | STBox, distanc if isinstance(other, shpb.BaseGeometry): gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tdwithin_tgeo_geo(self._inner, gs, distance, False, False) + result = tdwithin_tgeo_geo(self._inner, gs, distance, spheroid=True) elif isinstance(other, STBox): - result = tdwithin_tgeo_geo(self._inner, stbox_to_geo(other._inner), distance, False, False) + result = tdwithin_tgeo_geo(self._inner, stbox_to_geo(other._inner), distance, spheroid=True) elif isinstance(other, TGeomPoint): - result = tdwithin_tgeo_tgeo(self._inner, other._inner, distance, False, False) + result = tdwithin_tgeo_tgeo(self._inner, other._inner, distance, spheroid=True) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) diff --git a/pymeos/main/ttext.py b/pymeos/main/ttext.py index e956e732..2dfd521b 100644 --- a/pymeos/main/ttext.py +++ b/pymeos/main/ttext.py @@ -143,7 +143,7 @@ def value_set(self) -> set[str]: ttext_values """ values, count = ttext_values(self._inner) - return {text2cstring(values[i]) for i in range(count)} + return {text_to_cstring(values[i]) for i in range(count)} def min_value(self) -> str: """ @@ -233,7 +233,7 @@ def value_at_timestamp(self, timestamp: datetime) -> str: ttext_value_at_timestamp """ result = ttext_value_at_timestamptz(self._inner, datetime_to_timestamptz(timestamp), True) - return text2cstring(result[0]) + return text_to_cstring(result[0]) # ------------------------- Ever and Always Comparisons ------------------- def always_less(self, value: str | TText) -> bool: diff --git a/pymeos/temporal/temporal.py b/pymeos/temporal/temporal.py index b168ae66..9099df25 100644 --- a/pymeos/temporal/temporal.py +++ b/pymeos/temporal/temporal.py @@ -167,7 +167,7 @@ def from_merge(cls: type[Self], *temporals: TG) -> Self: temporal_merge_array """ _temporals = [temp._inner for temp in temporals if temp is not None] - result = temporal_merge_array(_temporals, len(_temporals)) + result = temporal_merge_array(_temporals) return Temporal._factory(result) @classmethod @@ -184,7 +184,7 @@ def from_merge_array(cls: type[Self], temporals: list[TG]) -> Self: objects. """ _temporals = [temp._inner for temp in temporals if temp is not None] - result = temporal_merge_array(_temporals, len(_temporals)) + result = temporal_merge_array(_temporals) return Temporal._factory(result) # ------------------------- Output ---------------------------------------- @@ -592,15 +592,15 @@ def temporal_sample( temporal_tsample """ if start is None: - st = pg_timestamptz_in("2000-01-03", -1) + st = timestamptz_in("2000-01-03", -1) elif isinstance(start, datetime): st = datetime_to_timestamptz(start) else: - st = pg_timestamptz_in(start, -1) + st = timestamptz_in(start, -1) if isinstance(duration, timedelta): dt = timedelta_to_interval(duration) else: - dt = pg_interval_in(duration, -1) + dt = interval_in(duration, -1) if interpolation is None: intrp = self.interpolation() else: @@ -627,15 +627,15 @@ def temporal_precision( temporal_tprecision """ if start is None: - st = pg_timestamptz_in("2000-01-03", -1) + st = timestamptz_in("2000-01-03", -1) elif isinstance(start, datetime): st = datetime_to_timestamptz(start) else: - st = pg_timestamptz_in(start, -1) + st = timestamptz_in(start, -1) if isinstance(duration, timedelta): dt = timedelta_to_interval(duration) else: - dt = pg_interval_in(duration, -1) + dt = interval_in(duration, -1) result = temporal_tprecision(self._inner, dt, st) return Temporal._factory(result) @@ -644,9 +644,9 @@ def to_instant(self) -> TI: Returns `self` as a :class:`TInstant`. MEOS Functions: - temporal_to_tinstant + temporal_as_tinstant """ - inst = temporal_to_tinstant(self._inner) + inst = temporal_as_tinstant(self._inner) return Temporal._factory(inst) def to_sequence(self, interpolation: TInterpolation) -> TS: @@ -656,7 +656,7 @@ def to_sequence(self, interpolation: TInterpolation) -> TS: MEOS Functions: temporal_to_sequence """ - seq = temporal_to_tsequence(self._inner, interpolation) + seq = temporal_as_tsequence(self._inner, interpolation) return Temporal._factory(seq) def to_sequenceset(self, interpolation: TInterpolation) -> TSS: @@ -664,9 +664,9 @@ def to_sequenceset(self, interpolation: TInterpolation) -> TSS: Returns `self` as a :class:`TSequenceSet`. MEOS Functions: - temporal_to_tsequenceset + temporal_as_tsequenceset """ - ss = temporal_to_tsequenceset(self._inner, interpolation) + ss = temporal_as_tsequenceset(self._inner, interpolation) return Temporal._factory(ss) def to_dataframe(self) -> pd.DataFrame: @@ -737,7 +737,7 @@ def merge(self, other: type(None) | Temporal[TBase] | list[Temporal[TBase]]) -> elif 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)]) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(new_temp) @@ -1160,7 +1160,7 @@ def frechet_distance(self, other: Temporal) -> float: MEOS Functions: temporal_frechet_distance """ - return temporal_frechet_distance(self._inner, other._inner) + return temporal_frechet_distance(self._inner, other._inner, spheroid=True) def dyntimewarp_distance(self, other: Temporal) -> float: """ @@ -1175,7 +1175,7 @@ def dyntimewarp_distance(self, other: Temporal) -> float: MEOS Functions: temporal_dyntimewarp_distance """ - return temporal_dyntimewarp_distance(self._inner, other._inner) + return temporal_dyntimewarp_distance(self._inner, other._inner, spheroid=True) def hausdorff_distance(self, other: Temporal) -> float: """ @@ -1190,7 +1190,7 @@ def hausdorff_distance(self, other: Temporal) -> float: MEOS Functions: temporal_hausdorff_distance """ - return temporal_hausdorff_distance(self._inner, other._inner) + return temporal_hausdorff_distance(self._inner, other._inner, spheroid=True) # ------------------------- Split Operations ------------------------------ def time_split( @@ -1217,11 +1217,11 @@ def time_split( temporal_time_split """ if start is None: - st = pg_timestamptz_in("2000-01-03", -1) + st = timestamptz_in("2000-01-03", -1) 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) - fragments, times, count = temporal_time_split(self._inner, dt, st) + st = datetime_to_timestamptz(start) if isinstance(start, datetime) else timestamptz_in(start, -1) + dt = timedelta_to_interval(duration) if isinstance(duration, timedelta) else interval_in(duration, -1) + fragments, times, count = temporal_time_split(self._inner, dt, st, border_inc=True) from ..factory import _TemporalFactory return [_TemporalFactory.create_temporal(fragments[i]) for i in range(count)] @@ -1230,7 +1230,8 @@ 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. + duration. When `self` includes its end timestamp, that timestamp + starts an (n+1)th tile, which holds only the end value. Args: n: An :class:`int` with the number of temporal tiles. @@ -1245,7 +1246,7 @@ def time_split_n(self, n: int) -> list[TG]: return [self] st = temporal_start_timestamptz(self._inner) dt = timedelta_to_interval((self.end_timestamp() - self.start_timestamp()) / n) - fragments, times, count = temporal_time_split(self._inner, dt, st) + fragments, times, count = temporal_time_split(self._inner, dt, st, border_inc=True) from ..factory import _TemporalFactory return [_TemporalFactory.create_temporal(fragments[i]) for i in range(count)] diff --git a/pymeos/temporal/tinstant.py b/pymeos/temporal/tinstant.py index d79ffbce..5bd2f2d6 100644 --- a/pymeos/temporal/tinstant.py +++ b/pymeos/temporal/tinstant.py @@ -44,9 +44,7 @@ def __init__( self._inner = as_tinstant(self.__class__._parse_function(string)) else: ts = ( - datetime_to_timestamptz(timestamp) - if isinstance(timestamp, datetime) - else pg_timestamptz_in(timestamp, -1) + datetime_to_timestamptz(timestamp) if isinstance(timestamp, datetime) else timestamptz_in(timestamp, -1) ) self._inner = self.__class__._make_function(self.__class__._cast_function(value), ts) diff --git a/pymeos/temporal/tsequence.py b/pymeos/temporal/tsequence.py index 77587217..457a367a 100644 --- a/pymeos/temporal/tsequence.py +++ b/pymeos/temporal/tsequence.py @@ -46,10 +46,8 @@ def __init__( (x._inner if isinstance(x, self.ComponentClass) else self.__class__._parse_function(x)) for x in instant_list ] - count = len(self._instants) self._inner = tsequence_make( self._instants, - count, lower_inc, upper_inc, interpolation.value, diff --git a/pymeos/temporal/tsequenceset.py b/pymeos/temporal/tsequenceset.py index 0e14de63..97a69acf 100644 --- a/pymeos/temporal/tsequenceset.py +++ b/pymeos/temporal/tsequenceset.py @@ -46,8 +46,7 @@ def __init__( (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) + self._inner = tsequenceset_make(sequences, normalize) @classmethod def from_sequences( diff --git a/pyproject.toml b/pyproject.toml index 50f1774e..90a70b86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ license = { file = 'LICENSE' } requires-python = '>=3.10' dependencies = [ - 'pymeos-cffi >=1.3.0, <2', + 'pymeos-cffi >=1.4.0a1, <2', 'python-dateutil', 'shapely>=2.0.0', ] diff --git a/tests/aggregators/__init__.py b/tests/aggregators/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/aggregators/aggregators_test.py b/tests/aggregators/aggregators_test.py new file mode 100644 index 00000000..51741a08 --- /dev/null +++ b/tests/aggregators/aggregators_test.py @@ -0,0 +1,178 @@ +import pytest + +from pymeos import ( + STBox, + TBoolSeq, + TBoolSeqSet, + TBox, + TFloatSeq, + TFloatSeqSet, + TGeomPointSeq, + TIntSeq, + TIntSeqSet, + TsTzSet, + TsTzSpan, + TsTzSpanSet, + TTextSeq, + TTextSeqSet, +) +from pymeos.aggregators import ( + TemporalAndAggregator, + TemporalAverageAggregator, + TemporalExtentAggregator, + TemporalFloatMaxAggregator, + TemporalFloatMinAggregator, + TemporalFloatSumAggregator, + TemporalInstantCountAggregator, + TemporalIntMaxAggregator, + TemporalIntMinAggregator, + TemporalIntSumAggregator, + TemporalNumberExtentAggregator, + TemporalOrAggregator, + TemporalPeriodCountAggregator, + TemporalPointExtentAggregator, + TemporalTextMaxAggregator, + TemporalTextMinAggregator, + TimeContinuousUnionAggregator, + TimeInstantaneousUnionAggregator, +) +from tests.conftest import TestPyMEOS + + +class TestAggregators(TestPyMEOS): + """Every aggregator on two overlapping inputs, laid out as #TestTBoxConstructors is.""" + + ints = [TIntSeq("[1@2000-01-01, 3@2000-01-03]"), TIntSeq("[2@2000-01-02, 2@2000-01-04]")] + floats = [TFloatSeq("[1@2000-01-01, 3@2000-01-03]"), TFloatSeq("[2@2000-01-02, 2@2000-01-04]")] + bools = [TBoolSeq("[t@2000-01-01, f@2000-01-03]"), TBoolSeq("[t@2000-01-02, t@2000-01-04]")] + texts = [TTextSeq('["a"@2000-01-01, "c"@2000-01-03]'), TTextSeq('["b"@2000-01-02, "b"@2000-01-04]')] + points = [ + TGeomPointSeq("[POINT(0 0)@2000-01-01, POINT(2 2)@2000-01-03]"), + TGeomPointSeq("[POINT(1 3)@2000-01-02, POINT(1 3)@2000-01-04]"), + ] + spans = [TsTzSpan("[2000-01-01, 2000-01-03]"), TsTzSpan("[2000-01-02, 2000-01-04]")] + sets = [TsTzSet("{2000-01-01, 2000-01-03}"), TsTzSet("{2000-01-02, 2000-01-03}")] + + @pytest.mark.parametrize( + "aggregator, inputs, expected", + [ + ( + TemporalAndAggregator, + bools, + TBoolSeqSet("{[t@2000-01-01, f@2000-01-03], (t@2000-01-03, t@2000-01-04]}"), + ), + (TemporalOrAggregator, bools, TBoolSeqSet("{[t@2000-01-01, t@2000-01-04]}")), + ( + TemporalIntMaxAggregator, + ints, + TIntSeqSet("{[1@2000-01-01, 2@2000-01-02, 3@2000-01-03], (2@2000-01-03, 2@2000-01-04]}"), + ), + ( + TemporalIntMinAggregator, + ints, + TIntSeqSet("{[1@2000-01-01, 2@2000-01-03, 2@2000-01-04]}"), + ), + ( + TemporalIntSumAggregator, + ints, + TIntSeqSet("{[1@2000-01-01, 3@2000-01-02, 5@2000-01-03], (2@2000-01-03, 2@2000-01-04]}"), + ), + ( + TemporalFloatMaxAggregator, + floats, + TFloatSeqSet("{[1@2000-01-01, 3@2000-01-03], (2@2000-01-03, 2@2000-01-04]}"), + ), + ( + TemporalFloatMinAggregator, + floats, + TFloatSeqSet("{[1@2000-01-01, 2@2000-01-02, 2@2000-01-04]}"), + ), + ( + TemporalFloatSumAggregator, + floats, + TFloatSeqSet( + "{[1@2000-01-01, 2@2000-01-02), [4@2000-01-02, 5@2000-01-03], (2@2000-01-03, 2@2000-01-04]}" + ), + ), + ( + TemporalAverageAggregator, + floats, + TFloatSeqSet("{[1@2000-01-01, 2@2000-01-02, 2.5@2000-01-03], (2@2000-01-03, 2@2000-01-04]}"), + ), + ( + TemporalTextMaxAggregator, + texts, + TTextSeqSet('{["a"@2000-01-01, "b"@2000-01-02, "c"@2000-01-03], ("b"@2000-01-03, "b"@2000-01-04]}'), + ), + ( + TemporalTextMinAggregator, + texts, + TTextSeqSet('{["a"@2000-01-01, "b"@2000-01-03, "b"@2000-01-04]}'), + ), + ( + TemporalInstantCountAggregator, + sets, + TIntSeq("{1@2000-01-01, 1@2000-01-02, 2@2000-01-03}"), + ), + ( + TemporalPeriodCountAggregator, + spans, + TIntSeqSet("{[1@2000-01-01, 2@2000-01-02, 2@2000-01-03], (1@2000-01-03, 1@2000-01-04]}"), + ), + ( + TimeInstantaneousUnionAggregator, + sets, + TsTzSet("{2000-01-01, 2000-01-02, 2000-01-03}"), + ), + ( + TimeContinuousUnionAggregator, + spans, + TsTzSpanSet("{[2000-01-01, 2000-01-04]}"), + ), + ], + ids=[ + "And", + "Or", + "IntMax", + "IntMin", + "IntSum", + "FloatMax", + "FloatMin", + "FloatSum", + "Average", + "TextMax", + "TextMin", + "InstantCount", + "PeriodCount", + "InstantaneousUnion", + "ContinuousUnion", + ], + ) + def test_aggregate(self, aggregator, inputs, expected): + assert aggregator.aggregate(inputs) == expected + + @pytest.mark.parametrize( + "aggregator, inputs, expected", + [ + ( + TemporalNumberExtentAggregator, + floats, + TBox("TBOXFLOAT XT([1, 3],[2000-01-01, 2000-01-04])"), + ), + ( + TemporalPointExtentAggregator, + points, + STBox("STBOX XT(((0,0),(2,3)),[2000-01-01, 2000-01-04])"), + ), + (TemporalExtentAggregator, ints, TsTzSpan("[2000-01-01, 2000-01-04]")), + ], + ids=["NumberExtent", "PointExtent", "TimeExtent"], + ) + def test_extent(self, aggregator, inputs, expected): + assert aggregator.aggregate(inputs) == expected + + def test_incremental_aggregation(self): + aggregation = TemporalIntSumAggregator.start_aggregation() + for temporal in self.ints: + aggregation.add(temporal) + assert aggregation.aggregation() == TemporalIntSumAggregator.aggregate(self.ints) diff --git a/tests/boxes/stbox_test.py b/tests/boxes/stbox_test.py index bcc676d9..9e7d034d 100644 --- a/tests/boxes/stbox_test.py +++ b/tests/boxes/stbox_test.py @@ -1261,3 +1261,26 @@ def test_gt(self): def test_ge(self): _ = self.stbxt >= self.other + + +class TestSTBoxTiling(TestSTBox): + # Mirrors #test_time_split of the temporal split tests, on the space tiles + trip = TGeomPointSeq( + "SRID=3857;[Point(481000 6586000)@2020-06-01 08:00:00+00, Point(487000 6588000)@2020-06-01 08:30:00+00]" + ) + + # The space tiles of the box, as #test_time_split reads the time bins + def test_space_tiles(self): + tiles = STBox.from_tpoint(self.trip).space_tiles(5e3, 0, 0, Point(0, 0), True) + assert [str(tile) for tile in tiles] == [ + "SRID=3857;STBOX X((480000,6585000),(485000,6590000))", + "SRID=3857;STBOX X((485000,6585000),(490000,6590000))", + ] + + # The fragments of the trip in those tiles, as #test_time_split reads the time fragments + def test_tpoint_space_split(self): + pieces = self.trip.space_split(5e3) + assert [str(piece) for piece in pieces] == [ + "{[POINT(481000 6586000)@2020-06-01 08:00:00+00, POINT(485000 6587333.333333333)@2020-06-01 08:20:00+00)}", + "{[POINT(485000 6587333.333333333)@2020-06-01 08:20:00+00, POINT(487000 6588000)@2020-06-01 08:30:00+00]}", + ] diff --git a/tests/boxes/tbox_test.py b/tests/boxes/tbox_test.py index c25a86e2..1ec4edc9 100644 --- a/tests/boxes/tbox_test.py +++ b/tests/boxes/tbox_test.py @@ -1123,3 +1123,15 @@ def test_gt(self): def test_ge(self): _ = self.tbfxt >= self.other + + +class TestTBoxTiling(TestTBox): + # Mirrors #test_value_time_split of the temporal split tests, on the value-time tiles + def test_tile(self): + box = TBox("TBOXFLOAT XT([0, 33.35670867892772],[2020-06-01 08:00:15.533+02, 2020-06-06 01:53:54.08198+02])") + tiles = box.tile(10, "1 day", 0.0, "2020-06-01 00:00:00+02") + assert tiles == [ + TBox(f"TBOXFLOAT XT([{v}, {v + 10}),[2020-06-0{d} 00:00:00+02, 2020-06-0{d + 1} 00:00:00+02))") + for d in range(1, 7) + for v in (0, 10, 20, 30) + ] diff --git a/tests/main/tbool_test.py b/tests/main/tbool_test.py index 92400c62..46e95ac4 100644 --- a/tests/main/tbool_test.py +++ b/tests/main/tbool_test.py @@ -2,6 +2,7 @@ from datetime import datetime, timedelta, timezone import pytest +from pymeos_cffi import MeosInvalidArgTypeError from pymeos import ( TBool, @@ -345,8 +346,8 @@ def test_as_hexwkb(self, temporal, expected): "{\n" ' "type": "MovingBoolean",\n' ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-01T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-01T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -354,7 +355,7 @@ def test_as_hexwkb(self, temporal, expected): " true\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00"\n' " ],\n" ' "interpolation": "None"\n' "}", @@ -364,8 +365,8 @@ def test_as_hexwkb(self, temporal, expected): "{\n" ' "type": "MovingBoolean",\n' ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -374,8 +375,8 @@ def test_as_hexwkb(self, temporal, expected): " false\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -387,8 +388,8 @@ def test_as_hexwkb(self, temporal, expected): "{\n" ' "type": "MovingBoolean",\n' ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -397,8 +398,8 @@ def test_as_hexwkb(self, temporal, expected): " false\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -410,8 +411,8 @@ def test_as_hexwkb(self, temporal, expected): "{\n" ' "type": "MovingBoolean",\n' ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-05T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-05T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -422,8 +423,8 @@ def test_as_hexwkb(self, temporal, expected): " false\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -434,8 +435,8 @@ def test_as_hexwkb(self, temporal, expected): " true\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-03T00:00:00+00",\n' - ' "2019-09-05T00:00:00+00"\n' + ' "2019-09-03T00:00:00+00:00",\n' + ' "2019-09-05T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -822,7 +823,7 @@ def test_segments(self, temporal, expected): @pytest.mark.parametrize( "temporal, expected", - [(tbi, 440045287), (tbds, 2385901957), (tbs, 2385901957), (tbss, 1543175996)], + [(tbi, 440045318), (tbds, 3614979951), (tbs, 3614979951), (tbss, 1190925880)], ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], ) def test_hash(self, temporal, expected): @@ -1490,35 +1491,6 @@ def test_when_false(self, temporal, expected): def test_at(self, temporal, restrictor, expected): assert temporal.at(restrictor) == expected - @pytest.mark.parametrize( - "temporal, expected", - [ - (tbi, TBoolInst("True@2019-09-01")), - (tbds, TBoolSeq("{True@2019-09-01}")), - (tbs, TBoolSeq("[True@2019-09-01, True@2019-09-02)")), - ( - tbss, - TBoolSeqSet("{[True@2019-09-01, True@2019-09-02),[True@2019-09-03, True@2019-09-05]}"), - ), - ], - ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], - ) - def test_at_max(self, temporal, expected): - assert temporal.at_max() == expected - - @pytest.mark.parametrize( - "temporal, expected", - [ - (tbi, TBoolInst("True@2019-09-01")), - (tbds, TBoolSeq("{False@2019-09-02}")), - (tbs, TBoolSeq("[False@2019-09-02]")), - (tbss, TBoolSeqSet("{[False@2019-09-02]}")), - ], - ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], - ) - def test_at_min(self, temporal, expected): - assert temporal.at_min() == expected - @pytest.mark.parametrize( "temporal, restrictor, expected", [ @@ -1589,35 +1561,6 @@ def test_at_min(self, temporal, expected): def test_minus(self, temporal, restrictor, expected): assert temporal.minus(restrictor) == expected - @pytest.mark.parametrize( - "temporal, expected", - [ - (tbi, None), - (tbds, TBoolSeq("{False@2019-09-02}")), - (tbs, TBoolSeq("[False@2019-09-02]")), - (tbss, TBoolSeqSet("{[False@2019-09-02]}")), - ], - ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], - ) - def test_minus_max(self, temporal, expected): - assert temporal.minus_max() == expected - - @pytest.mark.parametrize( - "temporal, expected", - [ - (tbi, None), - (tbds, TBoolSeq("{True@2019-09-01}")), - (tbs, TBoolSeq("[True@2019-09-01, True@2019-09-02)")), - ( - tbss, - TBoolSeqSet("{[True@2019-09-01, True@2019-09-02),[True@2019-09-03, True@2019-09-05]}"), - ), - ], - ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], - ) - def test_minus_min(self, temporal, expected): - assert temporal.minus_min() == expected - @pytest.mark.parametrize( "temporal, restrictor", [ @@ -1681,9 +1624,10 @@ def test_at_minus(self, temporal, restrictor): [tbi, tbds, tbs, tbss], ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], ) - def test_at_minus_min_max(self, temporal): - assert TBool.from_merge(temporal.at_min(), temporal.minus_min()) == temporal - assert TBool.from_merge(temporal.at_max(), temporal.minus_max()) == temporal + def test_min_max_restrictors_raise(self, temporal): + for restrict in (temporal.at_min, temporal.at_max, temporal.minus_min, temporal.minus_max): + with pytest.raises(MeosInvalidArgTypeError): + restrict() class TestTBoolComparisons(TestTBool): diff --git a/tests/main/tfloat_test.py b/tests/main/tfloat_test.py index b3e97c59..41c1089a 100644 --- a/tests/main/tfloat_test.py +++ b/tests/main/tfloat_test.py @@ -401,8 +401,8 @@ def test_as_hexwkb(self, temporal, expected): " 1.5\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-01T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-01T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -410,7 +410,7 @@ def test_as_hexwkb(self, temporal, expected): " 1.5\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00"\n' " ],\n" ' "interpolation": "None"\n' "}", @@ -424,8 +424,8 @@ def test_as_hexwkb(self, temporal, expected): " 2.5\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -434,8 +434,8 @@ def test_as_hexwkb(self, temporal, expected): " 2.5\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -451,8 +451,8 @@ def test_as_hexwkb(self, temporal, expected): " 2.5\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -461,8 +461,8 @@ def test_as_hexwkb(self, temporal, expected): " 2.5\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -478,8 +478,8 @@ def test_as_hexwkb(self, temporal, expected): " 2.5\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-05T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-05T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -490,8 +490,8 @@ def test_as_hexwkb(self, temporal, expected): " 2.5\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -502,8 +502,8 @@ def test_as_hexwkb(self, temporal, expected): " 1.5\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-03T00:00:00+00",\n' - ' "2019-09-05T00:00:00+00"\n' + ' "2019-09-03T00:00:00+00:00",\n' + ' "2019-09-05T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -958,12 +958,12 @@ def test_segments(self, temporal, expected): @pytest.mark.parametrize( "temporal, expected", [ - (tfi, 1307112078), - (tfds, 1935376725), - (tfs, 1935376725), - (tfss, 4247071962), - (tfs, 1935376725), - (tfss, 4247071962), + (tfi, 1307112109), + (tfds, 3164454719), + (tfs, 3164454719), + (tfss, 1518183798), + (tfs, 3164454719), + (tfss, 1518183798), ], ids=[ "Instant", @@ -2836,8 +2836,9 @@ def test_value_split(self, temporal, expected): ( tfss, [ - TFloatSeq("[1@2019-09-01,2@2019-09-02]"), - TFloatSeq("[1@2019-09-03, 1@2019-09-05]"), + TFloatSeqSet("{[1@2019-09-01, 2@2019-09-02]}"), + TFloatSeqSet("{[1@2019-09-03, 1@2019-09-05)}"), + TFloatSeqSet("{[1@2019-09-05]}"), ], ), ], @@ -2854,15 +2855,17 @@ def test_time_split(self, temporal, expected): ( 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@2019-09-01, 1.5@2019-09-01 12:00)"), + TFloatSeq("[1.5@2019-09-01 12:00, 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]"), + TFloatSeqSet("{[1@2019-09-01, 2@2019-09-02]}"), + TFloatSeqSet("{[1@2019-09-03, 1@2019-09-05)}"), + TFloatSeqSet("{[1@2019-09-05]}"), ], ), ], @@ -2886,9 +2889,10 @@ def test_time_split_n(self, temporal, expected): ( tfss, [ - TFloatSeq("{[1@2019-09-01, 2@2019-09-02)}"), - TFloatSeq("{[1@2019-09-03, 1@2019-09-05]}"), - TFloatSeq("{[2@2019-09-02]}"), + TFloatSeqSet("{[1@2019-09-01, 2@2019-09-02)}"), + TFloatSeqSet("{[1@2019-09-03, 1@2019-09-05)}"), + TFloatSeqSet("{[1@2019-09-05]}"), + TFloatSeqSet("{[2@2019-09-02]}"), ], ), ], diff --git a/tests/main/tgeogpoint_test.py b/tests/main/tgeogpoint_test.py index 4e415c37..4f3abeb2 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 MeosInvalidArgTypeError, MeosInvalidArgValueError from shapely import LineString, Point, set_srid from pymeos import ( @@ -436,6 +436,12 @@ def test_as_hexwkb(self, temporal, expected): tpi, "{\n" ' "type": "MovingPoint",\n' + ' "crs": {\n' + ' "type": "Name",\n' + ' "properties": {\n' + ' "name": "EPSG:4326"\n' + " }\n" + " },\n" ' "bbox": [\n' " [\n" " 1,\n" @@ -447,8 +453,8 @@ def test_as_hexwkb(self, temporal, expected): " ]\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-01T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-01T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -459,7 +465,7 @@ def test_as_hexwkb(self, temporal, expected): " ]\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00"\n' " ],\n" ' "interpolation": "None"\n' "}", @@ -468,6 +474,12 @@ def test_as_hexwkb(self, temporal, expected): tpds, "{\n" ' "type": "MovingPoint",\n' + ' "crs": {\n' + ' "type": "Name",\n' + ' "properties": {\n' + ' "name": "EPSG:4326"\n' + " }\n" + " },\n" ' "bbox": [\n' " [\n" " 1,\n" @@ -479,8 +491,8 @@ def test_as_hexwkb(self, temporal, expected): " ]\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -495,8 +507,8 @@ def test_as_hexwkb(self, temporal, expected): " ]\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -507,6 +519,12 @@ def test_as_hexwkb(self, temporal, expected): tps, "{\n" ' "type": "MovingPoint",\n' + ' "crs": {\n' + ' "type": "Name",\n' + ' "properties": {\n' + ' "name": "EPSG:4326"\n' + " }\n" + " },\n" ' "bbox": [\n' " [\n" " 1,\n" @@ -518,8 +536,8 @@ def test_as_hexwkb(self, temporal, expected): " ]\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -534,8 +552,8 @@ def test_as_hexwkb(self, temporal, expected): " ]\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -546,6 +564,12 @@ def test_as_hexwkb(self, temporal, expected): tpss, "{\n" ' "type": "MovingPoint",\n' + ' "crs": {\n' + ' "type": "Name",\n' + ' "properties": {\n' + ' "name": "EPSG:4326"\n' + " }\n" + " },\n" ' "bbox": [\n' " [\n" " 1,\n" @@ -557,8 +581,8 @@ def test_as_hexwkb(self, temporal, expected): " ]\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-05T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-05T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -575,8 +599,8 @@ def test_as_hexwkb(self, temporal, expected): " ]\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -593,8 +617,8 @@ def test_as_hexwkb(self, temporal, expected): " ]\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-03T00:00:00+00",\n' - ' "2019-09-05T00:00:00+00"\n' + ' "2019-09-03T00:00:00+00:00",\n' + ' "2019-09-05T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -984,7 +1008,7 @@ def test_segments(self, temporal, expected): @pytest.mark.parametrize( "temporal, expected", - [(tpi, 1181779687), (tpds, 1545137628), (tps, 1545137628), (tpss, 1008965061)], + [(tpi, 1181779718), (tpds, 2774215622), (tps, 2774215622), (tpss, 429936847)], ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], ) def test_hash(self, temporal, expected): @@ -2235,9 +2259,10 @@ def test_at_minus(self, temporal, restrictor): [tpi, tpds, tps, tpss], ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], ) - def test_at_minus_min_max(self, temporal): - assert TGeogPoint.from_merge(temporal.at_min(), temporal.minus_min()) == temporal - assert TGeogPoint.from_merge(temporal.at_max(), temporal.minus_max()) == temporal + def test_min_max_restrictors_raise(self, temporal): + for restrict in (temporal.at_min, temporal.at_max, temporal.minus_min, temporal.minus_max): + with pytest.raises(MeosInvalidArgTypeError): + restrict() class TestTGeogPointEverSpatialOperations(TestTGeogPoint): @@ -2470,8 +2495,9 @@ class TestTGeogPointSplitOperations(TestTGeogPoint): ( 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]"), + TGeogPointSeqSet("{[POINT(1 1)@2019-09-01, POINT(2 2)@2019-09-02]}"), + TGeogPointSeqSet("{[POINT(1 1)@2019-09-03, POINT(1 1)@2019-09-05)}"), + TGeogPointSeqSet("{[POINT(1 1)@2019-09-05]}"), ], ), ], @@ -2494,15 +2520,17 @@ def test_time_split(self, temporal, expected): ( 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]"), + TGeogPointSeq("[POINT(1 1)@2019-09-01, POINT(1.5 1.5)@2019-09-01 12:00)"), + TGeogPointSeq("[POINT(1.5 1.5)@2019-09-01 12:00, POINT(2 2)@2019-09-02)"), + TGeogPointSeq("[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]"), + TGeogPointSeqSet("{[POINT(1 1)@2019-09-01, POINT(2 2)@2019-09-02]}"), + TGeogPointSeqSet("{[POINT(1 1)@2019-09-03, POINT(1 1)@2019-09-05)}"), + TGeogPointSeqSet("{[POINT(1 1)@2019-09-05]}"), ], ), ], diff --git a/tests/main/tgeompoint_test.py b/tests/main/tgeompoint_test.py index d08c6a03..fa647db8 100644 --- a/tests/main/tgeompoint_test.py +++ b/tests/main/tgeompoint_test.py @@ -5,7 +5,7 @@ import numpy as np import pytest -from pymeos_cffi import MeosInvalidArgValueError +from pymeos_cffi import MeosInvalidArgTypeError, MeosInvalidArgValueError from shapely import GeometryCollection, LineString, MultiPoint, Point, Polygon from pymeos import ( @@ -466,8 +466,8 @@ def test_as_geojson(self, temporal, expected): " ]\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-01T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-01T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -478,7 +478,7 @@ def test_as_geojson(self, temporal, expected): " ]\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00"\n' " ],\n" ' "interpolation": "None"\n' "}", @@ -498,8 +498,8 @@ def test_as_geojson(self, temporal, expected): " ]\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -514,8 +514,8 @@ def test_as_geojson(self, temporal, expected): " ]\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -537,8 +537,8 @@ def test_as_geojson(self, temporal, expected): " ]\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -553,8 +553,8 @@ def test_as_geojson(self, temporal, expected): " ]\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -576,8 +576,8 @@ def test_as_geojson(self, temporal, expected): " ]\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-05T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-05T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -594,8 +594,8 @@ def test_as_geojson(self, temporal, expected): " ]\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -612,8 +612,8 @@ def test_as_geojson(self, temporal, expected): " ]\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-03T00:00:00+00",\n' - ' "2019-09-05T00:00:00+00"\n' + ' "2019-09-03T00:00:00+00:00",\n' + ' "2019-09-05T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -1010,10 +1010,10 @@ def test_segments(self, temporal, expected): @pytest.mark.parametrize( "temporal, expected", [ - (tpi, 382694564), - (tpds, 1664033448), - (tps, 1664033448), - (tpss, 2878566103), + (tpi, 382694595), + (tpds, 2893111442), + (tps, 2893111442), + (tpss, 1203500793), ], ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], ) @@ -2130,35 +2130,6 @@ class TestTGeomPointRestrictors(TestTGeomPoint): def test_at(self, temporal, restrictor, expected): assert temporal.at(restrictor) == expected - @pytest.mark.parametrize( - "temporal, expected", - [ - (tpi, TGeomPointSeqSet("{[Point(1 1)@2019-09-01]}")), - (tpds, TGeomPointSeqSet("{[Point(1 1)@2019-09-01]}")), - (tps, TGeomPointSeqSet("{[Point(1 1)@2019-09-01]}")), - ( - tpss, - TGeomPointSeqSet("{[Point(1 1)@2019-09-01],[Point(1 1)@2019-09-03,Point(1 1)@2019-09-05]}"), - ), - ], - ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], - ) - def test_at_min(self, temporal, expected): - assert temporal.at_min() == expected - - @pytest.mark.parametrize( - "temporal, expected", - [ - (tpi, TGeomPointSeqSet("{[Point(1 1)@2019-09-01]}")), - (tpds, TGeomPointSeqSet("{[Point(2 2)@2019-09-02]}")), - (tps, TGeomPointSeqSet("{[Point(2 2)@2019-09-02]}")), - (tpss, TGeomPointSeqSet("{[Point(2 2)@2019-09-02]}")), - ], - ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], - ) - def test_at_max(self, temporal, expected): - assert temporal.at_max() == expected - @pytest.mark.parametrize( "temporal, restrictor, expected", [ @@ -2259,43 +2230,6 @@ def test_at_max(self, temporal, expected): def test_minus(self, temporal, restrictor, expected): assert temporal.minus(restrictor) == expected - @pytest.mark.parametrize( - "temporal, expected", - [ - (tpi, None), - (tpds, TGeomPointSeqSet("{[Point(2 2)@2019-09-02]}")), - (tps, TGeomPointSeqSet("{(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]}")), - ], - ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], - ) - def test_minus_min(self, temporal, expected): - if expected is None: - assert temporal.minus_min() is None - else: - assert temporal.minus_min() == expected - - @pytest.mark.parametrize( - "temporal, expected", - [ - (tpi, None), - (tpds, TGeomPointSeqSet("{[Point(1 1)@2019-09-01]}")), - (tps, TGeomPointSeqSet("{[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"], - ) - def test_minus_max(self, temporal, expected): - if expected is None: - assert temporal.minus_max() is None - else: - assert temporal.minus_max() == expected - @pytest.mark.parametrize( "temporal, restrictor", [ @@ -2359,9 +2293,10 @@ def test_at_minus(self, temporal, restrictor): [tpi, tpds, tps, tpss], ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], ) - def test_at_minus_min_max(self, temporal): - assert TGeomPoint.from_merge(temporal.at_min(), temporal.minus_min()) == temporal - assert TGeomPoint.from_merge(temporal.at_max(), temporal.minus_max()) == temporal + def test_min_max_restrictors_raise(self, temporal): + for restrict in (temporal.at_min, temporal.at_max, temporal.minus_min, temporal.minus_max): + with pytest.raises(MeosInvalidArgTypeError): + restrict() class TestTGeomPointTopologicalFunctions(TestTGeomPoint): @@ -3140,8 +3075,9 @@ class TestTGeomPointSplitOperations(TestTGeomPoint): ( tpss, [ - TGeomPointSeq("[Point(1 1)@2019-09-01,Point(2 2)@2019-09-02]"), - TGeomPointSeq("[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]"), + TGeomPointSeqSet("{[POINT(1 1)@2019-09-01, POINT(2 2)@2019-09-02]}"), + TGeomPointSeqSet("{[POINT(1 1)@2019-09-03, POINT(1 1)@2019-09-05)}"), + TGeomPointSeqSet("{[POINT(1 1)@2019-09-05]}"), ], ), ], @@ -3164,15 +3100,17 @@ def test_time_split(self, temporal, expected): ( tps, [ - TGeomPointSeq("[Point(1 1)@2019-09-01, Point(1.5 1.5)@2019-09-01 12:00:00+00)"), - TGeomPointSeq("[Point(1.5 1.5)@2019-09-01 12:00:00+00, Point(2 2)@2019-09-02]"), + TGeomPointSeq("[POINT(1 1)@2019-09-01, POINT(1.5 1.5)@2019-09-01 12:00)"), + TGeomPointSeq("[POINT(1.5 1.5)@2019-09-01 12:00, POINT(2 2)@2019-09-02)"), + TGeomPointSeq("[POINT(2 2)@2019-09-02]"), ], ), ( tpss, [ - TGeomPointSeq("[Point(1 1)@2019-09-01,Point(2 2)@2019-09-02]"), - TGeomPointSeq("[Point(1 1)@2019-09-03, Point(1 1)@2019-09-05]"), + TGeomPointSeqSet("{[POINT(1 1)@2019-09-01, POINT(2 2)@2019-09-02]}"), + TGeomPointSeqSet("{[POINT(1 1)@2019-09-03, POINT(1 1)@2019-09-05)}"), + TGeomPointSeqSet("{[POINT(1 1)@2019-09-05]}"), ], ), ], diff --git a/tests/main/tint_test.py b/tests/main/tint_test.py index 42431b0c..6380fce6 100644 --- a/tests/main/tint_test.py +++ b/tests/main/tint_test.py @@ -355,8 +355,8 @@ def test_as_hexwkb(self, temporal, expected): " 1\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-01T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-01T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -364,7 +364,7 @@ def test_as_hexwkb(self, temporal, expected): " 1\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00"\n' " ],\n" ' "interpolation": "None"\n' "}", @@ -378,8 +378,8 @@ def test_as_hexwkb(self, temporal, expected): " 2\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -388,8 +388,8 @@ def test_as_hexwkb(self, temporal, expected): " 2\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -405,8 +405,8 @@ def test_as_hexwkb(self, temporal, expected): " 2\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -415,8 +415,8 @@ def test_as_hexwkb(self, temporal, expected): " 2\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -432,8 +432,8 @@ def test_as_hexwkb(self, temporal, expected): " 2\n" " ],\n" ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-05T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-05T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -444,8 +444,8 @@ def test_as_hexwkb(self, temporal, expected): " 2\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -456,8 +456,8 @@ def test_as_hexwkb(self, temporal, expected): " 1\n" " ],\n" ' "datetimes": [\n' - ' "2019-09-03T00:00:00+00",\n' - ' "2019-09-05T00:00:00+00"\n' + ' "2019-09-03T00:00:00+00:00",\n' + ' "2019-09-05T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -882,7 +882,7 @@ def test_segments(self, temporal, expected): @pytest.mark.parametrize( "temporal, expected", - [(tii, 440045287), (tids, 3589664982), (tis, 3589664982), (tiss, 205124107)], + [(tii, 440045318), (tids, 523775680), (tis, 523775680), (tiss, 2660990281)], ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], ) def test_hash(self, temporal, expected): @@ -2196,7 +2196,7 @@ class TestTIntTopologicalFunctions(TestTInt): "temporal, argument, expected", [ (tii, TIntInst("1@2019-09-02"), False), - (tii, TIntInst("2@2019-09-02"), True), + (tii, TIntInst("2@2019-09-01"), True), (tids, TIntInst("1@2019-09-03"), False), (tids, TIntInst("3@2019-09-02"), True), (tis, TIntInst("1@2019-09-03"), False), @@ -2573,8 +2573,9 @@ def test_value_split(self, temporal, expected): ( tiss, [ - TIntSeq("[1@2019-09-01,2@2019-09-02]"), - TIntSeq("[1@2019-09-03, 1@2019-09-05]"), + TIntSeqSet("{[1@2019-09-01, 2@2019-09-02]}"), + TIntSeqSet("{[1@2019-09-03, 1@2019-09-05)}"), + TIntSeqSet("{[1@2019-09-05]}"), ], ), ], @@ -2592,9 +2593,10 @@ def test_time_split(self, temporal, expected): ( tiss, [ - TIntSeq("{[1@2019-09-01, 1@2019-09-02)}"), - TIntSeq("{[1@2019-09-03, 1@2019-09-05]}"), - TIntSeq("{[2@2019-09-02]}"), + TIntSeqSet("{[1@2019-09-01, 1@2019-09-02)}"), + TIntSeqSet("{[1@2019-09-03, 1@2019-09-05)}"), + TIntSeqSet("{[1@2019-09-05]}"), + TIntSeqSet("{[2@2019-09-02]}"), ], ), ], diff --git a/tests/main/ttext_test.py b/tests/main/ttext_test.py index c575a447..0d109bf0 100644 --- a/tests/main/ttext_test.py +++ b/tests/main/ttext_test.py @@ -358,8 +358,8 @@ def test_as_hexwkb(self, temporal, expected): "{\n" ' "type": "MovingText",\n' ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-01T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-01T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -367,7 +367,7 @@ def test_as_hexwkb(self, temporal, expected): ' "AAA"\n' " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00"\n' " ],\n" ' "interpolation": "None"\n' "}", @@ -377,8 +377,8 @@ def test_as_hexwkb(self, temporal, expected): "{\n" ' "type": "MovingText",\n' ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -387,8 +387,8 @@ def test_as_hexwkb(self, temporal, expected): ' "BBB"\n' " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -400,8 +400,8 @@ def test_as_hexwkb(self, temporal, expected): "{\n" ' "type": "MovingText",\n' ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-02T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-02T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -410,8 +410,8 @@ def test_as_hexwkb(self, temporal, expected): ' "BBB"\n' " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true,\n' @@ -423,8 +423,8 @@ def test_as_hexwkb(self, temporal, expected): "{\n" ' "type": "MovingText",\n' ' "period": {\n' - ' "begin": "2019-09-01T00:00:00+00",\n' - ' "end": "2019-09-05T00:00:00+00",\n' + ' "begin": "2019-09-01T00:00:00+00:00",\n' + ' "end": "2019-09-05T00:00:00+00:00",\n' ' "lower_inc": true,\n' ' "upper_inc": true\n' " },\n" @@ -435,8 +435,8 @@ def test_as_hexwkb(self, temporal, expected): ' "BBB"\n' " ],\n" ' "datetimes": [\n' - ' "2019-09-01T00:00:00+00",\n' - ' "2019-09-02T00:00:00+00"\n' + ' "2019-09-01T00:00:00+00:00",\n' + ' "2019-09-02T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -447,8 +447,8 @@ def test_as_hexwkb(self, temporal, expected): ' "AAA"\n' " ],\n" ' "datetimes": [\n' - ' "2019-09-03T00:00:00+00",\n' - ' "2019-09-05T00:00:00+00"\n' + ' "2019-09-03T00:00:00+00:00",\n' + ' "2019-09-05T00:00:00+00:00"\n' " ],\n" ' "lower_inc": true,\n' ' "upper_inc": true\n' @@ -838,7 +838,7 @@ def test_segments(self, temporal, expected): @pytest.mark.parametrize( "temporal, expected", - [(tti, 1893808825), (ttds, 1223816819), (tts, 1223816819), (ttss, 2199213310)], + [(tti, 1893808856), (ttds, 2452894813), (tts, 2452894813), (ttss, 3439543446)], ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], ) def test_hash(self, temporal, expected):