Skip to content

Commit bb1e712

Browse files
committed
Add conversions to GeoDataFrame, fixes movingpandas#114
1 parent beb4ce0 commit bb1e712

6 files changed

+268
-54
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,4 @@ blogpost.md
124124
tutorials/Blogpost.ipynb
125125

126126
.vscode/
127+
tutorials/temp.gpkg

movingpandas/tests/test_trajectory.py

+59-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import warnings
66
from pandas.util.testing import assert_frame_equal
77
from geopandas import GeoDataFrame
8-
from shapely.geometry import Point
8+
from shapely.geometry import Point, LineString
99
from datetime import datetime, timedelta
1010
from fiona.crs import from_epsg
1111
from movingpandas.trajectory import Trajectory, DIRECTION_COL_NAME, SPEED_COL_NAME, MissingCRSWarning
@@ -282,9 +282,9 @@ def test_support_for_subclasses_of_point(self):
282282

283283
def test_support_for_other_geometry_column_names(self):
284284
df = pd.DataFrame([
285-
{'xxx': TestPoint(0, 0), 't': datetime(2018, 1, 1, 12, 0, 0)},
286-
{'xxx': TestPoint(6, 0), 't': datetime(2018, 1, 1, 12, 6, 0)},
287-
{'xxx': TestPoint(6, 6), 't': datetime(2018, 1, 1, 12, 10, 0)}
285+
{'xxx': Point(0, 0), 't': datetime(2018, 1, 1, 12, 0, 0)},
286+
{'xxx': Point(6, 0), 't': datetime(2018, 1, 1, 12, 6, 0)},
287+
{'xxx': Point(6, 6), 't': datetime(2018, 1, 1, 12, 10, 0)}
288288
]).set_index('t')
289289
geo_df = GeoDataFrame(df, geometry='xxx', crs=CRS_METRIC)
290290
traj = Trajectory(geo_df, 1)
@@ -294,9 +294,9 @@ def test_support_for_other_geometry_column_names(self):
294294

295295
def test_support_for_other_time_column_names(self):
296296
df = pd.DataFrame([
297-
{'geometry': TestPoint(0, 0), 'xxx': datetime(2018, 1, 1, 12, 0, 0)},
298-
{'geometry': TestPoint(6, 0), 'xxx': datetime(2018, 1, 1, 12, 6, 0)},
299-
{'geometry': TestPoint(6, 6), 'xxx': datetime(2018, 1, 1, 12, 10, 0)}
297+
{'geometry': Point(0, 0), 'xxx': datetime(2018, 1, 1, 12, 0, 0)},
298+
{'geometry': Point(6, 0), 'xxx': datetime(2018, 1, 1, 12, 6, 0)},
299+
{'geometry': Point(6, 6), 'xxx': datetime(2018, 1, 1, 12, 10, 0)}
300300
]).set_index('xxx')
301301
geo_df = GeoDataFrame(df, crs=CRS_METRIC)
302302
traj = Trajectory(geo_df, 1)
@@ -308,6 +308,58 @@ def test_support_for_other_time_column_names(self):
308308
traj.to_linestring()
309309
traj.to_linestringm_wkt()
310310

