# Movement data in GIS #17: spatial analysis of GeoPandas trajectories

In Movement data in GIS #16, I presented a new way to deal with trajectory data using GeoPandas and how to load the trajectory GeoDataframes as a QGIS layer. Following up on this initial experiment, I’ve now implemented a first version of an algorithm that performs a spatial analysis on my GeoPandas trajectories.

The first spatial analysis algorithm I’ve implemented is **Clip trajectories by extent**. Implementing this algorithm revealed a couple of pitfalls:

- To achieve correct results, we need to compute spatial intersections between
**linear trajectory segments**and the extent. Therefore, we need to convert our point GeoDataframe to a line GeoDataframe. - Based on the spatial intersection, we need to take care of
**computing the corresponding timestamps**of the events when trajectories enter or leave the extent. - A trajectory can
**intersect the extent multiple times**. Therefore, we cannot simply use the global minimum and maximum timestamp of intersecting segments. - GeoPandas provides spatial intersection functionality but if the trajectory contains consecutive rows without location change, these will result in
**zero length lines**and those cause an empty intersection result.

So far, the clip result only contains the trajectory id plus a suffix indicating the sequence of the intersection segments for a specific trajectory (because one trajectory can intersect the extent multiple times). The following screenshot shows one highlighted trajectory that intersects the extent three times and the resulting clipped trajectories:

This algorithm together with the basic trajectory from points algorithm is now available in a Processing algorithm provider plugin called Processing Trajectory.

Note: This plugin depends on GeoPandas.

Note for Windows users: GeoPandas is not a standard package that is available in OSGeo4W, so you’ll have to install it manually. (For the necessary steps, see this answer on gis.stackexchange.com)

The implemented tests show how to use the Trajectory class independently of QGIS. So far, I’m only testing the spatial properties though:

def test_two_intersections_with_same_polygon(self): polygon = Polygon([(5,-5),(7,-5),(7,12),(5,12),(5,-5)]) data = [{'id':1, 'geometry':Point(0,0), 't':datetime(2018,1,1,12,0,0)}, {'id':1, 'geometry':Point(6,0), 't':datetime(2018,1,1,12,10,0)}, {'id':1, 'geometry':Point(10,0), 't':datetime(2018,1,1,12,15,0)}, {'id':1, 'geometry':Point(10,10), 't':datetime(2018,1,1,12,30,0)}, {'id':1, 'geometry':Point(0,10), 't':datetime(2018,1,1,13,0,0)}] df = pd.DataFrame(data).set_index('t') geo_df = GeoDataFrame(df, crs={'init': '31256'}) traj = Trajectory(1, geo_df) intersections = traj.intersection(polygon) result = [] for x in intersections: result.append(x.to_linestring()) expected_result = [LineString([(5,0),(6,0),(7,0)]), LineString([(7,10),(5,10)])] self.assertEqual(result, expected_result)

One issue with implementing the algorithms as QGIS Processing tools in this way is that the tools are independent of one another. That means that each tool has to repeat the expensive step of creating the trajectory objects in memory. I’m not sure this can be solved.