From 574506837f5e04f6e49ff1cd99da28bc54fc30ba Mon Sep 17 00:00:00 2001 From: anitagraser Date: Sat, 14 Jan 2023 23:31:19 +0100 Subject: [PATCH] New param agg for to_traj_gdf --- .../tests/test_trajectory_collection.py | 34 +++++ movingpandas/trajectory.py | 12 +- movingpandas/trajectory_collection.py | 4 +- tutorials/6-trajectory-metrics.ipynb | 134 ++++++++++++++++++ 4 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 tutorials/6-trajectory-metrics.ipynb diff --git a/movingpandas/tests/test_trajectory_collection.py b/movingpandas/tests/test_trajectory_collection.py index cf9717d..3e3d1c7 100644 --- a/movingpandas/tests/test_trajectory_collection.py +++ b/movingpandas/tests/test_trajectory_collection.py @@ -291,3 +291,37 @@ def test_to_traj_gdf(self): expected_line_gdf = GeoDataFrame(df2, crs=CRS_METRIC) assert_frame_equal(traj_gdf, expected_line_gdf) + + def test_to_traj_gdf_aggregate(self): + temp_df = self.geo_df.drop(columns=["val2"]) + tc = TrajectoryCollection(temp_df, "id") + traj_gdf = tc.to_traj_gdf(agg={"obj": "mode", "val":["min", "max"]}) + print(traj_gdf) + rows = [ + { + "traj_id": 1, + "start_t": datetime(2018, 1, 1, 12, 0, 0), + "end_t": datetime(2018, 1, 1, 14, 15, 0), + "geometry": LineString([(0, 0), (6, 0), (6, 6), (9, 9)]), + "length": 12 + sqrt(18), + "direction": 45.0, + "obj_mode": "A", + "val_min": 2, + "val_max": 9 + }, + { + "traj_id": 2, + "start_t": datetime(2018, 1, 1, 12, 0, 0), + "end_t": datetime(2018, 1, 2, 13, 15, 0), + "geometry": LineString([(10, 10), (16, 10), (16, 16), (190, 10)]), + "length": 12 + sqrt(174 * 174 + 36), + "direction": 90.0, + "obj_mode": "A", + "val_min": 3, + "val_max": 10 + }, + ] + df2 = pd.DataFrame(rows) + expected_line_gdf = GeoDataFrame(df2, crs=CRS_METRIC) + + assert_frame_equal(traj_gdf, expected_line_gdf) \ No newline at end of file diff --git a/movingpandas/trajectory.py b/movingpandas/trajectory.py index 9f1fa14..f85ec11 100644 --- a/movingpandas/trajectory.py +++ b/movingpandas/trajectory.py @@ -7,6 +7,7 @@ from datetime import datetime from pandas import DataFrame, to_datetime, Series from pandas.core.indexes.datetimes import DatetimeIndex +from pandas import Series from geopandas import GeoDataFrame from geopy.distance import geodesic @@ -413,7 +414,7 @@ def to_line_gdf(self): line_gdf.set_geometry("geometry", inplace=True) return line_gdf - def to_traj_gdf(self, wkt=False): + def to_traj_gdf(self, wkt=False, agg=False): """ Return a GeoDataFrame with one row containing the trajectory as a single LineString. @@ -432,6 +433,15 @@ def to_traj_gdf(self, wkt=False): } if wkt: properties["wkt"] = self.to_linestringm_wkt() + if agg: + for key, value in agg.items(): + if type(value) != list: + value = [value] + for v in value: + if v == "mode": + properties[f"{key}_{v}"] = self.df.agg({key: Series.mode})[key][0] + else: + properties[f"{key}_{v}"] = self.df.agg({key: v})[key] df = DataFrame([properties]) traj_gdf = GeoDataFrame(df, crs=self.crs) return traj_gdf diff --git a/movingpandas/trajectory_collection.py b/movingpandas/trajectory_collection.py index eab1915..c2f555f 100644 --- a/movingpandas/trajectory_collection.py +++ b/movingpandas/trajectory_collection.py @@ -136,7 +136,7 @@ def to_line_gdf(self): gdf.reset_index(drop=True, inplace=True) return gdf - def to_traj_gdf(self, wkt=False): + def to_traj_gdf(self, wkt=False, agg=False): """ Return a GeoDataFrame with one row per Trajectory within the TrajectoryCollection @@ -145,7 +145,7 @@ def to_traj_gdf(self, wkt=False): ------- GeoDataFrame """ - gdfs = [traj.to_traj_gdf(wkt) for traj in self.trajectories] + gdfs = [traj.to_traj_gdf(wkt, agg) for traj in self.trajectories] gdf = concat(gdfs) gdf.reset_index(drop=True, inplace=True) return gdf diff --git a/tutorials/6-trajectory-metrics.ipynb b/tutorials/6-trajectory-metrics.ipynb new file mode 100644 index 0000000..1b12ce5 --- /dev/null +++ b/tutorials/6-trajectory-metrics.ipynb @@ -0,0 +1,134 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Computing Trajectory Metrics\n", + "\n", + "\n", + "\n", + "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/anitagraser/movingpandas/main?filepath=tutorials/6-trajectory-metrics.ipynb)\n", + "\n", + "**

This notebook demonstrates the current development version of MovingPandas.

**\n", + "\n", + "For tutorials using the latest release visit https://github.com/anitagraser/movingpandas-examples.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import urllib\n", + "import os\n", + "import numpy as np\n", + "import pandas as pd\n", + "import geopandas as gpd\n", + "from shapely.geometry import Point, LineString, Polygon, MultiPoint\n", + "from datetime import datetime, timedelta\n", + "from holoviews import opts, dim\n", + "\n", + "import sys\n", + "sys.path.append(\"..\")\n", + "import movingpandas as mpd\n", + "mpd.show_versions()\n", + "\n", + "import warnings\n", + "warnings.simplefilter(\"ignore\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Raw AIS trajectories" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gdf = gpd.read_file('data/demodata_ais.gpkg')\n", + "gdf['t'] = pd.to_datetime(gdf['Timestamp'], format='%d/%m/%Y %H:%M:%S')\n", + "gdf = gdf[gdf.SOG>0]\n", + "gdf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "traj_collection = mpd.TrajectoryCollection(gdf, 'MMSI', min_length=100, t='t')\n", + "print(\"Finished creating {} trajectories\".format(len(traj_collection)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "trips = mpd.ObservationGapSplitter(traj_collection).split(gap=timedelta(minutes=30))\n", + "print(\"Extracted {} individual trips from {} continuous vessel tracks\".format(len(trips), len(traj_collection)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "traj_gdf = trips.to_traj_gdf(agg={\"ShipType\": \"mode\", \"MMSI\": \"mode\", \"SOG\": [\"min\", \"max\"]})\n", + "traj_gdf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "movingpandas", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "vscode": { + "interpreter": { + "hash": "e083ffb052fc3090335b68330fa2108e4b62c90ad12f6dc729a484d53ff96ca7" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}