311+
def test_to_point_gdf(self):
312+
df = pd.DataFrame([
313+
{'geometry': Point(0, 0), 't': datetime(2018, 1, 1, 12, 0, 0)},
314+
{'geometry': Point(6, 0), 't': datetime(2018, 1, 1, 12, 6, 0)},
315+
{'geometry': Point(6, 6), 't': datetime(2018, 1, 1, 12, 10, 0)}
316+
]).set_index('t')
317+
geo_df = GeoDataFrame(df, crs=CRS_METRIC)
318+
traj = Trajectory(geo_df, 1)
319+
point_gdf = traj.to_point_gdf()
320+
assert_frame_equal(point_gdf, geo_df)
321+
322+
def test_to_line_gdf(self):
323+
df = pd.DataFrame([
324+
{'geometry': Point(0, 0), 't': datetime(2018, 1, 1, 12, 0, 0)},
325+
{'geometry': Point(6, 0), 't': datetime(2018, 1, 1, 12, 6, 0)},
326+
{'geometry': Point(6, 6), 't': datetime(2018, 1, 1, 12, 10, 0)}
327+
]).set_index('t')
328+
geo_df = GeoDataFrame(df, crs=CRS_METRIC)
329+
traj = Trajectory(geo_df, 1)
330+
line_gdf = traj.to_line_gdf()
331+
332+
df2 = pd.DataFrame([
333+
{'t': datetime(2018, 1, 1, 12, 6, 0), 'prev_t': datetime(2018, 1, 1, 12, 0, 0), 'geometry': LineString([(0, 0), (6, 0)])},
334+
{'t': datetime(2018, 1, 1, 12, 10, 0), 'prev_t': datetime(2018, 1, 1, 12, 6, 0), 'geometry': LineString([(6, 0), (6, 6)])}
335+
])
336+
expected_line_gdf = GeoDataFrame(df2, crs=CRS_METRIC)
337+
338+
assert_frame_equal(line_gdf, expected_line_gdf)
339+
340+
def test_to_traj_gdf(self):
341+
df = pd.DataFrame([
342+
{'geometry': Point(0, 0), 't': datetime(1970, 1, 1, 0, 0, 0)},
343+
{'geometry': Point(6, 0), 't': datetime(1970, 1, 1, 0, 6, 0)},
344+
{'geometry': Point(6, 6), 't': datetime(1970, 1, 1, 0, 10, 0)}
345+
]).set_index('t')
346+
geo_df = GeoDataFrame(df, crs=CRS_METRIC)
347+
traj = Trajectory(geo_df, 1)
348+
traj_gdf = traj.to_traj_gdf()
349+
350+
props = {'id': 1, 'start_t': datetime(1970, 1, 1, 0, 0, 0), 'end_t': datetime(1970, 1, 1, 0, 10, 0), 'geometry': LineString([(0, 0), (6, 0), (6, 6)])}
351+
df2 = pd.DataFrame([props])
352+
expected_line_gdf = GeoDataFrame(df2, crs=CRS_METRIC)
353+
354+
assert_frame_equal(traj_gdf, expected_line_gdf)
355+
356+
traj_gdf_wkt = traj.to_traj_gdf(wkt=True)
357+
props['wkt'] = 'LINESTRING M (0.0 0.0 0.0, 6.0 0.0 360.0, 6.0 6.0 600.0)'
358+
df2 = pd.DataFrame([props])
359+
expected_line_gdf_wkt = GeoDataFrame(df2, crs=CRS_METRIC)
360+
361+
assert_frame_equal(traj_gdf_wkt, expected_line_gdf_wkt)
362+
311363
"""
312364
This test should work but fails in my PyCharm probably due to https://github.com/pyproj4/pyproj/issues/134
313365

movingpandas/tests/test_trajectory_collection.py

+36-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
import pandas as pd
44
import pytest
5+
from pandas.util.testing import assert_frame_equal
56
from geopandas import GeoDataFrame
6-
from shapely.geometry import Point, Polygon
7+
from shapely.geometry import Point, Polygon, LineString
78
from fiona.crs import from_epsg
89
from datetime import datetime, timedelta
910
from copy import copy
1011
from movingpandas.trajectory_collection import TrajectoryCollection
1112

12-
1313
CRS_METRIC = from_epsg(31256)
1414
CRS_LATLON = from_epsg(4326)
1515

@@ -166,3 +166,37 @@ def filter_trajectory(trajectory):
166166
with pytest.raises(ValueError):
167167
for _ in collection:
168168
pass
169+
170+
def test_to_point_gdf(self):
171+
point_gdf = self.collection.to_point_gdf()
172+
assert_frame_equal(point_gdf, self.geo_df)
173+
174+
def test_to_line_gdf(self):
175+
temp_df = self.geo_df.drop(columns=['obj', 'val', 'val2'])
176+
tc = TrajectoryCollection(temp_df, 'id')
177+
line_gdf = tc.to_line_gdf()
178+
179+
df2 = pd.DataFrame([
180+
{'id': 1, 't': datetime(2018, 1, 1, 12, 6, 0), 'prev_t': datetime(2018, 1, 1, 12, 0, 0), 'geometry': LineString([(0, 0), (6, 0)])},
181+
{'id': 1, 't': datetime(2018, 1, 1, 14, 10, 0), 'prev_t': datetime(2018, 1, 1, 12, 6, 0), 'geometry': LineString([(6, 0), (6, 6)])},
182+
{'id': 1, 't': datetime(2018, 1, 1, 14, 15, 0), 'prev_t': datetime(2018, 1, 1, 14, 10, 0), 'geometry': LineString([(6, 6), (9, 9)])},
183+
{'id': 2, 't': datetime(2018, 1, 1, 12, 6, 0), 'prev_t': datetime(2018, 1, 1, 12, 0, 0), 'geometry': LineString([(10, 10), (16, 10)])},
184+
{'id': 2, 't': datetime(2018, 1, 2, 13, 10, 0), 'prev_t': datetime(2018, 1, 1, 12, 6, 0), 'geometry': LineString([(16, 10), (16, 16)])},
185+
{'id': 2, 't': datetime(2018, 1, 2, 13, 15, 0), 'prev_t': datetime(2018, 1, 2, 13, 10, 0), 'geometry': LineString([(16, 16), (190, 19)])}
186+
])
187+
expected_line_gdf = GeoDataFrame(df2, crs=CRS_METRIC)
188+
189+
assert_frame_equal(line_gdf, expected_line_gdf)
190+
191+
def test_to_traj_gdf(self):
192+
temp_df = self.geo_df.drop(columns=['obj', 'val', 'val2'])
193+
tc = TrajectoryCollection(temp_df, 'id')
194+
traj_gdf = tc.to_traj_gdf()
195+
196+
rows = [{'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)])},
197+
{'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, 19)])}]
198+
df2 = pd.DataFrame(rows)
199+
expected_line_gdf = GeoDataFrame(df2, crs=CRS_METRIC)
200+
201+
assert_frame_equal(traj_gdf, expected_line_gdf)
202+

movingpandas/trajectory.py

+45
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from shapely.affinity import translate
88
from shapely.geometry import Point, LineString
99
from datetime import datetime
10+
from pandas import DataFrame
1011
from geopandas import GeoDataFrame
1112
try:
1213
from pyproj import CRS
@@ -269,6 +270,50 @@ def to_linestringm_wkt(self):
269270
wkt = "LINESTRING M ({})".format(coords[:-2])
270271
return wkt
271272

273+
def to_point_gdf(self):
274+
"""
275+
Return the trajectory's points as GeoDataFrame.
276+
277+
Returns
278+
-------
279+
GeoDataFrame
280+
"""
281+
return self.df
282+
283+
def to_line_gdf(self):
284+
"""
285+
Return the trajectory's line segments as GeoDataFrame.
286+
287+
Returns
288+
-------
289+
GeoDataFrame
290+
"""
291+
line_gdf = self._to_line_df()
292+
line_gdf.drop(columns=['geometry', 'prev_pt'], inplace=True)
293+
line_gdf.reset_index(drop=True, inplace=True)
294+
line_gdf.rename(columns={'line': 'geometry'}, inplace=True)
295+
return line_gdf
296+
297+
def to_traj_gdf(self, wkt=False):
298+
"""
299+
Return a GeoDataFrame with one row containing the trajectory as a single LineString
300+
301+
Returns
302+
-------
303+
GeoDataFrame
304+
"""
305+
properties = {
306+
'id': self.id,
307+
'start_t': self.get_start_time(),
308+
'end_t': self.get_end_time(),
309+
'geometry': self.to_linestring()
310+
}
311+
if wkt:
312+
properties['wkt'] = self.to_linestringm_wkt()
313+
df = DataFrame([properties])
314+
traj_gdf = GeoDataFrame(df, crs=self.crs)
315+
return traj_gdf
316+
272317
def get_start_location(self):
273318
"""
274319
Return the trajectory's start location.

