Skip to content

Commit

Permalink
Add intersection function, fixes #378
Browse files Browse the repository at this point in the history
  • Loading branch information
anitagraser committed Apr 19, 2024
1 parent 87b4a80 commit 15ed5d3
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 11 deletions.
25 changes: 19 additions & 6 deletions movingpandas/tests/test_trajectory_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,17 @@ def test_get_intersecting(self):
assert len(collection) == 1
assert collection.trajectories[0] == self.collection.trajectories[0]

def test_intersection(self):
feature = {
"geometry": {
"type": "Polygon",
"coordinates": [[(-1, -1), (-1, 1), (1, 1), (1, -1), (-1, -1)]],
},
"properties": {"id": 1, "name": "foo"},
}
collection = self.collection.intersection(feature)
assert len(collection) == 1

def test_clip(self):
polygon = Polygon([(-1, -1), (-1, 1), (1, 1), (1, -1), (-1, -1)])
collection = self.collection.copy()
Expand All @@ -169,14 +180,16 @@ def test_clip(self):
assert collection.trajectories[0].to_linestring().wkt == "LINESTRING (0 0, 1 0)"

def test_clip_with_multipolygon(self):
polygon = MultiPolygon([
Polygon([(-1, -1), (-1, 1), (1, 1), (1, -1), (-1, -1)]),
Polygon([(5, 1), (7, 1), (7, 3), (5, 3), (5, 1)])
])
polygon = MultiPolygon(
[
Polygon([(-1, -1), (-1, 1), (1, 1), (1, -1), (-1, -1)]),
Polygon([(5, 1), (7, 1), (7, 3), (5, 3), (5, 1)]),
]
)
collection = self.collection.clip(polygon)
assert len(collection) == 2
assert collection.trajectories[0].to_linestring().wkt == "LINESTRING (0 0, 1 0)"
assert collection.trajectories[1].to_linestring().wkt == "LINESTRING (6 1, 6 3)"
assert collection.trajectories[1].to_linestring().wkt == "LINESTRING (6 1, 6 3)"

""" Fails, related to https://github.com/movingpandas/movingpandas/discussions/235
def test_clip_with_multipolygon2(self):
Expand All @@ -188,7 +201,7 @@ def test_clip_with_multipolygon2(self):
assert len(collection) == 2
assert collection.trajectories[0].to_linestring().wkt == "LINESTRING (0 0, 1 0)"
assert collection.trajectories[1].to_linestring().wkt == "LINESTRING (3 0, 4 0)"
"""
"""

def test_clip_with_min_length(self):
polygon = Polygon([(-1, -1), (-1, 1), (1, 1), (1, -1), (-1, -1)])
Expand Down
2 changes: 1 addition & 1 deletion movingpandas/trajectory.py
Original file line number Diff line number Diff line change
Expand Up @@ -1433,7 +1433,7 @@ def clip(self, polygon, point_based=False):

def intersection(self, feature, point_based=False):
"""
Return the trajectory segments that intersects the given feature.
Return the trajectory segments that intersects the given polygon feature.
Feature attributes are appended to the trajectory's DataFrame.
Expand Down
44 changes: 41 additions & 3 deletions movingpandas/trajectory_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ def __init__(
crs : string
CRS of the x/y coordinates
min_length : numeric
Desired minimum length of trajectories. Length is calculated using
CRS units, except if the CRS is geographic (e.g. EPSG:4326 WGS84)
Desired minimum length of trajectories. Length is calculated using
CRS units, except if the CRS is geographic (e.g. EPSG:4326 WGS84)
then length is calculated in meters.
(Shorter trajectories are discarded.)
min_duration : timedelta
Expand Down Expand Up @@ -420,6 +420,42 @@ def get_intersecting(self, polygon):
result.trajectories = intersecting
return result

def intersection(self, feature, point_based=False):
"""
Intersect trajectories with the given polygon feature.
Feature attributes are appended to the trajectory's DataFrame.
By default, the trajectory's line representation is clipped by the
polygon. If pointbased=True, the trajectory's point representation is
used instead, leading to shorter segments.
Parameters
----------
feature : shapely Feature
Feature to intersect with
point_based : bool
Clipping method
Returns
-------
TrajectoryCollection
Intersecting trajectory segments
"""
intersections = []
for traj in self:
try:
for intersect in traj.intersection(feature, point_based):
if (
intersect.get_length() >= self.min_length
): # TODO also test min_duration
intersections.append(intersect)
except: # noqa E722
pass
result = copy(self)
result.trajectories = intersections
return result

def clip(self, polygon, point_based=False):
"""
Clip trajectories by the given polygon.
Expand All @@ -440,7 +476,9 @@ def clip(self, polygon, point_based=False):
for traj in self:
try:
for intersect in traj.clip(polygon, point_based):
if (intersect.get_length() >= self.min_length): # TODO also test min_duration
if (
intersect.get_length() >= self.min_length
): # TODO also test min_duration
clipped.append(intersect)
except: # noqa E722
pass
Expand Down
43 changes: 42 additions & 1 deletion tutorials/0-debug.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,47 @@
"generalized.hvplot(tiles=None, c='speed', clim=(0,20), line_width=2, marker_size=0, **hv_defaults)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"feature = {\n",
" \"geometry\": {\n",
" \"type\": \"Polygon\",\n",
" \"coordinates\": [[(116, 39), (116.5, 39), (116.5, 40), (116, 40), (116, 39)]],\n",
" },\n",
" \"properties\": {\"id\": 1, \"name\": \"foo\"},\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"intersections = generalized.intersection(feature)\n",
"intersections.hvplot(tiles=None, c='speed', clim=(0,20), line_width=2, marker_size=0, **hv_defaults)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"intersections.trajectories[0].df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -207,7 +248,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.10"
"version": "3.10.11"
},
"vscode": {
"interpreter": {
Expand Down

0 comments on commit 15ed5d3

Please sign in to comment.