Repository: mie-lab/trackintel
Pull request #284 (2021-07-22 12:28:53)

ENH: test data guidelines


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 02f62d3..9cf8e55 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -38,13 +38,20 @@ This is a place to collect conventions we agreed upon until we find the right pl
 ### Time stamps
 All timestamps are timezone aware pandas `datetime64[ns, UTC]` objects. The default timezone should be `UTC` but the user should be free to use a different one if he wants. See [Issue 101](https://github.com/mie-lab/trackintel/issues/18). 
 
-### Organization of tests
+### Tests 
+#### Organization of tests
 See [Issue 23](https://github.com/mie-lab/trackintel/issues/23)
 - The test folder copies the folder structure that the trackintel.trackintel folder has.
 - Every python module has a single test file
 - Every function has 1 test class
 - Every method of this function should test a single property
 
+#### Test data
+If possible test data should be
+- independent of unrelated preprocessing steps (e.g., avoid starting with positionfixes if you write tests for trips)
+- simple and easy to understand (e.g., try to have a short example with an isolated special case rather than a large dataset that contains a lot of special cases)
+- defined directly in the code itself (e.g, [this example](https://github.com/mie-lab/trackintel/blob/e0c0cdd0d8472ba7b113b3819d062ea8abcd8168/tests/io/test_postgis_gpd.py#L50)
+
 ### Integrety of input data
 Functions should never change the input dataframe but rather return an altered copy.
 
@@ -66,7 +73,7 @@ See [issue 117](https://github.com/mie-lab/trackintel/issues/117)
   - Staypoints: `GeoDataFrame (as trackintel staypoints)`
   - Triplegs: `GeoDataFrame (as trackintel triplegs)`
   - Locations: `GeoDataFrame (as trackintel locations)`  
-  - Trips: `DataFrame (as trackintel trips)`
+  - Trips: `(Geo)DataFrame (as trackintel trips)`
   - Tours: `DataFrame (as trackintel tours)` 
 
 ### Others
diff --git a/docs/conf.py b/docs/conf.py
index 622e5fb..1d160d8 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -74,7 +74,7 @@ def setup(app):
 # The full version, including alpha/beta/rc tags
 # release = version
 
-version = "1.1.1"
+version = "1.1.2"
 release = version
 
 
diff --git a/trackintel/__version__.py b/trackintel/__version__.py
index 5b3343a..e851482 100644
--- a/trackintel/__version__.py
+++ b/trackintel/__version__.py
@@ -1,3 +1,3 @@
-VERSION = (1, 1, 1)
+VERSION = (1, 1, 2)
 
 __version__ = ".".join(map(str, VERSION))
diff --git a/trackintel/io/file.py b/trackintel/io/file.py
index 363f93a..1681a2e 100644
--- a/trackintel/io/file.py
+++ b/trackintel/io/file.py
@@ -1,4 +1,5 @@
 import warnings
+from geopandas.geodataframe import GeoDataFrame
 
 import numpy as np
 import dateutil
@@ -8,6 +9,7 @@ import pandas as pd
 import pytz
 import warnings
 from shapely import wkt
+from shapely import geometry
 from shapely.geometry import Point
 
 
@@ -424,6 +426,7 @@ def read_trips_csv(*args, columns=None, tz=None, index_col=object(), **kwargs):
         The column names to rename in the format {'old_name':'trackintel_standard_name'}.
         The required columns for this function include: "user_id", "started_at",
         "finished_at", "origin_staypoint_id" and "destination_staypoint_id".
+        An optional column is "geom" of type MultiPoint, containing start and destination points of the trip
 
     tz : str, optional
         pytz compatible timezone string. If None UTC is assumed.
@@ -434,8 +437,8 @@ def read_trips_csv(*args, columns=None, tz=None, index_col=object(), **kwargs):
 
     Returns
     -------
-    trips : DataFrame (as trackintel trips)
-        A DataFrame containing the trips.
+    trips : (Geo)DataFrame (as trackintel trips)
+        A DataFrame containing the trips. GeoDataFrame if geometry column exists.
 
     Notes
     -----
@@ -445,10 +448,14 @@ def read_trips_csv(*args, columns=None, tz=None, index_col=object(), **kwargs):
     --------
     >>> trackintel.read_trips_csv('data.csv')
     >>> trackintel.read_trips_csv('data.csv', columns={'start_time':'started_at', 'User':'user_id'})
-        user_id                started_at               finished_at  origin_staypoint_id  destination_staypoint_id
+        user_id                started_at               finished_at  origin_staypoint_id  destination_staypoint_id\
     id
     0         1 2015-11-27 08:00:00+00:00 2015-11-27 08:15:00+00:00                    2                         5
     1         1 2015-11-27 08:20:22+00:00 2015-11-27 08:35:22+00:00                    5                         3
+                                geom  
+    id                                                     
+    0   MULTIPOINT (116.31842 39.98470, 116.29873 39.999729)
+    1   MULTIPOINT (116.29873 39.98402, 116.32480 40.009269)
     """
     columns = {} if columns is None else columns
 
@@ -472,6 +479,11 @@ def read_trips_csv(*args, columns=None, tz=None, index_col=object(), **kwargs):
         if not pd.api.types.is_datetime64tz_dtype(trips[col]):
             trips[col] = _localize_timestamp(dt_series=trips[col], pytz_tzinfo=tz, col_name=col)
 
+    # convert to geodataframe
+    if "geom" in trips.columns:
+        trips["geom"] = trips["geom"].apply(wkt.loads)
+        trips = gpd.GeoDataFrame(trips, geometry="geom")
+
     # assert validity of trips
     trips.as_trips
     return trips
@@ -485,13 +497,16 @@ def write_trips_csv(trips, filename, *args, **kwargs):
 
     Parameters
     ----------
-    trips : DataFrame (as trackintel trips)
+    trips : (Geo)DataFrame (as trackintel trips)
         The trips to store to the CSV file.
 
     filename : str
         The file to write to.
     """
     df = trips.copy()
+    if isinstance(df, GeoDataFrame):
+        geom_col_name = df.geometry.name
+        df[geom_col_name] = df[geom_col_name].apply(wkt.dumps)
     df.to_csv(filename, index=True, *args, **kwargs)
 
 
diff --git a/trackintel/model/trips.py b/trackintel/model/trips.py
index 73abb1c..3d57f22 100644
--- a/trackintel/model/trips.py
+++ b/trackintel/model/trips.py
@@ -3,6 +3,7 @@ from trackintel.io.postgis import write_trips_postgis
 from trackintel.io.file import write_trips_csv
 from trackintel.model.util import copy_docstring
 import pandas as pd
+import geopandas as gpd
 
 import trackintel as ti
 
@@ -17,7 +18,9 @@ class TripsAccessor(object):
     Requires at least the following columns:
     ['user_id', 'started_at', 'finished_at', 'origin_staypoint_id', 'destination_staypoint_id']
 
-    The 'index' of the GeoDataFrame will be treated as unique identifier of the `Trips`
+    The 'index' of the (Geo)DataFrame will be treated as unique identifier of the `Trips`
+
+    Trips have an optional geometry of type MultiPoint which describes the start and the end point of the trip
 
     For several usecases, the following additional columns are required:
     ['context', 'origin_activity', 'destination_activity', 'modes', 'primary_mode', 'tour_id']
@@ -25,15 +28,13 @@ class TripsAccessor(object):
     Notes
     -----
     `Trips` are an aggregation level in transport planning that summarize all movement and all non-essential actions
-    (e.g., waiting) between two relevant activities.
-
-    The following assumptions are implemented
-
-        - All movement before the first and after the last activity is omitted
+    (e.g., waiting) between two relevant activities. The following assumptions are implemented
         - If we do not record a person for more than `gap_threshold` minutes, we assume that the person performed an \
-            activity in the recording gap and split the trip at the gap.
-        - Trips that start/end in a recording gap can have an unknown origin/destination.
-        - There are no trips without a (recored) tripleg.
+          activity in the recording gap and split the trip at the gap.
+        - Trips that start/end in a recording gap can have an unknown origin/destination staypoint id.
+        - If the origin (or destination) staypoint is unknown (and a geometry column exists), the origin/destination
+          geometry is set as the first coordinate of the first tripleg (or the last coordinate of the last tripleg)
+        - There are no trips without a (recorded) tripleg.
 
     'started_at' and 'finished_at' are timezone aware pandas datetime objects.
 
@@ -65,6 +66,15 @@ class TripsAccessor(object):
             obj["finished_at"]
         ), "dtype of finished_at is {} but has to be datetime64 and timezone aware".format(obj["finished_at"].dtype)
 
+        # Check geometry if Trips is a GeoDataFrame
+        if isinstance(obj, gpd.GeoDataFrame):
+            # check geometry
+            assert obj.geometry.is_valid.all(), (
+                "Not all geometries are valid. Try x[~ x.geometry.is_valid] " "where x is you GeoDataFrame"
+            )
+            if obj.geometry.iloc[0].geom_type != "MultiPoint":
+                raise AttributeError("The geometry must be a MultiPoint (only first checked).")
+
     def plot(self, *args, **kwargs):
         """
         Plot this collection of trips.
diff --git a/trackintel/preprocessing/triplegs.py b/trackintel/preprocessing/triplegs.py
index ef18e06..776c3b3 100644
--- a/trackintel/preprocessing/triplegs.py
+++ b/trackintel/preprocessing/triplegs.py
@@ -2,6 +2,10 @@ import warnings
 
 import numpy as np
 import pandas as pd
+from shapely import geometry
+from tqdm import tqdm
+from shapely.geometry import MultiPoint, Point
+import geopandas as gpd
 
 
 def smoothen_triplegs(triplegs, tolerance=1.0, preserve_topology=True):
@@ -36,7 +40,7 @@ def smoothen_triplegs(triplegs, tolerance=1.0, preserve_topology=True):
     return ret_tpls
 
 
-def generate_trips(spts, tpls, gap_threshold=15):
+def generate_trips(spts, tpls, gap_threshold=15, add_geometry=True):
     """Generate trips based on staypoints and triplegs.
 
     Parameters
@@ -49,6 +53,10 @@ def generate_trips(spts, tpls, gap_threshold=15):
         Maximum allowed temporal gap size in minutes. If tracking data is missing for more than
         `gap_threshold` minutes, then a new trip begins after the gap.
 
+    add_geometry : bool default True
+        If True, the start and end coordinates of each trip are added to the output table in a geometry column "geom"
+        of type MultiPoint. Set `add_geometry=False` for better runtime performance (if coordinates are not required).
+
     print_progress : bool, default False
         If print_progress is True, the progress bar is displayed
 
@@ -60,7 +68,7 @@ def generate_trips(spts, tpls, gap_threshold=15):
     triplegs: GeoDataFrame (as trackintel triplegs)
         The original triplegs with a new column ``[`trip_id`]``.
 
-    trips: GeoDataFrame (as trackintel trips)
+    trips: (Geo)DataFrame (as trackintel trips)
         The generated trips.
 
     Notes
@@ -74,7 +82,12 @@ def generate_trips(spts, tpls, gap_threshold=15):
         - If we do not record a person for more than `gap_threshold` minutes,
           we assume that the person performed an activity in the recording gap and split the trip at the gap.
         - Trips that start/end in a recording gap can have an unknown origin/destination
-        - There are no trips without a (recored) tripleg
+        - There are no trips without a (recorded) tripleg
+        - Trips optionally have their start and end point as geometry of type MultiPoint, if `add_geometry==True`
+        - If the origin (or destination) staypoint is unknown, and `add_geometry==True`, the origin (and destination)
+          geometry is set as the first coordinate of the first tripleg (or the last coordinate of the last tripleg),
+          respectively. Trips with missing values can still be identified via col `origin_staypoint_id`.
+
 
     Examples
     --------
@@ -114,6 +127,9 @@ def generate_trips(spts, tpls, gap_threshold=15):
             tpls[["started_at", "finished_at", "user_id", "type"]],
         ]
     )
+    if add_geometry:
+        spts_tpls["geom"] = pd.concat([spts.geometry, tpls.geometry])
+
     # transform nan to bool
     spts_tpls["activity"].fillna(False, inplace=True)
 
@@ -193,6 +209,11 @@ def generate_trips(spts, tpls, gap_threshold=15):
     trips_with_act["origin_staypoint_id"] = trips_with_act["spts_tpls_id"].shift(1)
     trips_with_act["destination_staypoint_id"] = trips_with_act["spts_tpls_id"].shift(-1)
 
+    # add geometry for start and end points
+    if add_geometry:
+        trips_with_act["origin_geom"] = trips_with_act["geom"].shift(1)
+        trips_with_act["destination_geom"] = trips_with_act["geom"].shift(-1)
+
     # add prev_trip_id and next_trip_id for activity staypoints
     trips_with_act["prev_trip_id"] = trips_with_act["trip_id"].shift(1)
     trips_with_act["next_trip_id"] = trips_with_act["trip_id"].shift(-1)
@@ -218,6 +239,28 @@ def generate_trips(spts, tpls, gap_threshold=15):
         axis=1,
     )
 
+    # fill missing points and convert to MultiPoint
+    # for all trips with missing 'origin_staypoint_id' we now assign the startpoint of the first tripleg of the trip.
+    # for all tripls with missing 'destination_staypoint_id' we now assign the endpoint of the last tripleg of the trip.
+    if add_geometry:
+        # fill geometry for origin staypoints that are NaN
+        origin_nan_rows = trips[pd.isna(trips["origin_staypoint_id"])].copy()
+        trips.loc[pd.isna(trips["origin_staypoint_id"]), "origin_geom"] = origin_nan_rows.tpls.map(
+            # from tpls table, get the first point of the first tripleg for the trip
+            lambda x: tpls.loc[x[0], tpls.geometry.name].boundary[0]
+        )
+        # fill geometry for destionations staypoints that are NaN
+        destination_nan_rows = trips[pd.isna(trips["destination_staypoint_id"])].copy()
+        trips.loc[pd.isna(trips["destination_staypoint_id"]), "destination_geom"] = destination_nan_rows.tpls.map(
+            # from tpls table, get the last point of the last tripleg on the trip
+            lambda x: tpls.loc[x[-1], tpls.geometry.name].boundary[1]
+        )
+        # convert to GeoDataFrame with MultiPoint column
+        trips["geom"] = [MultiPoint([x, y]) for x, y in zip(trips.origin_geom, trips.destination_geom)]
+        trips = gpd.GeoDataFrame(trips, geometry="geom")
+        # cleanup
+        trips.drop(["origin_geom", "destination_geom"], inplace=True, axis=1)
+
     # now handle the data that is aggregated in the trips
     # assign trip_id to tpls
     temp = trips.explode("tpls")


Test patch:
diff --git a/tests/data/geolife_long/trips.csv b/tests/data/geolife_long/trips.csv
index 6a4ea07..e910b17 100644
--- a/tests/data/geolife_long/trips.csv
+++ b/tests/data/geolife_long/trips.csv
@@ -1,15 +1,15 @@
-id,user_id,started_at,finished_at,origin_staypoint_id,destination_staypoint_id
-0,0,2008-10-23 02:53:04+00:00,2008-10-23 03:05:00+00:00,,0.0
-1,0,2008-10-23 04:08:07+00:00,2008-10-23 04:34:37+00:00,0.0,1.0
-2,0,2008-10-23 09:42:25+00:00,2008-10-23 10:45:41+00:00,1.0,5.0
-3,0,2008-10-23 11:09:22+00:00,2008-10-23 11:10:57+00:00,5.0,6.0
-4,0,2008-10-24 02:09:59+00:00,2008-10-24 02:47:06+00:00,6.0,
-5,1,2008-10-23 05:53:05+00:00,2008-10-23 06:01:42+00:00,,10.0
-6,1,2008-10-23 10:32:53+00:00,2008-10-23 11:10:19+00:00,10.0,11.0
-7,1,2008-10-23 11:49:08+00:00,2008-10-23 11:49:48+00:00,11.0,12.0
-8,1,2008-10-23 23:43:46+00:00,2008-10-24 00:23:03+00:00,12.0,14.0
-9,1,2008-10-24 01:45:41+00:00,2008-10-24 02:02:46+00:00,14.0,15.0
-10,1,2008-10-24 02:28:19+00:00,2008-10-24 02:31:32+00:00,15.0,16.0
-11,1,2008-10-24 03:16:35+00:00,2008-10-24 04:12:50+00:00,16.0,20.0
-12,1,2008-10-24 05:28:05+00:00,2008-10-24 05:39:50+00:00,20.0,21.0
-13,1,2008-10-24 06:08:42+00:00,2008-10-24 06:35:50+00:00,21.0,
+id,user_id,started_at,finished_at,origin_staypoint_id,destination_staypoint_id,geom
+0,0,2008-10-23 02:53:04+00:00,2008-10-23 03:05:00+00:00,,0.0,"MULTIPOINT (116.3184169999999966 39.9847019999999986, 116.2987250000000046 39.9840190000000035)"
+1,0,2008-10-23 04:08:07+00:00,2008-10-23 04:34:37+00:00,0.0,1.0,"MULTIPOINT (116.2987250000000046 39.9840190000000035, 116.3248009999999937 39.9997290000000021)"
+2,0,2008-10-23 09:42:25+00:00,2008-10-23 10:45:41+00:00,1.0,5.0,"MULTIPOINT (116.3248009999999937 39.9997290000000021, 116.3216824999999943 40.0089049999999986)"
+3,0,2008-10-23 11:09:22+00:00,2008-10-23 11:10:57+00:00,5.0,6.0,"MULTIPOINT (116.3216824999999943 40.0089049999999986, 116.3209779999999967 40.0092690000000033)"
+4,0,2008-10-24 02:09:59+00:00,2008-10-24 02:47:06+00:00,6.0,,"MULTIPOINT (116.3209779999999967 40.0092690000000033, 116.3211620000000011 40.0092089999999985)"
+5,1,2008-10-23 05:53:05+00:00,2008-10-23 06:01:42+00:00,,10.0,"MULTIPOINT (116.3192360000000036 39.9840939999999989, 116.3275380000000041 39.9780510000000007)"
+6,1,2008-10-23 10:32:53+00:00,2008-10-23 11:10:19+00:00,10.0,11.0,"MULTIPOINT (116.3275380000000041 39.9780510000000007, 116.3064135000000050 40.0138019999999983)"
+7,1,2008-10-23 11:49:08+00:00,2008-10-23 11:49:48+00:00,11.0,12.0,"MULTIPOINT (116.3064135000000050 40.0138019999999983, 116.3065320000000042 40.0138200000000026)"
+8,1,2008-10-23 23:43:46+00:00,2008-10-24 00:23:03+00:00,12.0,14.0,"MULTIPOINT (116.3065320000000042 40.0138200000000026, 116.3266370000000052 39.9779920000000004)"
+9,1,2008-10-24 01:45:41+00:00,2008-10-24 02:02:46+00:00,14.0,15.0,"MULTIPOINT (116.3266370000000052 39.9779920000000004, 116.3084029999999984 39.9810390000000027)"
+10,1,2008-10-24 02:28:19+00:00,2008-10-24 02:31:32+00:00,15.0,16.0,"MULTIPOINT (116.3084029999999984 39.9810390000000027, 116.3101889999999941 39.9813740000000024)"
+11,1,2008-10-24 03:16:35+00:00,2008-10-24 04:12:50+00:00,16.0,20.0,"MULTIPOINT (116.3101889999999941 39.9813740000000024, 116.3267620000000022 39.9779330000000002)"
+12,1,2008-10-24 05:28:05+00:00,2008-10-24 05:39:50+00:00,20.0,21.0,"MULTIPOINT (116.3267620000000022 39.9779330000000002, 116.3112610000000018 39.9827119999999994)"
+13,1,2008-10-24 06:08:42+00:00,2008-10-24 06:35:50+00:00,21.0,,"MULTIPOINT (116.3112610000000018 39.9827119999999994, 116.3270629999999954 39.9778990000000007)"
diff --git a/tests/data/trips_mod_columns.csv b/tests/data/trips_mod_columns.csv
index 92bffb5..05f507a 100644
--- a/tests/data/trips_mod_columns.csv
+++ b/tests/data/trips_mod_columns.csv
@@ -1,5 +1,5 @@
-id;user_id;started_at;finished_at;orig_stp;dest_stp
-0;1;2015-11-27T08:00:00Z;2015-11-27T08:15:00Z;2;5
-1;1;2015-11-27T08:20:22Z;2015-11-27T08:35:22Z;2;5
-2;1;2015-11-27T12:39:28Z;2015-11-27T12:55:28Z;2;5
-3;1;2015-11-27T13:57:20Z;2015-11-27T14:13:20Z;2;5
\ No newline at end of file
+id;user_id;started_at;finished_at;orig_stp;dest_stp;geom
+0;1;2015-11-27T08:00:00Z;2015-11-27T08:15:00Z;2;5;MULTIPOINT (116.3184169999999966 39.9847019999999986, 116.2987250000000046 39.9840190000000035)
+1;1;2015-11-27T08:20:22Z;2015-11-27T08:35:22Z;2;5;MULTIPOINT (116.3184169999999966 39.9847019999999986, 116.2987250000000046 39.9840190000000035)
+2;1;2015-11-27T12:39:28Z;2015-11-27T12:55:28Z;2;5;MULTIPOINT (116.3184169999999966 39.9847019999999986, 116.2987250000000046 39.9840190000000035)
+3;1;2015-11-27T13:57:20Z;2015-11-27T14:13:20Z;2;5;MULTIPOINT (116.3184169999999966 39.9847019999999986, 116.2987250000000046 39.9840190000000035)
diff --git a/tests/io/test_file.py b/tests/io/test_file.py
index aee483e..67e027b 100644
--- a/tests/io/test_file.py
+++ b/tests/io/test_file.py
@@ -264,7 +264,8 @@ class TestTrips:
         trips = ti.read_trips_csv(orig_file, sep=";", index_col="id")
         column_mapping = {"orig_stp": "origin_staypoint_id", "dest_stp": "destination_staypoint_id"}
         mod_trips = ti.read_trips_csv(mod_file, columns=column_mapping, sep=";", index_col="id")
-        assert mod_trips.equals(trips)
+        mod_trips_wo_geom = pd.DataFrame(mod_trips.drop(columns=["geom"]))
+        assert mod_trips_wo_geom.equals(trips)
 
         trips["started_at"] = trips["started_at"].apply(lambda d: d.isoformat().replace("+00:00", "Z"))
         trips["finished_at"] = trips["finished_at"].apply(lambda d: d.isoformat().replace("+00:00", "Z"))
diff --git a/tests/io/test_postgis_gpd.py b/tests/io/test_postgis_gpd.py
index f4ab5ec..4d69257 100644
--- a/tests/io/test_postgis_gpd.py
+++ b/tests/io/test_postgis_gpd.py
@@ -154,7 +154,7 @@ def example_trips():
             "destination_staypoint_id": 1,
         },
     ]
