Repository: mie-lab/trackintel

Issue:
All generating functions should return Trackintel classes
Currently we return GeoDataFrames / DataFrames from function as `read_positionfixes_csv` etc.
They should return Trackintel classes.

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab) Report
Attention: `2 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`8a17c7b`)](https://app.codecov.io/gh/mie-lab/trackintel/commit/8a17c7bc7df2e2315feb9a30cea2f9eea7cdd16e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab) 92.94% compared to head [(`bbdb079`)](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab) 92.93%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #554      +/-   ##
==========================================
- Coverage   92.94%   92.93%   -0.02%     
==========================================
  Files          32       32              
  Lines        2042     2038       -4     
  Branches      368      368              
==========================================
- Hits         1898     1894       -4     
  Misses        130      130              
  Partials       14       14              
```


| [Files](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab) | Coverage Δ | |
|---|---|---|
| [trackintel/io/file.py](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9pby9maWxlLnB5) | `100.00% <ø> (ø)` | |
| [trackintel/io/from\_geopandas.py](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9pby9mcm9tX2dlb3BhbmRhcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [trackintel/model/trips.py](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9tb2RlbC90cmlwcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [trackintel/preprocessing/positionfixes.py](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9wcmVwcm9jZXNzaW5nL3Bvc2l0aW9uZml4ZXMucHk=) | `98.83% <100.00%> (ø)` | |
| [trackintel/preprocessing/staypoints.py](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9wcmVwcm9jZXNzaW5nL3N0YXlwb2ludHMucHk=) | `99.09% <100.00%> (ø)` | |
| [trackintel/preprocessing/triplegs.py](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9wcmVwcm9jZXNzaW5nL3RyaXBsZWdzLnB5) | `100.00% <100.00%> (ø)` | |
| [trackintel/preprocessing/trips.py](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9wcmVwcm9jZXNzaW5nL3RyaXBzLnB5) | `97.56% <100.00%> (+0.01%)` | :arrow_up: |
| [trackintel/io/dataset\_reader.py](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9pby9kYXRhc2V0X3JlYWRlci5weQ==) | `46.90% <50.00%> (+0.23%)` | :arrow_up: |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/mie-lab/trackintel/pull/554?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab).


Patch:
diff --git a/trackintel/io/dataset_reader.py b/trackintel/io/dataset_reader.py
index 4c434ed..4b168f5 100644
--- a/trackintel/io/dataset_reader.py
+++ b/trackintel/io/dataset_reader.py
@@ -12,6 +12,7 @@ from sklearn.neighbors import NearestNeighbors
 from tqdm import tqdm
 
 from trackintel.preprocessing.util import calc_temp_overlap
+from trackintel import Positionfixes, Staypoints, Triplegs
 
 FEET2METER = 0.3048
 CRS_WGS84 = 4326
@@ -113,7 +114,7 @@ def read_geolife(geolife_path, print_progress=False):
     labels = _get_labels(geolife_path, uids)
     # get the dfs in form of an generator and concatinate them
     gdf = pd.concat(_get_df(geolife_path, uids, print_progress), axis=0, ignore_index=True)
-    gdf = gpd.GeoDataFrame(gdf, geometry="geom", crs=CRS_WGS84)
+    gdf = Positionfixes(gdf, geometry="geom", crs=CRS_WGS84)
     gdf["accuracy"] = np.nan
     gdf.index.name = "id"
     return gdf, labels
@@ -437,7 +438,7 @@ def read_mzmv(mzmv_path):
     tpls = pd.merge(tpls, vp, on=["user_id", "ETNR"], how="left")
 
     # # with the geometry we can build GeoDataFrame
-    tpls = gpd.GeoDataFrame(tpls, geometry="geometry", crs=CRS_WGS84)
+    tpls = Triplegs(tpls, geometry="geometry", crs=CRS_WGS84)
     tpls.index.name = "tripleg_id"
 
     # # set invalid geometries as missing geometries
@@ -669,7 +670,7 @@ def _mzmv_generate_sp(tpls, zf):
 
     sp["XY"] = gpd.points_from_xy(sp["X"], sp["Y"], crs=CRS_WGS84)
     sp["XY_CH1904"] = gpd.points_from_xy(sp["X_CH1903"], sp["Y_CH1903"], crs=CRS_CH1903)
-    sp = gpd.GeoDataFrame(sp, geometry="XY", crs=CRS_WGS84)
+    sp = Staypoints(sp, geometry="XY", crs=CRS_WGS84)
     sp.index.name = "staypoint_id"
 
     # fix datetime dtype
diff --git a/trackintel/io/file.py b/trackintel/io/file.py
index b33b917..4c487cb 100644
--- a/trackintel/io/file.py
+++ b/trackintel/io/file.py
@@ -13,7 +13,6 @@ from trackintel.io.from_geopandas import (
 )
 from trackintel.io.util import _index_warning_default_none
 from trackintel.model.util import doc, _shared_docs
-from trackintel import Positionfixes
 
 
 @_index_warning_default_none
diff --git a/trackintel/io/from_geopandas.py b/trackintel/io/from_geopandas.py
index 38d1baa..fa13f66 100644
--- a/trackintel/io/from_geopandas.py
+++ b/trackintel/io/from_geopandas.py
@@ -3,6 +3,8 @@ import pandas as pd
 import geopandas as gpd
 import pytz
 
+from trackintel import Positionfixes, Staypoints, Triplegs, Locations, Trips, Tours
+
 
 def read_positionfixes_gpd(
     gdf, tracked_at="tracked_at", user_id="user_id", geom_col=None, crs=None, tz=None, mapper=None
@@ -51,9 +53,7 @@ def read_positionfixes_gpd(
         columns.update(mapper)
 
     pfs = _trackintel_model(gdf, columns, geom_col, crs, ["tracked_at"], tz)
-    # assert validity of positionfixes
-    pfs.as_positionfixes
-    return pfs
+    return Positionfixes(pfs)
 
 
 def read_staypoints_gpd(
@@ -114,9 +114,7 @@ def read_staypoints_gpd(
 
     sp = _trackintel_model(gdf, columns, geom_col, crs, ["started_at", "finished_at"], tz)
 
-    # assert validity of staypoints
-    sp.as_staypoints
-    return sp
+    return Staypoints(sp)
 
 
 def read_triplegs_gpd(
@@ -176,9 +174,7 @@ def read_triplegs_gpd(
         columns.update(mapper)
 
     tpls = _trackintel_model(gdf, columns, geom_col, crs, ["started_at", "finished_at"], tz)
-    # assert validity of triplegs
-    tpls.as_triplegs
-    return tpls
+    return Triplegs(tpls)
 
 
 def read_trips_gpd(
@@ -253,9 +249,7 @@ def read_trips_gpd(
 
     trips = _trackintel_model(gdf, columns, geom_col, crs, ["started_at", "finished_at"], tz)
 
-    # assert validity of trips
-    trips.as_trips
-    return trips
+    return Trips(trips)
 
 
 def read_locations_gpd(gdf, user_id="user_id", center="center", extent=None, crs=None, mapper=None):
@@ -306,9 +300,7 @@ def read_locations_gpd(gdf, user_id="user_id", center="center", extent=None, crs
     if extent is not None:
         locs["extent"] = gpd.GeoSeries(locs["extent"])
 
-    # assert validity of locations
-    locs.as_locations
-    return locs
+    return Locations(locs)
 
 
 def read_tours_gpd(
@@ -358,11 +350,7 @@ def read_tours_gpd(
         columns.update(mapper)
 
     tours = _trackintel_model(gdf, set_names=columns, tz_cols=["started_at", "finished_at"], tz=tz)
-
-    # assert validity of tours
-    tours.as_tours
-
-    return tours
+    return Tours(tours)
 
 
 def _trackintel_model(gdf, set_names=None, geom_col=None, crs=None, tz_cols=None, tz=None):
diff --git a/trackintel/model/trips.py b/trackintel/model/trips.py
index e97cfb5..5e5d1ce 100644
--- a/trackintel/model/trips.py
+++ b/trackintel/model/trips.py
@@ -172,7 +172,7 @@ class TripsGeoDataFrame(TrackintelGeoDataFrame, TripsDataFrame, gpd.GeoDataFrame
     fallback_class = TripsDataFrame
 
     def __init__(self, *args, validate=True, **kwargs):
-        super().__init__(*args, **kwargs)
+        super().__init__(*args, validate=validate, **kwargs)
         if validate:
             TripsGeoDataFrame.validate(self)
 
diff --git a/trackintel/preprocessing/positionfixes.py b/trackintel/preprocessing/positionfixes.py
index 3282721..d386cae 100644
--- a/trackintel/preprocessing/positionfixes.py
+++ b/trackintel/preprocessing/positionfixes.py
@@ -7,7 +7,7 @@ import pandas as pd
 from shapely.geometry import LineString
 from tqdm import tqdm
 
-from trackintel import Positionfixes, Staypoints
+from trackintel import Positionfixes, Staypoints, Triplegs
 from trackintel.geogr import check_gdf_planar, point_haversine_dist
 from trackintel.preprocessing.util import _explode_agg, angle_centroid_multipoints, applyParallel
 
@@ -150,11 +150,6 @@ def generate_staypoints(
         pfs = _explode_agg("pfs_id", "staypoint_id", pfs, sp)
     sp = gpd.GeoDataFrame(sp, columns=sp_column, geometry=geo_col, crs=pfs.crs)
 
-    if len(sp) > 0:
-        sp.as_staypoints
-    else:
-        warnings.warn("No staypoints can be generated, returning empty sp.")
-
     ## dtype consistency
     # sp id (generated by this function) should be int64
     sp.index = sp.index.astype("int64")
@@ -164,7 +159,11 @@ def generate_staypoints(
     # user_id of sp should be the same as ret_pfs
     sp["user_id"] = sp["user_id"].astype(pfs["user_id"].dtype)
 
-    return pfs, sp
+    if len(sp) == 0:
+        warnings.warn("No staypoints can be generated, returning empty sp.")
+        return pfs, sp
+
+    return pfs, Staypoints(sp)
 
 
 def generate_triplegs(
@@ -379,11 +378,6 @@ def generate_triplegs(
         # assert validity of triplegs
         tpls, pfs = _drop_invalid_triplegs(tpls, pfs)
 
-        if len(tpls) > 0:
-            tpls.as_triplegs
-        else:
-            warnings.warn("No triplegs can be generated, returning empty tpls.")
-
         if case == 2:
             pfs.drop(columns="staypoint_id", inplace=True)
 
@@ -394,8 +388,11 @@ def generate_triplegs(
 
         # user_id of tpls should be the same as pfs
         tpls["user_id"] = tpls["user_id"].astype(pfs["user_id"].dtype)
+        if len(tpls) == 0:
+            warnings.warn("No triplegs can be generated, returning empty tpls.")
+            return pfs, tpls
 
-        return pfs, tpls
+        return pfs, Triplegs(tpls)
 
     else:
         raise AttributeError(f"Method unknown. We only support 'between_staypoints'. You passed {method}")
diff --git a/trackintel/preprocessing/staypoints.py b/trackintel/preprocessing/staypoints.py
index 547f71c..7096f10 100644
--- a/trackintel/preprocessing/staypoints.py
+++ b/trackintel/preprocessing/staypoints.py
@@ -4,7 +4,7 @@ import pandas as pd
 from sklearn.cluster import DBSCAN
 import warnings
 
-from trackintel import Staypoints
+from trackintel import Staypoints, Locations
 from trackintel.geogr.distances import meters_to_decimal_degrees, check_gdf_planar
 from trackintel.preprocessing.util import applyParallel, angle_centroid_multipoints
 
@@ -180,11 +180,6 @@ def generate_locations(
     # merge non_activities back if "activities_only" flag is set
     sp = pd.concat([sp, non_activities])
 
-    if len(locs) > 0:
-        locs.as_locations  # empty location is not valid
-    else:
-        warnings.warn("No locations can be generated, returning empty locs.")
-
     ## dtype consistency
     # locs id (generated by this function) should be int64
     locs.index = locs.index.astype("int64")
@@ -192,8 +187,12 @@ def generate_locations(
     sp["location_id"] = sp["location_id"].astype("Int64")
     # user_id of locs should be the same as sp
     locs["user_id"] = locs["user_id"].astype(sp["user_id"].dtype)
-
-    return sp, locs
+    if len(locs) == 0:
+        warnings.warn("No locations can be generated, returning empty locs.")
+        return sp, locs
+    # keep class of staypoints
+    sp = Staypoints(sp) if isinstance(staypoints, Staypoints) else sp
+    return sp, Locations(locs)
 
 
 def _gen_locs_dbscan(sp, distance_metric, db):
@@ -243,7 +242,7 @@ def merge_staypoints(staypoints, triplegs, max_time_gap="10min", agg={}):
 
     Returns
     -------
-    sp: DataFrame
+    sp: GeoDataFrame (as trackintel staypoints)
         The new staypoints with the default columns and columns in `agg`, where staypoints at same location and close in
         time are aggregated.
 
diff --git a/trackintel/preprocessing/triplegs.py b/trackintel/preprocessing/triplegs.py
index be3f57a..f2ce355 100644
--- a/trackintel/preprocessing/triplegs.py
+++ b/trackintel/preprocessing/triplegs.py
@@ -5,7 +5,7 @@ import numpy as np
 import pandas as pd
 from shapely.geometry import MultiPoint, Point
 
-from trackintel import Staypoints, Triplegs
+from trackintel import Staypoints, Triplegs, Trips
 from trackintel.preprocessing.util import _explode_agg
 
 
@@ -225,7 +225,7 @@ def generate_trips(staypoints, triplegs, gap_threshold=15, add_geometry=True):
     # user_id of trips should be the same as tpls
     trips["user_id"] = trips["user_id"].astype(tpls["user_id"].dtype)
 
-    return sp, tpls, trips
+    return sp, tpls, Trips(trips)
 
 
 def _concat_staypoints_triplegs(staypoints, triplegs, add_geometry):
diff --git a/trackintel/preprocessing/trips.py b/trackintel/preprocessing/trips.py
index 3563841..cbc9c1d 100644
--- a/trackintel/preprocessing/trips.py
+++ b/trackintel/preprocessing/trips.py
@@ -7,6 +7,7 @@ import pandas as pd
 from tqdm import tqdm
 
 import trackintel as ti
+from trackintel import Tours, Trips
 
 
 def get_trips_grouped(trips, tours):
@@ -66,7 +67,7 @@ def generate_tours(
     ----------
     trips : GeoDataFrame (as trackintel trips)
 
-    staypoints : GeoDataFrame (as trackintel staypoints), default None
+    staypoints : GeoDataFrame (as trackintel staypoints), optional
         Must have `location_id` column to connect trips via locations to a tour.
         If None, trips will be connected based only by the set distance threshold `max_dist`.
 
@@ -120,13 +121,13 @@ def generate_tours(
         geom_col = None  # not used
         crs_is_projected = False  # not used
         ti.Staypoints.validate(staypoints)
+        ti.TripsDataFrame.validate(trips)
     else:
         # if no location is given, we need the trips table to have a geometry column
-        assert isinstance(trips, gpd.geodataframe.GeoDataFrame), "Trips table must be a GeoDataFrame"
+        ti.TripsGeoDataFrame.validate(trips)
         geom_col = trips.geometry.name
         # get crs
         crs_is_projected = ti.geogr.check_gdf_planar(trips)
-        ti.TripsGeoDataFrame.validate(trips)
 
     # convert max_time to timedelta
     if isinstance(max_time, str):
@@ -183,7 +184,7 @@ def generate_tours(
     # trips id (generated by this function) should be int64
     tours.index = tours.index.astype("int64")
 
-    return trips_with_tours, tours
+    return trips_with_tours, Tours(tours)
 
 
 def _generate_tours_user(


Test patch:
diff --git a/tests/io/test_file.py b/tests/io/test_file.py
index 316e6f3..ad79269 100644
--- a/tests/io/test_file.py
+++ b/tests/io/test_file.py
@@ -78,6 +78,13 @@ class TestPositionfixes:
         pfs = ti.read_positionfixes_csv(file, sep=";", index_col=None)
         assert pfs.index.name is None
 
+    def test_type(self):
+        """Test if returned object is Positionfix"""
+        file = os.path.join("tests", "data", "positionfixes.csv")
+        ind_name = "id"
+        pfs = ti.read_positionfixes_csv(file, sep=";", index_col=ind_name)
+        assert isinstance(pfs, ti.Positionfixes)
+
 
 class TestTriplegs:
     """Test for 'read_triplegs_csv' and 'write_triplegs_csv' functions."""
@@ -146,6 +153,13 @@ class TestTriplegs:
         pfs = ti.read_triplegs_csv(file, sep=";", index_col=None)
         assert pfs.index.name is None
 
+    def test_type(self):
+        """Test if returned object is Triplegs"""
+        file = os.path.join("tests", "data", "triplegs.csv")
+        ind_name = "id"
+        tpls = ti.read_triplegs_csv(file, sep=";", index_col=ind_name)
+        assert isinstance(tpls, ti.Triplegs)
+
 
 class TestStaypoints:
     """Test for 'read_staypoints_csv' and 'write_staypoints_csv' functions."""
@@ -211,6 +225,13 @@ class TestStaypoints:
         pfs = ti.read_staypoints_csv(file, sep=";", index_col=None)
         assert pfs.index.name is None
 
+    def test_type(self):
+        """Test if returned object is Staypoint"""
+        file = os.path.join("tests", "data", "staypoints.csv")
+        ind_name = "id"
+        sp = ti.read_staypoints_csv(file, sep=";", index_col=ind_name)
+        assert isinstance(sp, ti.Staypoints)
+
 
 @pytest.fixture
 def example_locations():
@@ -226,8 +247,7 @@ def example_locations():
     ]
     locs = gpd.GeoDataFrame(data=list_dict, geometry="center", crs="EPSG:4326")
     locs.index.name = "id"
-    locs.as_locations
-    return locs
+    return ti.Locations(locs)
 
 
 class TestLocations:
@@ -279,6 +299,13 @@ class TestLocations:
         assert_geodataframe_equal(locs, locs_in)
         os.remove(tmp_file)
 
+    def test_type(self):
+        """Test if returned object is Locations"""
+        file = os.path.join("tests", "data", "locations.csv")
+        ind_name = "id"
+        sp = ti.read_locations_csv(file, sep=";", index_col=ind_name)
+        assert isinstance(sp, ti.Locations)
+
 
 class TestTrips:
     """Test for 'read_trips_csv' and 'write_trips_csv' functions."""
@@ -336,6 +363,13 @@ class TestTrips:
         gdf = ti.read_trips_csv(file, sep=";", index_col=None)
         assert gdf.index.name is None
 
+    def test_type(self):
+        """Test if returned object is Trips"""
+        file = os.path.join("tests", "data", "trips.csv")
+        ind_name = "id"
+        trips = ti.read_trips_csv(file, sep=";", index_col=ind_name)
+        assert isinstance(trips, ti.TripsDataFrame)
+
 
 @pytest.fixture
 def example_tours():
@@ -352,8 +386,7 @@ def example_tours():
     ]
     tours = pd.DataFrame(data=list_dict)
     tours.index.name = "id"
-    tours.as_tours
-    return tours
+    return ti.Tours(tours)
 
 
 class TestTours:
@@ -386,3 +419,10 @@ class TestTours:
         tours.as_tours.to_csv(tmp_file)
         assert filecmp.cmp(orig_file, tmp_file, shallow=False)
         os.remove(tmp_file)
+
+    def test_type(self):
+        """Test if returned object is Tours"""
+        file = os.path.join("tests", "data", "tours.csv")
+        ind_name = "id"
+        sp = ti.read_tours_csv(file, sep=";", index_col=ind_name)
+        assert isinstance(sp, ti.Tours)
diff --git a/tests/io/test_from_geopandas.py b/tests/io/test_from_geopandas.py
index 8f9ebe1..74356c3 100644
--- a/tests/io/test_from_geopandas.py
+++ b/tests/io/test_from_geopandas.py
@@ -34,9 +34,8 @@ def example_positionfixes():
         {"user_id": 0, "tracked_at": t2, "geom": p2},
         {"user_id": 1, "tracked_at": t3, "geom": p3},
     ]
-    pfs = gpd.GeoDataFrame(data=list_dict, geometry="geom", crs="EPSG:4326")
+    pfs = ti.Positionfixes(data=list_dict, geometry="geom", crs="EPSG:4326")
     pfs.index.name = "id"
-    pfs.as_positionfixes
     return pfs
 
 
@@ -142,6 +141,12 @@ class TestRead_Positionfixes_Gpd:
         example_positionfixes.rename(columns=mapper, inplace=True)
         assert_geodataframe_equal(example_positionfixes, pfs)
 
+    def test_type(self, example_positionfixes):
+        """Test if return value is of right type"""
+        pfs = gpd.GeoDataFrame(example_positionfixes)
+        pfs = read_positionfixes_gpd(pfs)
+        assert isinstance(pfs, ti.Positionfixes)
+
 
 class TestRead_Triplegs_Gpd:
     """Test `read_triplegs_gpd()` function."""
@@ -171,6 +176,12 @@ class TestRead_Triplegs_Gpd:
 
         assert_index_equal(tpls.columns, gdf.columns)
 
+    def test_type(self):
+        """Test if return value is of right type"""
+        tpls = gpd.read_file(os.path.join("tests", "data", "triplegs.geojson"))
+        tpls = read_triplegs_gpd(tpls, user_id="User", geom_col="geometry", crs="EPSG:4326", tz="utc")
+        assert isinstance(tpls, ti.Triplegs)
+
 
 class TestRead_Staypoints_Gpd:
     """Test `read_staypoints_gpd()` function."""
@@ -200,6 +211,12 @@ class TestRead_Staypoints_Gpd:
 
         assert_index_equal(gdf.columns, sp.columns)
 
+    def test_type(self):
+        """Test if return value is of right type"""
+        gdf = gpd.read_file(os.path.join("tests", "data", "staypoints.geojson"))
+        sp = read_staypoints_gpd(gdf, "start_time", "end_time", geom_col="geometry", crs="EPSG:4326", tz="utc")
+        assert isinstance(sp, ti.Staypoints)
+
 
 @pytest.fixture()
 def example_locations():
@@ -212,14 +229,13 @@ def example_locations():
         {"user_id": 0, "center": p2},
         {"user_id": 1, "center": p2},
     ]
-    locs = gpd.GeoDataFrame(data=list_dict, geometry="center", crs="EPSG:4326")
+    locs = ti.Locations(data=list_dict, geometry="center", crs="EPSG:4326")
     locs.index.name = "id"
 
     coords = [[8.45, 47.6], [8.45, 47.4], [8.55, 47.4], [8.55, 47.6], [8.45, 47.6]]
     extent = Polygon(coords)
     locs["extent"] = extent  # broadcasting
     locs["extent"] = gpd.GeoSeries(locs["extent"])  # dtype
-    locs.as_locations
     return locs
 
 
@@ -257,6 +273,12 @@ class TestRead_Locations_Gpd:
         example_locations.rename(columns=mapper, inplace=True)
         assert_geodataframe_equal(locs, example_locations)
 
+    def test_type(self):
+        """Test if return value is of right type"""
+        gdf = gpd.read_file(os.path.join("tests", "data", "locations.geojson"))
+        locs = read_locations_gpd(gdf, user_id="User", center="geometry", crs="EPSG:4326")
+        assert isinstance(locs, ti.Locations)
+
 
 @pytest.fixture
 def example_trips():
@@ -275,9 +297,8 @@ def example_trips():
     for n, d in enumerate(list_dict):
         d["started_at"] = start + 4 * n * h
         d["finished_at"] = d["started_at"] + h
-    trips = gpd.GeoDataFrame(data=list_dict, geometry="geom", crs="EPSG:2056")
+    trips = ti.Trips(data=list_dict, geometry="geom", crs="EPSG:2056")
     trips.index.name = "id"
-    trips.as_trips
     return trips
 
 
@@ -309,7 +330,7 @@ class TestRead_Trips_Gpd:
     def test_without_geometry(self, example_trips):
         """Test if DataFrame without geometry stays the same."""
         columns_without_geom = example_trips.columns.difference(["geom"])
-        trips = pd.DataFrame(example_trips[columns_without_geom], copy=True)
+        trips = ti.Trips(example_trips[columns_without_geom], copy=True)
         trips_after_function = read_trips_gpd(trips)
         assert_frame_equal(trips, trips_after_function)
 
@@ -333,6 +354,12 @@ class TestRead_Trips_Gpd:
         example_trips["started_at"] = pd.to_datetime(example_trips["started_at"], utc=True)
         assert_geodataframe_equal(example_trips, trips)
 
+    def test_type(self, example_trips):
+        """Test if return value is of right type"""
+        df = gpd.GeoDataFrame(example_trips)
+        trips = read_trips_gpd(df, tz="utc")
+        assert isinstance(trips, ti.TripsGeoDataFrame)
+
 
 @pytest.fixture
 def example_tours():
@@ -345,9 +372,8 @@ def example_tours():
         {"user_id": 0, "started_at": start + 2 * h, "finished_at": start + 3 * h},
         {"user_id": 1, "started_at": start + 4 * h, "finished_at": start + 5 * h},
     ]
-    tours = pd.DataFrame(data=list_dict)
+    tours = ti.Tours(data=list_dict)
     tours.index.name = "id"
-    tours.as_tours
     return tours
 
 
@@ -371,3 +397,9 @@ class TestRead_Tours_Gpd:
         tours = read_tours_gpd(example_tours, mapper=mapper)
         example_tours.rename(columns=mapper, inplace=True)
         assert_frame_equal(tours, example_tours)
+
+    def test_type(self, example_tours):
+        """Test if return value is of right type"""
+        gdf = pd.DataFrame(example_tours)
+        tours = read_tours_gpd(gdf, tz="utc")
+        assert isinstance(tours, ti.Tours)
diff --git a/tests/io/test_postgis.py b/tests/io/test_postgis.py
index aeb64f7..9bbb81d 100644
--- a/tests/io/test_postgis.py
+++ b/tests/io/test_postgis.py
@@ -64,9 +64,8 @@ def example_positionfixes():
         {"user_id": 0, "tracked_at": t2, "geom": p2},
         {"user_id": 1, "tracked_at": t3, "geom": p3},
     ]
-    pfs = gpd.GeoDataFrame(data=list_dict, geometry="geom", crs="EPSG:4326")
+    pfs = ti.Positionfixes(data=list_dict, geometry="geom", crs="EPSG:4326")
     pfs.index.name = "id"
-    pfs.as_positionfixes
     return pfs
 
 
@@ -87,9 +86,8 @@ def example_staypoints():
         {"user_id": 0, "started_at": t2, "finished_at": t3, "geom": p2},
         {"user_id": 1, "started_at": t3, "finished_at": t3 + one_hour, "geom": p3},
     ]
-    sp = gpd.GeoDataFrame(data=list_dict, geometry="geom", crs="EPSG:4326")
+    sp = ti.Staypoints(data=list_dict, geometry="geom", crs="EPSG:4326")
     sp.index.name = "id"
-    sp.as_staypoints
     return sp
 
 
@@ -112,10 +110,9 @@ def example_triplegs():
         {"id": 2, "user_id": 1, "started_at": t3, "finished_at": t3 + one_hour, "geom": g3},
     ]
 
-    tpls = gpd.GeoDataFrame(data=list_dict, geometry="geom", crs="EPSG:4326")
+    tpls = ti.Triplegs(data=list_dict, geometry="geom", crs="EPSG:4326")
     tpls.set_index("id", inplace=True)
 
-    tpls.as_triplegs
     return tpls
 
 
@@ -131,9 +128,8 @@ def example_locations():
         {"user_id": 0, "center": p2},
         {"user_id": 1, "center": p3},
     ]
-    locs = gpd.GeoDataFrame(data=list_dict, geometry="center", crs="EPSG:4326")
+    locs = ti.Locations(data=list_dict, geometry="center", crs="EPSG:4326")
     locs.index.name = "id"
-    locs.as_locations
     return locs
 
 
@@ -156,9 +152,8 @@ def example_trips():
             "destination_staypoint_id": 1,
         },
     ]
-    trips = pd.DataFrame(data=list_dict)
+    trips = ti.Trips(data=list_dict)
     trips.index.name = "id"
-    trips.as_trips
     return trips
 
 
@@ -175,9 +170,8 @@ def example_tours():
         {"user_id": 0, "started_at": t2, "finished_at": t2 + h},
         {"user_id": 1, "started_at": t3, "finished_at": t3 + h},
     ]
-    tours = pd.DataFrame(data=list_dict)
+    tours = ti.Tours(data=list_dict)
     tours.index.name = "id"
-    tours.as_tours
     return tours
 
 
@@ -651,7 +645,7 @@ class TestTrips:
         """Test if read with geometry works."""
         mp1 = MultiPoint([(0.0, 0.0), (1.0, 1.0)])
         mp2 = MultiPoint([(2.0, 2.0), (3.0, 3.0)])
-        trips = gpd.GeoDataFrame(example_trips, copy=True, geometry=[mp1, mp2, mp2], crs="EPSG:4326")
+        trips = ti.Trips(example_trips, copy=True, geometry=[mp1, mp2, mp2], crs="EPSG:4326")
         conn_string, conn = conn_postgis
         table = "trips"
         sql = f"SELECT * FROM {table}"
diff --git a/tests/model/test_locations.py b/tests/model/test_locations.py
index 84da561..fe0e739 100644
--- a/tests/model/test_locations.py
+++ b/tests/model/test_locations.py
@@ -1,6 +1,7 @@
 import os
 
 import pytest
+import geopandas as gpd
 from shapely.geometry import LineString
 
 import trackintel as ti
@@ -16,7 +17,8 @@ def testdata_locs():
         method="dbscan", epsilon=10, num_samples=1, distance_metric="haversine", agg_level="dataset"
     )
     locs.as_locations
-    return locs
+    # we want test the accessor of GeoDataFrames and not Locations
+    return gpd.GeoDataFrame(locs)
 
 
 class TestLocations:
diff --git a/tests/model/test_positionfixes.py b/tests/model/test_positionfixes.py
index 7239580..c390cbd 100644
--- a/tests/model/test_positionfixes.py
+++ b/tests/model/test_positionfixes.py
@@ -2,17 +2,18 @@ import os
 import pytest
 import numpy as np
 
+import geopandas as gpd
 from shapely.geometry import LineString
 
 import trackintel as ti
-from trackintel import Positionfixes
 
 
 @pytest.fixture
 def testdata_geolife():
     """Read geolife test data from files."""
     pfs, _ = ti.io.dataset_reader.read_geolife(os.path.join("tests", "data", "geolife"))
-    return pfs
+    # we want test the accessor of GeoDataFrames and not Positionfixes
+    return gpd.GeoDataFrame(pfs)
 
 
 class TestPositionfixes:
diff --git a/tests/model/test_staypoints.py b/tests/model/test_staypoints.py
index 323aaae..cc9166f 100644
--- a/tests/model/test_staypoints.py
+++ b/tests/model/test_staypoints.py
@@ -1,10 +1,10 @@
 import os
 import pytest
 
+import geopandas as gpd
 from shapely.geometry import LineString
 
 import trackintel as ti
-from trackintel import Staypoints
 
 
 @pytest.fixture
@@ -12,7 +12,8 @@ def testdata_sp():
     """Read sp test data from files."""
     sp_file = os.path.join("tests", "data", "staypoints.csv")
     sp = ti.read_staypoints_csv(sp_file, sep=";", index_col="id")
-    return sp
+    # we want test the accessor of GeoDataFrames and not Staypoints
+    return gpd.GeoDataFrame(sp)
 
 
 class TestStaypoints:
diff --git a/tests/model/test_tours.py b/tests/model/test_tours.py
index 573d582..0613bc8 100644
--- a/tests/model/test_tours.py
+++ b/tests/model/test_tours.py
@@ -1,6 +1,8 @@
 import pytest
 import os
 
+import geopandas as gpd
+
 import trackintel as ti
 from trackintel import Tours
 
@@ -10,7 +12,8 @@ def test_tours():
     """Read test data from file"""
     tours_file = os.path.join("tests", "data", "tours.csv")
     tours = ti.read_tours_csv(tours_file, sep=";", index_col="id")
-    return tours
+    # we want test the accessor of GeoDataFrames and not Tours
+    return gpd.GeoDataFrame(tours)
 
 
 class TestTours:
diff --git a/tests/model/test_triplegs.py b/tests/model/test_triplegs.py
index a94fb5c..af59f5d 100644
--- a/tests/model/test_triplegs.py
+++ b/tests/model/test_triplegs.py
@@ -1,6 +1,7 @@
 import os
 
 import pytest
+import geopandas as gpd
 from shapely.geometry import Point
 
 import trackintel as ti
@@ -13,8 +14,8 @@ def testdata_tpls():
     pfs, _ = ti.io.dataset_reader.read_geolife(os.path.join("tests", "data", "geolife"))
     pfs, sp = pfs.as_positionfixes.generate_staypoints(method="sliding", dist_threshold=25, time_threshold=5)
     _, tpls = pfs.as_positionfixes.generate_triplegs(sp, method="between_staypoints")
-
-    return tpls
+    # we want test the accessor of GeoDataFrames and not Triplegs
+    return gpd.GeoDataFrame(tpls)
 
 
 class TestTriplegs:
diff --git a/tests/model/test_trips.py b/tests/model/test_trips.py
index dbf1633..d713b86 100644
--- a/tests/model/test_trips.py
+++ b/tests/model/test_trips.py
@@ -1,9 +1,9 @@
 import os
 import pytest
+import geopandas as gpd
 from shapely.geometry import Point
 
 import trackintel as ti
-from trackintel import TripsDataFrame, TripsGeoDataFrame
 
 
 @pytest.fixture
@@ -11,7 +11,8 @@ def testdata_trips():
     """Read trips test data from file."""
     path = os.path.join("tests", "data", "geolife_long", "trips.csv")
     test_trips = ti.read_trips_csv(path, index_col="id", geom_col="geom")
-    return test_trips
+    # we want test the accessor of GeoDataFrames and not Trips
+    return gpd.GeoDataFrame(test_trips)
 
 
 class TestTrips:
diff --git a/tests/preprocessing/test_positionfixes.py b/tests/preprocessing/test_positionfixes.py
index e8c6f71..579570d 100644
--- a/tests/preprocessing/test_positionfixes.py
+++ b/tests/preprocessing/test_positionfixes.py
@@ -302,6 +302,12 @@ class TestGenerate_staypoints:
         with pytest.warns(UserWarning, match=warn_string):
             pfs, sp = pfs.as_positionfixes.generate_staypoints()
 
+    def test_sp_type(self):
+        """Test if sp are really Staypoints"""
+        pfs, _ = ti.io.dataset_reader.read_geolife(os.path.join("tests", "data", "geolife_long"))
+        _, sp = pfs.generate_staypoints()
+        assert isinstance(sp, ti.Staypoints)
+
 
 class Test_Generate_staypoints_sliding_user:
     """Test for _generate_staypoints_sliding_user."""
@@ -554,3 +560,8 @@ class TestGenerate_triplegs:
         # set user ID to string
         pfs["user_id"] = pfs["user_id"].astype(str) + "not_numerical_interpretable_str"
         pfs, _ = pfs.as_positionfixes.generate_triplegs()
+
+    def test_tpls_type(self, example_positionfixes_isolated):
+        """Test that Tripleg generation returns correct type"""
+        _, tpls = example_positionfixes_isolated.as_positionfixes.generate_triplegs()
+        assert isinstance(tpls, ti.Triplegs)
diff --git a/tests/preprocessing/test_staypoints.py b/tests/preprocessing/test_staypoints.py
index e21790a..579b145 100644
--- a/tests/preprocessing/test_staypoints.py
+++ b/tests/preprocessing/test_staypoints.py
@@ -478,6 +478,13 @@ class TestGenerate_locations:
         with pytest.raises(KeyError, match=msg):
             example_staypoints.as_staypoints.generate_locations(activities_only=True)
 
+    def test_location_type(self, example_staypoints):
+        """Test that returned locations is of type Locations"""
+        _, locs = example_staypoints.as_staypoints.generate_locations(
+            method="dbscan", epsilon=10, num_samples=2, distance_metric="haversine", agg_level="dataset"
+        )
+        assert isinstance(locs, ti.Locations)
+
 
 class TestMergeStaypoints:
     def test_merge_staypoints(self, example_staypoints_merge):
diff --git a/tests/preprocessing/test_triplegs.py b/tests/preprocessing/test_triplegs.py
index 45e2a33..af99006 100644
--- a/tests/preprocessing/test_triplegs.py
+++ b/tests/preprocessing/test_triplegs.py
@@ -389,6 +389,12 @@ class TestGenerate_trips:
         with pytest.raises(AssertionError, match=error_msg):
             generate_trips(sp, tpls)
 
+    def test_trips_type(self, example_triplegs):
+        """Test if trips are really Trips"""
+        sp, tpls = example_triplegs
+        _, _, trips = generate_trips(sp, tpls)
+        assert isinstance(trips, ti.TripsGeoDataFrame)
+
 
 def _create_debug_sp_tpls_data(sp, tpls, gap_threshold):
     """Preprocess sp and tpls for "test_generate_trips_*."""
diff --git a/tests/preprocessing/test_trips.py b/tests/preprocessing/test_trips.py
index e1b830d..fbb7c0d 100644
--- a/tests/preprocessing/test_trips.py
+++ b/tests/preprocessing/test_trips.py
@@ -306,6 +306,12 @@ class TestGenerate_tours:
             _ = ti.preprocessing.trips.generate_tours(trips, max_time=24)
             assert e_info == "Parameter max_time must be either of type String or pd.Timedelta!"
 
+    def test_tours_type(self, example_trip_data):
+        """Test if retuned tours are of type Tours"""
+        trips, _ = example_trip_data
+        _, tours = trips.as_trips.generate_tours()
+        assert isinstance(tours, ti.Tours)
+
 
 class TestTourHelpers:
     """Test auxiliary function for trip grouping"""