Repository: movingpandas/movingpandas

Issue:
OutlierCleaner may return invalid trajectories (len<2)
Looks like OutlierCleaner may create trajectories with a length of 1

Discussion:
codecov-commenter: :warning: Please install the !['codecov app svg image'](https://github.com/codecov/engineering-team/assets/152432831/e90313f4-9d3a-4b63-8b54-cfe14e7ec20d) to ensure uploads and comments are reliably processed by Codecov.

## [Codecov](https://app.codecov.io/gh/movingpandas/movingpandas/pull/444?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 88.81%. Comparing base [(`3f462dc`)](https://app.codecov.io/gh/movingpandas/movingpandas/commit/3f462dc644673009b8c0e0195e5f53cac6145924?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) to head [(`09f44bb`)](https://app.codecov.io/gh/movingpandas/movingpandas/commit/09f44bb23438de7a4e79bd868d896ab04f99c608?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).
> Report is 2 commits behind head on main.

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.
> :exclamation:  There is a different number of reports uploaded between BASE (3f462dc) and HEAD (09f44bb). Click for more details.
> 
> <details><summary>HEAD has 2 uploads less than BASE</summary>
>
>| Flag | BASE (3f462dc) | HEAD (09f44bb) |
>|------|------|------|
>||5|3|
></details>

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #444      +/-   ##
==========================================
- Coverage   96.35%   88.81%   -7.55%     
==========================================
  Files          35       35              
  Lines        4257     4274      +17     
==========================================
- Hits         4102     3796     -306     
- Misses        155      478     +323     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/movingpandas/movingpandas/pull/444?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).


Patch:
diff --git a/movingpandas/trajectory_cleaner.py b/movingpandas/trajectory_cleaner.py
index fcac6d0..39600ea 100644
--- a/movingpandas/trajectory_cleaner.py
+++ b/movingpandas/trajectory_cleaner.py
@@ -1,5 +1,7 @@
 # -*- coding: utf-8 -*-
 
+import warnings
+
 import pandas as pd
 from pandas.api.types import is_numeric_dtype
 
@@ -148,4 +150,14 @@ class OutlierCleaner(TrajectoryCleaner):
             prev = curr
 
         out_traj.df.drop(ixs, inplace=True)
+
+        if not out_traj.is_valid():
+            warnings.warn(
+                f"Cleaning failed for trajectory {out_traj.id} "
+                "due to invalid result (e.g. <2 points remaining). "
+                "Returning original trajectory.",
+                UserWarning,
+            )
+            out_traj = traj.copy()
+
         return out_traj


Test patch:
diff --git a/movingpandas/tests/test_trajectory_cleaner.py b/movingpandas/tests/test_trajectory_cleaner.py
index 5470fe5..abfa05d 100644
--- a/movingpandas/tests/test_trajectory_cleaner.py
+++ b/movingpandas/tests/test_trajectory_cleaner.py
@@ -4,6 +4,7 @@ from movingpandas.trajectory import Trajectory
 from movingpandas.trajectory_collection import TrajectoryCollection
 from .test_trajectory import make_traj, Node
 from movingpandas.trajectory_cleaner import IqrCleaner, OutlierCleaner
+import pytest
 import pandas as pd
 from pyproj import CRS
 from shapely.geometry import Point
@@ -92,3 +93,10 @@ class TestTrajectoryCleaner:
         cleaned = OutlierCleaner(traj).clean(v_max=100, units=("km", "h"))
         expected = "LINESTRING (-8.5829 41.1451, -8.5843 41.1464, -8.586 41.1487, -8.5872 41.1492, -8.5882 41.1489)"  # noqa F401
         assert cleaned.to_linestring().wkt == expected
+
+        # when traj is invalid after cleaning (e.g. <2 points),
+        # must give warning & return original traj
+        with pytest.warns(UserWarning):
+            cleaned = OutlierCleaner(traj).clean(v_max=0.001)
+            assert cleaned.is_valid()
+            assert cleaned == traj