-    trips = gpd.GeoDataFrame(data=list_dict)
+    trips = pd.DataFrame(data=list_dict)
     trips.index.name = "id"
     assert trips.as_trips
     return trips
diff --git a/tests/model/test_trips.py b/tests/model/test_trips.py
index 44c1dcc..f2b87ab 100644
--- a/tests/model/test_trips.py
+++ b/tests/model/test_trips.py
@@ -1,17 +1,46 @@
 import os
 import pytest
+from shapely.geometry import Point, MultiPoint
 
 import trackintel as ti
 
 
+@pytest.fixture
+def testdata_trips():
+    """Read trips test data from file."""
+    test_trips = ti.read_trips_csv(os.path.join("tests", "data", "geolife_long", "trips.csv"), index_col="id")
+    return test_trips
+
+
 class TestTrips:
     """Tests for the TripsAccessor."""
 
-    def test_accessor(self):
+    def test_accessor(self, testdata_trips):
         """Test if the as_trips accessor checks the required column for trips."""
-        trips = ti.read_trips_csv(os.path.join("tests", "data", "geolife_long", "trips.csv"), index_col="id")
+        trips = testdata_trips.copy()
         assert trips.as_trips
 
         # user_id
         with pytest.raises(AttributeError):
             trips.drop(["user_id"], axis=1).as_trips
