Repository: movingpandas/movingpandas

Issue:
Add explore function (like in GeoPandas)
#### Is your feature request related to a problem?

Sometimes it can be difficult to install geoviews to get interactive plots

#### Describe the solution you'd like

Since GeoPandas already ships with interactive plots using folium, I'd like to see an .explore() function in MovingPandas as well https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.explore.html

#### API breaking implications

None

#### Describe alternatives you've considered

None, since this is the only interactive plotting provided via GeoPandas functions.

#### Additional context

Maybe something as simple as:

```python
tc.to_line_gdf().explore()
```


Discussion:
anitagraser: ![image](https://github.com/movingpandas/movingpandas/assets/590385/46c24604-cbde-4393-b3d1-55d6c2c71eaa)

anitagraser: ![image](https://github.com/movingpandas/movingpandas/assets/590385/c6594c7d-819f-4038-8fa9-f865abf2a8ab)


Patch:
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 96e6c0d..cc219c6 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -57,8 +57,8 @@ toc_object_entries_show_parents = "hide"
 html_theme = "pydata_sphinx_theme"
 
 html_context = {
-   # ...
-   "default_mode": "light"
+    # ...
+    "default_mode": "light"
 }
 
 # The name of an image file (relative to this directory) to place at the top
@@ -90,9 +90,6 @@ html_theme_options = {
 html_static_path = ["_static"]
 
 
-
-
-
 autodoc_mock_imports = [
     "hvplot",
     "proj",
diff --git a/environment-geopandas-1.0.yml b/environment-geopandas-pre1.0.yml
similarity index 75%
rename from environment-geopandas-1.0.yml
rename to environment-geopandas-pre1.0.yml
index 7430eb0..1b6a39b 100644
--- a/environment-geopandas-1.0.yml
+++ b/environment-geopandas-pre1.0.yml
@@ -7,6 +7,4 @@ dependencies:
   - geopy
   - pytest
   - pytest-cov
-  - pip
-  - pip:
-    - geopandas==1.0.0rc1
+  - geopandas=0.14.4
diff --git a/environment.yml b/environment.yml
index 26cffd6..a9eeb78 100644
--- a/environment.yml
+++ b/environment.yml
@@ -1,4 +1,4 @@
-name: movingpandas
+name: movingpandas-dev
 channels:
   - conda-forge
 dependencies:
@@ -8,7 +8,7 @@ dependencies:
   - codecov
   - colorcet
   - cython
-  - hvplot=0.8.3
+  - hvplot
   - flake8
   - geopandas
   - geoviews
diff --git a/movingpandas/trajectory.py b/movingpandas/trajectory.py
index 1ebf848..eb0c13c 100644
--- a/movingpandas/trajectory.py
+++ b/movingpandas/trajectory.py
@@ -258,6 +258,40 @@ class Trajectory:
         """
         return _TrajectoryPlotter(self, *args, **kwargs).plot()
 
+    def explore(self, *args, **kwargs):
+        """
+        Generate a plot using GeoPandas explore (folium/leaflet.js)
+        https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.explore.html
+
+        Parameters
+        ----------
+        args :
+            These parameters will be passed to GeoPandas explore
+        kwargs :
+            These parameters will be passed to GeoPandas explore
+
+        Returns
+        -------
+        m : folium.folium.Map
+            folium Map instance
+
+        Examples
+        --------
+        Plot speed along trajectory (with legend and specified figure size):
+
+        >>> trajectory.explore(column='speed', vmax=20, tiles="CartoDB positron")
+        """  # noqa: E501
+        from importlib.metadata import version
+
+        if version("geopandas") >= "1.0.0":
+            return self.to_line_gdf().explore(*args, **kwargs)
+        else:
+            raise (
+                NotImplementedError(
+                    "Please install geopandas >= 1.0.0 to use this function."
+                )
+            )
+
     def hvplot(self, *args, **kwargs):
         """
         Generate an interactive plot using HoloViews.
diff --git a/movingpandas/trajectory_cleaner.py b/movingpandas/trajectory_cleaner.py
index f87c03e..fcac6d0 100644
--- a/movingpandas/trajectory_cleaner.py
+++ b/movingpandas/trajectory_cleaner.py
@@ -62,7 +62,7 @@ class IqrCleaner(TrajectoryCleaner):
     ----------
     columns: dictionary
         Information regarding the columns that will be used to clean the trajectory
-        and the accompanying thresholds. 
+        and the accompanying thresholds.
 
         Key-value pairs of columns and alpha (IQR multiplier).
         Note: Setting alpha=3 is widely used.
diff --git a/movingpandas/trajectory_collection.py b/movingpandas/trajectory_collection.py
index 7e668da..4191ca9 100644
--- a/movingpandas/trajectory_collection.py
+++ b/movingpandas/trajectory_collection.py
@@ -830,6 +830,40 @@ class TrajectoryCollection:
         """
         return _TrajectoryPlotter(self, *args, **kwargs).plot()
 
+    def explore(self, *args, **kwargs):
+        """
+        Generate a plot using GeoPandas explore (folium/leaflet.js)
+        https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.explore.html
+
+        Parameters
+        ----------
+        args :
+            These parameters will be passed to GeoPandas explore
+        kwargs :
+            These parameters will be passed to GeoPandas explore
+
+        Returns
+        -------
+        m : folium.folium.Map
+            folium Map instance
+
+        Examples
+        --------
+        Plot speed along trajectory (with legend and specified figure size):
+
+        >>> trajectory_collection.explore(column='speed', vmax=20, tiles="CartoDB positron")
+        """  # noqa: E501
+        from importlib.metadata import version
+
+        if version("geopandas") >= "1.0.0":
+            return self.to_line_gdf().explore(*args, **kwargs)
+        else:
+            raise (
+                NotImplementedError(
+                    "Please install geopandas >= 1.0.0 to use this function."
+                )
+            )
+
     def hvplot(self, *args, **kwargs):
         """
         Generate an interactive trajectory plot.
diff --git a/tutorials/0-debug.ipynb b/tutorials/0-debug.ipynb
index 7a42199..7d8bd47 100644
--- a/tutorials/0-debug.ipynb
+++ b/tutorials/0-debug.ipynb
@@ -143,6 +143,15 @@
     "toy_traj.plot(column=\"speed\", linewidth=5)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "toy_traj.explore()"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -182,6 +191,33 @@
     "generalized = mpd.MinDistanceGeneralizer(tc).generalize(tolerance=100)\n"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "generalized.plot()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "generalized.explore(column='trajectory_id', tiles=\"CartoDB positron\", style_kwds={'weight':5})"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "generalized.add_speed(overwrite=True).explore(column='speed', vmax=20, tiles=\"CartoDB positron\", style_kwds={'weight':5})"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -244,6 +280,13 @@
     "intersections.hvplot_pts(tiles=None,  **hv_defaults)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
   {
    "cell_type": "code",
    "execution_count": null,


Test patch:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 1b4e641..5a26443 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -48,7 +48,7 @@ jobs:
             os: ubuntu-latest
           - env: environment-minimum-3.9.yml
             os: ubuntu-latest
-          - env: environment-geopandas-1.0.yml
+          - env: environment-geopandas-pre1.0.yml
             os: ubuntu-latest
 
     steps:
diff --git a/movingpandas/tests/__init__.py b/movingpandas/tests/__init__.py
index ffece2d..1ecb446 100644
--- a/movingpandas/tests/__init__.py
+++ b/movingpandas/tests/__init__.py
@@ -12,5 +12,18 @@ def _importorskip(modname):
     return has, func
 
 
+def _skip_if_geopandas_pre_1():
+    from importlib.metadata import version
+
+    if version("geopandas") >= "1.0.0":
+        has = True
+    else:
+        has = False
+    func = pytest.mark.skipif(not has, reason="requires geopandas >= 1.0")
+    return has, func
+
+
 has_stonesoup, requires_stonesoup = _importorskip("stonesoup")
 has_holoviews, requires_holoviews = _importorskip("holoviews")
+has_folium, requires_folium = _importorskip("folium")
+has_geopandas1, requires_geopandas1 = _skip_if_geopandas_pre_1()
diff --git a/movingpandas/tests/test_trajectory.py b/movingpandas/tests/test_trajectory.py
index 5a71d94..4096eb0 100644
--- a/movingpandas/tests/test_trajectory.py
+++ b/movingpandas/tests/test_trajectory.py
@@ -19,7 +19,7 @@ from movingpandas.trajectory import (
 )
 from movingpandas.unit_utils import MissingCRSWarning
 
-from . import requires_holoviews, has_holoviews
+from . import requires_holoviews, has_holoviews, requires_folium, has_geopandas1
 
 
 CRS_METRIC = CRS.from_user_input(31256)
@@ -776,6 +776,17 @@ class TestTrajectory:
         plot = self.default_traj_metric.plot()
         assert isinstance(plot, Axes)
 
+    @requires_folium
+    def test_explore_exists(self):
+        from folium.folium import Map
+
+        if has_geopandas1:
+            plot = self.default_traj_metric.explore()
+            assert isinstance(plot, Map)
+        else:
+            with pytest.raises(NotImplementedError):
+                plot = self.default_traj_metric.explore()
+
     @requires_holoviews
     def test_hvplot_exists(self):
         import holoviews
diff --git a/movingpandas/tests/test_trajectory_collection.py b/movingpandas/tests/test_trajectory_collection.py
index 7a37db1..21e6c4c 100644
--- a/movingpandas/tests/test_trajectory_collection.py
+++ b/movingpandas/tests/test_trajectory_collection.py
@@ -22,7 +22,7 @@ from movingpandas.trajectory import (
     TIMEDELTA_COL_NAME,
 )
 from movingpandas.trajectory_collection import TrajectoryCollection
-from . import requires_holoviews
+from . import requires_holoviews, requires_folium, has_geopandas1
 
 CRS_METRIC = CRS.from_user_input(31256)
 CRS_LATLON = CRS.from_user_input(4326)
@@ -235,6 +235,17 @@ class TestTrajectoryCollection:
         result = self.collection.plot()
         assert isinstance(result, Axes)
 
+    @requires_folium
+    def test_explore_exists(self):
+        from folium.folium import Map
+
+        if has_geopandas1:
+            plot = self.collection.explore()
+            assert isinstance(plot, Map)
+        else:
+            with pytest.raises(NotImplementedError):
+                plot = self.collection.explore()
+
     @requires_holoviews
     def test_hvplot_exists(self):
         import holoviews