movingpandas/trajectory_collection.py

+37
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,43 @@ def copy(self):
8282
# on __init__().
8383
return TrajectoryCollection(trajectories, min_length=self.min_length)
8484

85+
def to_point_gdf(self):
86+
"""
87+
Return the trajectories' points as GeoDataFrame.
88+
89+
Returns
90+
-------
91+
GeoDataFrame
92+
"""
93+
gdfs = [traj.df for traj in self.trajectories]
94+
return pd.concat(gdfs)
95+
96+
def to_line_gdf(self):
97+
"""
98+
Return the trajectories' line segments as GeoDataFrame.
99+
100+
Returns
101+
-------
102+
GeoDataFrame
103+
"""
104+
gdfs = [traj.to_line_gdf() for traj in self.trajectories]
105+
gdf = pd.concat(gdfs)
106+
gdf.reset_index(drop=True, inplace=True)
107+
return gdf
108+
109+
def to_traj_gdf(self, wkt=False):
110+
"""
111+
Return a GeoDataFrame with one row per Trajectory within the TrajectoryCollection
112+
113+
Returns
114+
-------
115+
GeoDataFrame
116+
"""
117+
gdfs = [traj.to_traj_gdf(wkt) for traj in self.trajectories]
118+
gdf = pd.concat(gdfs)
119+
gdf.reset_index(drop=True, inplace=True)
120+
return gdf
121+
85122
def _df_to_trajectories(self, df, traj_id_col, obj_id_col):
86123
trajectories = []
87124
for traj_id, values in df.groupby([traj_id_col]):

0 commit comments

Comments
 (0)