+
+    def test_accessor_geometry_type(self, testdata_trips):
+        """Test if the as_trips accessor requires MultiPoint geometry."""
+        trips_wrong_geom = testdata_trips.copy()
+
+        # check geometry type
+        with pytest.raises(AttributeError, match="The geometry must be a MultiPoint"):
+            trips_wrong_geom["geom"] = Point([(13.476808430, 48.573711823)])
+            trips_wrong_geom.as_trips
+
+    def test_accessor_geometry_name(self, testdata_trips):
+        """Test that it also works with a different geometry column name"""
+        trips_other_geom_name = testdata_trips.copy()
+
+        trips_other_geom_name["other_geom"] = Point(13.476808430, 48.573711823)
+        trips_other_geom_name.set_geometry("other_geom", inplace=True)
+        trips_other_geom_name.drop(columns=["geom"], inplace=True)
+
+        # check that it works with other geometry name
+        with pytest.raises(AttributeError, match="The geometry must be a MultiPoint"):
+            trips_other_geom_name.as_trips
diff --git a/tests/preprocessing/test_triplegs.py b/tests/preprocessing/test_triplegs.py
index 4a7aa1d..58f5c55 100644
--- a/tests/preprocessing/test_triplegs.py
+++ b/tests/preprocessing/test_triplegs.py
@@ -7,6 +7,7 @@ import pandas as pd
 import pytest
 from geopandas.testing import assert_geodataframe_equal
 from pandas.testing import assert_frame_equal, assert_series_equal
+from shapely import geometry
 from shapely.geometry import LineString, Point
 
 import trackintel as ti
@@ -65,8 +66,72 @@ class TestGenerate_trips:
 
         # generate trips and a joint staypoint/triplegs dataframe
         stps, tpls, trips = generate_trips(stps, tpls, gap_threshold=15)
+        trips = trips[
+            ["user_id", "started_at", "finished_at", "origin_staypoint_id", "destination_staypoint_id", "geom"]
+        ]
+        # test if generated trips are equal
+        assert_geodataframe_equal(trips_loaded, trips)
+
+    def test_trip_wo_geom(self):
+        """Test if the add_geometry parameter shows correct behavior"""
+        # create trips from geolife (based on positionfixes)
+        pfs, _ = ti.io.dataset_reader.read_geolife(os.path.join("tests", "data", "geolife_long"))
+        pfs, stps = pfs.as_positionfixes.generate_staypoints(
+            method="sliding", dist_threshold=25, time_threshold=5, gap_threshold=1e6
+        )
+        stps = stps.as_staypoints.create_activity_flag(time_threshold=15)
+        pfs, tpls = pfs.as_positionfixes.generate_triplegs(stps)
+
+        # generate trips dataframe with geometry
+        _, _, trips = generate_trips(stps, tpls, gap_threshold=15)
+        trips = pd.DataFrame(trips.drop(["geom"], axis=1))
+
+        # generate trips without geometry
+        _, _, trips_wo_geom = generate_trips(stps, tpls, gap_threshold=15, add_geometry=False)
+
         # test if generated trips are equal
-        assert_frame_equal(trips_loaded, trips)
+        assert_frame_equal(trips_wo_geom, trips)
+
+    def test_trip_coordinates(self):
+        """Test if coordinates of start and destination are correct"""
+        # create trips from geolife (based on positionfixes)
+        pfs, _ = ti.io.dataset_reader.read_geolife(os.path.join("tests", "data", "geolife_long"))
+        pfs, stps = pfs.as_positionfixes.generate_staypoints(
+            method="sliding", dist_threshold=25, time_threshold=5, gap_threshold=1e6
+        )
+        stps = stps.as_staypoints.create_activity_flag(time_threshold=15)
+        pfs, tpls = pfs.as_positionfixes.generate_triplegs(stps)
+
+        # generate trips and a joint staypoint/triplegs dataframe
+        stps, tpls, trips = ti.preprocessing.triplegs.generate_trips(stps, tpls, gap_threshold=15)
+
+        # Check start and destination points of all rows
+        for i, row in trips.iterrows():
+            start_point_trips = row["geom"][0]  # get origin Point in generated trips
+            if not pd.isna(row["origin_staypoint_id"]):
+                # compare to the Point in the staypoints
+                correct_start_point = stps.loc[row["origin_staypoint_id"], "geom"]
+            else:
+                # check if it is the first point of the tripleg
+                # get all triplegs on this trip
+                tpls_on_trip = tpls[tpls["trip_id"] == row.name]
+                # correct point is the first point on the tripleg
+                correct_start_point, _ = tpls_on_trip.iloc[0]["geom"].boundary
+
+            assert correct_start_point == start_point_trips
+
+            dest_point_trips = row["geom"][1]  # get destination Point in generated trips
+            if not pd.isna(row["destination_staypoint_id"]):
+                correct_dest_point = stps.loc[row["destination_staypoint_id"], "geom"]
+                # compare to the Point in the staypoints
+            else:
+                # check if it is the last point of the tripleg
+                # get all triplegs on this trip
+                tpls_on_trip = tpls[tpls["trip_id"] == row.name]
+                # correct point is the first point on the tripleg
+                _, correct_dest_point = tpls_on_trip.iloc[-1]["geom"].boundary
+
+            assert correct_dest_point == dest_point_trips
 
     def test_accessor(self):
         """Test if the accessor leads to the same results as the explicit function."""
@@ -86,7 +151,7 @@ class TestGenerate_trips:
         stps_acc, tpls_acc, trips_acc = tpls.as_triplegs.generate_trips(stps, gap_threshold=15)
 
         # test if generated trips are equal
-        pd.testing.assert_frame_equal(trips_expl, trips_acc)
+        assert_geodataframe_equal(trips_expl, trips_acc)
         assert_geodataframe_equal(stps_expl, stps_acc)
         assert_geodataframe_equal(tpls_expl, tpls_acc)
 
@@ -114,7 +179,7 @@ class TestGenerate_trips:
         # test if generated trips are equal (1,2)
         assert_geodataframe_equal(spts_1, spts_2)
         assert_geodataframe_equal(tpls_1, tpls_2)
-        pd.testing.assert_frame_equal(trips_1, trips_2)
+        assert_geodataframe_equal(trips_1, trips_2)
 
     def test_generate_trips_missing_link(self):
         """Test nan is assigned for missing link between stps and trips, and tpls and trips."""
@@ -162,6 +227,7 @@ class TestGenerate_trips:
         # generate trips and a joint staypoint/triplegs dataframe
         stps, tpls, trips = generate_trips(stps, tpls, gap_threshold=15)
         stps_, tpls_, trips_ = _generate_trips_old(stps, tpls, gap_threshold=15)
+        trips.drop(columns=["geom"], inplace=True)
 
         # test if generated trips are equal
         # ignore column order and index dtype
@@ -226,7 +292,7 @@ class TestGenerate_trips:
         assert tpls_in.as_triplegs
 
         # load ground truth data
-        trips_loaded = pd.read_csv(os.path.join("tests", "data", "trips", "trips_gaps.csv"), index_col="id")
+        trips_loaded = ti.read_trips_csv(os.path.join("tests", "data", "trips", "trips_gaps.csv"), index_col="id")
         trips_loaded["started_at"] = pd.to_datetime(trips_loaded["started_at"], utc=True)
         trips_loaded["finished_at"] = pd.to_datetime(trips_loaded["finished_at"], utc=True)
 
@@ -236,11 +302,11 @@ class TestGenerate_trips:
         stps_tpls_loaded["finished_at"] = pd.to_datetime(stps_tpls_loaded["finished_at"], utc=True)
 
         # generate trips and a joint staypoint/triplegs dataframe
-        stps_proc, tpls_proc, trips = generate_trips(stps_in, tpls_in, gap_threshold=gap_threshold)
+        stps_proc, tpls_proc, trips = generate_trips(stps_in, tpls_in, gap_threshold=gap_threshold, add_geometry=False)
         stps_tpls = _create_debug_stps_tpls_data(stps_proc, tpls_proc, gap_threshold=gap_threshold)
 
         # test if generated trips are equal
-        assert_frame_equal(trips_loaded, trips)
+        pd.testing.assert_frame_equal(trips_loaded, trips)
 
         # test if generated staypoints/triplegs are equal (especially important for trip ids)
         assert_frame_equal(stps_tpls_loaded, stps_tpls, check_dtype=False)
@@ -287,7 +353,7 @@ class TestGenerate_trips:
         spts_tpls = pd.DataFrame(spts_tpls)
         spts = spts_tpls[spts_tpls["type"] == "staypoint"]
         tpls = spts_tpls[spts_tpls["type"] == "tripleg"]
-        spts_, tpls_, trips = generate_trips(spts, tpls)
+        spts_, tpls_, trips = generate_trips(spts, tpls, add_geometry=False)
         trip_id_truth = pd.Series([None, None, None, 0, None], dtype="Int64")
         trip_id_truth.index = spts_.index  # don't check index
         assert_series_equal(spts_["trip_id"], trip_id_truth, check_names=False)