From 511032ba26de54c3911cf6a3c4718ad48a2ec34f Mon Sep 17 00:00:00 2001 From: mar3021 Date: Wed, 14 Aug 2024 15:51:17 +0200 Subject: [PATCH 1/6] Add explore() function examples --- 1-tutorials/1-getting-started.ipynb | 68 +++++-- 1-tutorials/8-detecting-stops.ipynb | 177 +++++++++++++++++-- 1-tutorials/9-aggregating-trajectories.ipynb | 92 ++++++++-- 1-tutorials/99-mini-example.ipynb | 77 ++++++-- environment.yml | 2 +- 5 files changed, 353 insertions(+), 63 deletions(-) diff --git a/1-tutorials/1-getting-started.ipynb b/1-tutorials/1-getting-started.ipynb index 0f6f988..e1a7343 100644 --- a/1-tutorials/1-getting-started.ipynb +++ b/1-tutorials/1-getting-started.ipynb @@ -29,7 +29,7 @@ "import geopandas as gpd\n", "import movingpandas as mpd\n", "import shapely as shp\n", - "import hvplot.pandas \n", + "import hvplot.pandas\n", "\n", "from geopandas import GeoDataFrame, read_file\n", "from shapely.geometry import Point, LineString, Polygon\n", @@ -37,9 +37,12 @@ "from holoviews import opts\n", "\n", "import warnings\n", - "warnings.filterwarnings('ignore')\n", "\n", - "opts.defaults(opts.Overlay(active_tools=['wheel_zoom'], frame_width=500, frame_height=400))\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "opts.defaults(\n", + " opts.Overlay(active_tools=[\"wheel_zoom\"], frame_width=500, frame_height=400)\n", + ")\n", "\n", "mpd.show_versions()" ] @@ -62,12 +65,14 @@ "metadata": {}, "outputs": [], "source": [ - "df = pd.DataFrame([\n", - " {'geometry':Point(0,0), 't':datetime(2018,1,1,12,0,0)},\n", - " {'geometry':Point(6,0), 't':datetime(2018,1,1,12,6,0)},\n", - " {'geometry':Point(6,6), 't':datetime(2018,1,1,12,10,0)},\n", - " {'geometry':Point(9,9), 't':datetime(2018,1,1,12,15,0)}\n", - "]).set_index('t')\n", + "df = pd.DataFrame(\n", + " [\n", + " {\"geometry\": Point(0, 0), \"t\": datetime(2018, 1, 1, 12, 0, 0)},\n", + " {\"geometry\": Point(6, 0), \"t\": datetime(2018, 1, 1, 12, 6, 0)},\n", + " {\"geometry\": Point(6, 6), \"t\": datetime(2018, 1, 1, 12, 10, 0)},\n", + " {\"geometry\": Point(9, 9), \"t\": datetime(2018, 1, 1, 12, 15, 0)},\n", + " ]\n", + ").set_index(\"t\")\n", "gdf = GeoDataFrame(df, crs=31256)\n", "toy_traj = mpd.Trajectory(gdf, 1)\n", "toy_traj" @@ -131,7 +136,7 @@ "metadata": {}, "outputs": [], "source": [ - "df = pd.read_csv('../data/geolife_small.csv', delimiter=';')\n", + "df = pd.read_csv(\"../data/geolife_small.csv\", delimiter=\";\")\n", "df" ] }, @@ -141,7 +146,7 @@ "metadata": {}, "outputs": [], "source": [ - "traj_collection = mpd.TrajectoryCollection(df, 'trajectory_id', t='t', x='X', y='Y')\n", + "traj_collection = mpd.TrajectoryCollection(df, \"trajectory_id\", t=\"t\", x=\"X\", y=\"Y\")\n", "print(traj_collection)" ] }, @@ -151,7 +156,7 @@ "metadata": {}, "outputs": [], "source": [ - "traj_collection.plot(column='trajectory_id', legend=True, figsize=(9,5))" + "traj_collection.plot(column=\"trajectory_id\", legend=True, figsize=(9, 5))" ] }, { @@ -168,7 +173,7 @@ "metadata": {}, "outputs": [], "source": [ - "gdf = read_file('../data/geolife_small.gpkg')\n", + "gdf = read_file(\"../data/geolife_small.gpkg\")\n", "gdf" ] }, @@ -186,7 +191,7 @@ "metadata": {}, "outputs": [], "source": [ - "traj_collection = mpd.TrajectoryCollection(gdf, 'trajectory_id', t='t')\n", + "traj_collection = mpd.TrajectoryCollection(gdf, \"trajectory_id\", t=\"t\")\n", "print(traj_collection)" ] }, @@ -196,7 +201,7 @@ "metadata": {}, "outputs": [], "source": [ - "traj_collection.plot(column='trajectory_id', legend=True, figsize=(9,5))" + "traj_collection.plot(column=\"trajectory_id\", legend=True, figsize=(9, 5))" ] }, { @@ -208,6 +213,22 @@ "traj_collection.plot()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To visualize trajectories in their geographical context, we can also create interactive plots with basemaps:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "traj_collection.explore(column=\"trajectory_id\", cmap=\"plasma\", tiles=\"CartoDB positron\")" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -232,7 +253,7 @@ "metadata": {}, "outputs": [], "source": [ - "my_traj.plot(linewidth=5, capstyle='round', figsize=(9,3))" + "my_traj.plot(linewidth=5, capstyle=\"round\", figsize=(9, 3))" ] }, { @@ -240,7 +261,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "To visualize trajectories in their geographical context, we can also create interactive plots with basemaps:" + "To visualize trajectories in their geographical context, we can again create interactive plots with basemaps:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_traj.hvplot(line_width=7.0, tiles=\"OSM\")" ] }, { @@ -249,7 +279,7 @@ "metadata": {}, "outputs": [], "source": [ - "my_traj.hvplot(line_width=7.0, tiles='OSM')" + "my_traj.explore(tiles=\"CartoDB positron\", style_kwds={\"weight\": 4})" ] }, { @@ -274,7 +304,7 @@ " }\n", ")\n", "gdf = gpd.GeoDataFrame(df, crs=4326)\n", - "tc = mpd.TrajectoryCollection(gdf, traj_id_col='trajectory_id', t='t')\n", + "tc = mpd.TrajectoryCollection(gdf, traj_id_col=\"trajectory_id\", t=\"t\")\n", "tc" ] }, diff --git a/1-tutorials/8-detecting-stops.ipynb b/1-tutorials/8-detecting-stops.ipynb index e427686..f13348c 100644 --- a/1-tutorials/8-detecting-stops.ipynb +++ b/1-tutorials/8-detecting-stops.ipynb @@ -30,8 +30,9 @@ "import geopandas as gpd\n", "import movingpandas as mpd\n", "import shapely as shp\n", - "import hvplot.pandas \n", + "import hvplot.pandas\n", "import matplotlib.pyplot as plt\n", + "import folium\n", "\n", "from geopandas import GeoDataFrame, read_file\n", "from shapely.geometry import Point, LineString, Polygon\n", @@ -39,10 +40,13 @@ "from holoviews import opts\n", "\n", "import warnings\n", - "warnings.filterwarnings('ignore')\n", "\n", - "plot_defaults = {'linewidth':5, 'capstyle':'round', 'figsize':(9,3), 'legend':True}\n", - "opts.defaults(opts.Overlay(active_tools=['wheel_zoom'], frame_width=500, frame_height=400))\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "plot_defaults = {\"linewidth\": 5, \"capstyle\": \"round\", \"figsize\": (9, 3), \"legend\": True}\n", + "opts.defaults(\n", + " opts.Overlay(active_tools=[\"wheel_zoom\"], frame_width=500, frame_height=400)\n", + ")\n", "\n", "mpd.show_versions()" ] @@ -53,8 +57,8 @@ "metadata": {}, "outputs": [], "source": [ - "gdf = read_file('../data/geolife_small.gpkg')\n", - "tc = mpd.TrajectoryCollection(gdf, 'trajectory_id', t='t')" + "gdf = read_file(\"../data/geolife_small.gpkg\")\n", + "tc = mpd.TrajectoryCollection(gdf, \"trajectory_id\", t=\"t\")" ] }, { @@ -81,7 +85,12 @@ "metadata": {}, "outputs": [], "source": [ - "traj_plot = my_traj.hvplot(title='Trajectory {}'.format(my_traj.id), line_width=7.0, tiles='CartoLight', color='slategray') \n", + "traj_plot = my_traj.hvplot(\n", + " title=\"Trajectory {}\".format(my_traj.id),\n", + " line_width=7.0,\n", + " tiles=\"CartoLight\",\n", + " color=\"slategray\",\n", + ")\n", "traj_plot" ] }, @@ -109,7 +118,9 @@ "outputs": [], "source": [ "%%time\n", - "stop_time_ranges = detector.get_stop_time_ranges(min_duration=timedelta(seconds=60), max_diameter=100)" + "stop_time_ranges = detector.get_stop_time_ranges(\n", + " min_duration=timedelta(seconds=60), max_diameter=100\n", + ")" ] }, { @@ -118,7 +129,7 @@ "metadata": {}, "outputs": [], "source": [ - "for x in stop_time_ranges: \n", + "for x in stop_time_ranges:\n", " print(x)" ] }, @@ -137,7 +148,9 @@ "outputs": [], "source": [ "%%time\n", - "stop_points = detector.get_stop_points(min_duration=timedelta(seconds=60), max_diameter=100)" + "stop_points = detector.get_stop_points(\n", + " min_duration=timedelta(seconds=60), max_diameter=100\n", + ")" ] }, { @@ -155,10 +168,48 @@ "metadata": {}, "outputs": [], "source": [ - "stop_point_plot = traj_plot * stop_points.hvplot(geo=True, size='duration_s', color='deeppink')\n", + "stop_point_plot = traj_plot * stop_points.hvplot(\n", + " geo=True, size=\"duration_s\", color=\"deeppink\"\n", + ")\n", "stop_point_plot" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stop_points_gdf = gpd.GeoDataFrame(stop_points, geometry=\"geometry\", crs=\"EPSG:4326\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = my_traj.explore(\n", + " color=\"blue\",\n", + " style_kwds={\"weight\": 4},\n", + " name=\"Trajectory\",\n", + ")\n", + "\n", + "stop_points_gdf.explore(\n", + " m=m,\n", + " color=\"red\",\n", + " style_kwds={\n", + " \"style_function\": lambda x: {\"radius\": x[\"properties\"][\"duration_s\"] / 10}\n", + " },\n", + " name=\"Stop points\",\n", + ")\n", + "\n", + "folium.TileLayer(\"OpenStreetMap\").add_to(m)\n", + "folium.LayerControl().add_to(m)\n", + "\n", + "m" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -174,7 +225,9 @@ "outputs": [], "source": [ "%%time\n", - "stop_segments = detector.get_stop_segments(min_duration=timedelta(seconds=60), max_diameter=100)" + "stop_segments = detector.get_stop_segments(\n", + " min_duration=timedelta(seconds=60), max_diameter=100\n", + ")" ] }, { @@ -192,10 +245,50 @@ "metadata": {}, "outputs": [], "source": [ - "stop_segment_plot = stop_point_plot * stop_segments.hvplot(line_width=7.0, tiles=None, color='orange') \n", + "stop_segment_plot = stop_point_plot * stop_segments.hvplot(\n", + " line_width=7.0, tiles=None, color=\"orange\"\n", + ")\n", "stop_segment_plot" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = my_traj.explore(\n", + " color=\"blue\",\n", + " tooltip=\"trajectory_id\",\n", + " popup=True,\n", + " style_kwds={\"weight\": 4},\n", + " name=\"Trajectory\",\n", + ")\n", + "\n", + "stop_segments.explore(\n", + " m=m,\n", + " color=\"orange\",\n", + " tooltip=\"trajectory_id\",\n", + " popup=True,\n", + " style_kwds={\"weight\": 4},\n", + " name=\"Stop segments\",\n", + ")\n", + "\n", + "stop_points_gdf.explore(\n", + " m=m,\n", + " color=\"red\",\n", + " tooltip=\"stop_id\",\n", + " popup=True,\n", + " marker_kwds={\"radius\": 3},\n", + " name=\"Stop points\",\n", + ")\n", + "\n", + "folium.TileLayer(\"CartoDB positron\").add_to(m)\n", + "folium.LayerControl().add_to(m)\n", + "\n", + "m" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -211,7 +304,9 @@ "outputs": [], "source": [ "%%time\n", - "split = mpd.StopSplitter(my_traj).split(min_duration=timedelta(seconds=60), max_diameter=100)" + "split = mpd.StopSplitter(my_traj).split(\n", + " min_duration=timedelta(seconds=60), max_diameter=100\n", + ")" ] }, { @@ -238,7 +333,22 @@ "metadata": {}, "outputs": [], "source": [ - "stop_segment_plot + split.hvplot(title='Trajectory {} split at stops'.format(my_traj.id), line_width=7.0, tiles='CartoLight')" + "split.explore(\n", + " column=\"trajectory_id\", tiles=\"CartoDB dark_matter\", style_kwds={\"weight\": 4}\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stop_segment_plot + split.hvplot(\n", + " title=\"Trajectory {} split at stops\".format(my_traj.id),\n", + " line_width=7.0,\n", + " tiles=\"CartoLight\",\n", + ")" ] }, { @@ -259,7 +369,9 @@ "source": [ "%%time\n", "detector = mpd.TrajectoryStopDetector(tc)\n", - "stop_points = detector.get_stop_points(min_duration=timedelta(seconds=120), max_diameter=100)\n", + "stop_points = detector.get_stop_points(\n", + " min_duration=timedelta(seconds=120), max_diameter=100\n", + ")\n", "len(stop_points)" ] }, @@ -269,8 +381,37 @@ "metadata": {}, "outputs": [], "source": [ - "ax = tc.plot(figsize=(7,7))\n", - "stop_points.plot(ax=ax, color='red')" + "ax = tc.plot(figsize=(7, 7))\n", + "stop_points.plot(ax=ax, color=\"red\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = tc.explore(\n", + " column=\"trajectory_id\",\n", + " tooltip=\"trajectory_id\",\n", + " popup=True,\n", + " style_kwds={\"weight\": 4},\n", + " name=\"Trajectories\",\n", + ")\n", + "\n", + "stop_points.explore(\n", + " m=m,\n", + " color=\"red\",\n", + " tooltip=\"stop_id\",\n", + " popup=True,\n", + " marker_kwds={\"radius\": 5},\n", + " name=\"Stop points\",\n", + ")\n", + "\n", + "folium.TileLayer(\"CartoDB positron\").add_to(m)\n", + "folium.LayerControl().add_to(m)\n", + "\n", + "m" ] }, { diff --git a/1-tutorials/9-aggregating-trajectories.ipynb b/1-tutorials/9-aggregating-trajectories.ipynb index 30d0ed7..dd9047b 100644 --- a/1-tutorials/9-aggregating-trajectories.ipynb +++ b/1-tutorials/9-aggregating-trajectories.ipynb @@ -32,8 +32,9 @@ "import geopandas as gpd\n", "import movingpandas as mpd\n", "import shapely as shp\n", - "import hvplot.pandas \n", + "import hvplot.pandas\n", "import matplotlib.pyplot as plt\n", + "import folium\n", "\n", "from geopandas import GeoDataFrame, read_file\n", "from shapely.geometry import Point, LineString, Polygon\n", @@ -41,10 +42,13 @@ "from holoviews import opts, dim\n", "\n", "import warnings\n", - "warnings.filterwarnings('ignore')\n", "\n", - "plot_defaults = {'linewidth':5, 'capstyle':'round', 'figsize':(9,3), 'legend':True}\n", - "opts.defaults(opts.Overlay(active_tools=['wheel_zoom'], frame_width=500, frame_height=400))\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "plot_defaults = {\"linewidth\": 5, \"capstyle\": \"round\", \"figsize\": (9, 3), \"legend\": True}\n", + "opts.defaults(\n", + " opts.Overlay(active_tools=[\"wheel_zoom\"], frame_width=500, frame_height=400)\n", + ")\n", "\n", "mpd.show_versions()" ] @@ -55,8 +59,17 @@ "metadata": {}, "outputs": [], "source": [ - "gdf = read_file('../data/geolife_small.gpkg')\n", - "tc = mpd.TrajectoryCollection(gdf, 'trajectory_id', t='t')" + "gdf = read_file(\"../data/geolife_small.gpkg\")\n", + "tc = mpd.TrajectoryCollection(gdf, \"trajectory_id\", t=\"t\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tc.hvplot(line_width=7.0, tiles=\"StamenTonerBackground\")" ] }, { @@ -65,7 +78,7 @@ "metadata": {}, "outputs": [], "source": [ - "tc.hvplot(line_width=7.0, tiles='StamenTonerBackground')" + "tc.explore(column=\"trajectory_id\", cmap=\"plasma\", style_kwds={\"weight\": 4})" ] }, { @@ -99,7 +112,12 @@ "metadata": {}, "outputs": [], "source": [ - "aggregator = mpd.TrajectoryCollectionAggregator(generalized, max_distance=1000, min_distance=100, min_stop_duration=timedelta(minutes=5))" + "aggregator = mpd.TrajectoryCollectionAggregator(\n", + " generalized,\n", + " max_distance=1000,\n", + " min_distance=100,\n", + " min_stop_duration=timedelta(minutes=5),\n", + ")" ] }, { @@ -110,8 +128,26 @@ "source": [ "pts = aggregator.get_significant_points_gdf()\n", "clusters = aggregator.get_clusters_gdf()\n", - "( pts.hvplot(geo=True, tiles='StamenTonerBackground') * \n", - " clusters.hvplot(geo=True, color='red' ) )" + "(\n", + " pts.hvplot(geo=True, tiles=\"StamenTonerBackground\")\n", + " * clusters.hvplot(geo=True, color=\"red\")\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = pts.explore(marker_kwds={\"radius\": 3}, name=\"Significant points\")\n", + "\n", + "clusters.explore(m=m, color=\"red\", marker_kwds={\"radius\": 3}, name=\"Cluster centroids\")\n", + "\n", + "folium.TileLayer(\"CartoDB positron\").add_to(m)\n", + "folium.LayerControl().add_to(m)\n", + "\n", + "m" ] }, { @@ -129,8 +165,40 @@ "metadata": {}, "outputs": [], "source": [ - "( flows.hvplot(geo=True, hover_cols=['weight'], line_width=dim('weight')*7, color='#1f77b3', tiles='StamenTonerBackground') * \n", - " clusters.hvplot(geo=True, color='red', size=dim('n') ) )" + "(\n", + " flows.hvplot(\n", + " geo=True,\n", + " hover_cols=[\"weight\"],\n", + " line_width=dim(\"weight\") * 7,\n", + " color=\"#1f77b3\",\n", + " tiles=\"StamenTonerBackground\",\n", + " )\n", + " * clusters.hvplot(geo=True, color=\"red\", size=dim(\"n\"))\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = flows.explore(\n", + " style_kwds={\"weight\": 5},\n", + " name=\"Flows\",\n", + ")\n", + "\n", + "clusters.explore(\n", + " m=m,\n", + " color=\"red\",\n", + " style_kwds={\"style_function\": lambda x: {\"radius\": x[\"properties\"][\"n\"]}},\n", + " name=\"Clusters\",\n", + ")\n", + "\n", + "folium.TileLayer(\"OpenStreetMap\").add_to(m)\n", + "folium.LayerControl().add_to(m)\n", + "\n", + "m" ] }, { diff --git a/1-tutorials/99-mini-example.ipynb b/1-tutorials/99-mini-example.ipynb index 59070cc..6354db7 100644 --- a/1-tutorials/99-mini-example.ipynb +++ b/1-tutorials/99-mini-example.ipynb @@ -24,7 +24,8 @@ "outputs": [], "source": [ "import warnings\n", - "warnings.filterwarnings('ignore')" + "\n", + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -37,7 +38,8 @@ "import geopandas as gpd\n", "import movingpandas as mpd\n", "from hvplot import pandas\n", - "from datetime import datetime, timedelta" + "from datetime import datetime, timedelta\n", + "import folium" ] }, { @@ -56,8 +58,17 @@ "metadata": {}, "outputs": [], "source": [ - "gdf = gpd.read_file('../data/geolife_small.gpkg')\n", - "gdf.plot(figsize=(9,5))" + "gdf = gpd.read_file(\"../data/geolife_small.gpkg\")\n", + "gdf.plot(figsize=(9, 5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gdf.hvplot(geo=True).opts(active_tools=[\"pan\", \"wheel_zoom\"])" ] }, { @@ -66,7 +77,7 @@ "metadata": {}, "outputs": [], "source": [ - "gdf.hvplot(geo=True).opts(active_tools=['pan', 'wheel_zoom'])" + "gdf.explore()" ] }, { @@ -91,7 +102,7 @@ "metadata": {}, "outputs": [], "source": [ - "tc = mpd.TrajectoryCollection(gdf, 'trajectory_id', t='t')\n", + "tc = mpd.TrajectoryCollection(gdf, \"trajectory_id\", t=\"t\")\n", "tc" ] }, @@ -101,7 +112,16 @@ "metadata": {}, "outputs": [], "source": [ - "tc.plot(column='trajectory_id', legend=True, figsize=(9,5))" + "tc.plot(column=\"trajectory_id\", legend=True, figsize=(9, 5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tc.explore(column=\"trajectory_id\", cmap=\"plasma\", style_kwds={\"weight\": 4})" ] }, { @@ -118,7 +138,9 @@ "metadata": {}, "outputs": [], "source": [ - "tc.plot(column='speed', linewidth=5, capstyle='round', legend=True, vmax=20, figsize=(9,5))" + "tc.plot(\n", + " column=\"speed\", linewidth=5, capstyle=\"round\", legend=True, vmax=20, figsize=(9, 5)\n", + ")" ] }, { @@ -136,7 +158,19 @@ "outputs": [], "source": [ "detector = mpd.TrajectoryStopDetector(tc)\n", - "stop_points = detector.get_stop_points(min_duration=timedelta(seconds=120), max_diameter=100)" + "stop_points = detector.get_stop_points(\n", + " min_duration=timedelta(seconds=120), max_diameter=100\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ax = tc.plot(figsize=(9, 5))\n", + "stop_points.plot(ax=ax, color=\"red\", markersize=100)" ] }, { @@ -145,8 +179,10 @@ "metadata": {}, "outputs": [], "source": [ - "ax = tc.plot(figsize=(9,5))\n", - "stop_points.plot(ax=ax, color='red', markersize=100)" + "(\n", + " tc.hvplot(line_width=7, tiles=None, frame_width=400, frame_height=400)\n", + " * stop_points.hvplot(geo=True, color=\"black\", size=100)\n", + ")" ] }, { @@ -155,8 +191,23 @@ "metadata": {}, "outputs": [], "source": [ - "( tc.hvplot(line_width=7, tiles=None, frame_width=400, frame_height=400) * \n", - " stop_points.hvplot(geo=True, color='black', size=100) )" + "m = tc.explore(\n", + " column=\"trajectory_id\",\n", + " cmap=\"autumn\",\n", + " style_kwds={\"weight\": 4},\n", + " name=\"Trajectories\",\n", + ")\n", + "\n", + "stop_points.explore(\n", + " m=m,\n", + " marker_kwds={\"radius\": 4},\n", + " name=\"Stop points\",\n", + ")\n", + "\n", + "folium.TileLayer(\"CartoDB dark_matter\").add_to(m)\n", + "folium.LayerControl().add_to(m)\n", + "\n", + "m" ] }, { diff --git a/environment.yml b/environment.yml index 5b555f4..e1c31e6 100644 --- a/environment.yml +++ b/environment.yml @@ -4,7 +4,6 @@ channels: dependencies: - python>=3.12 - pip - - geopandas - jupyter_client<8 - notebook - pandas @@ -13,3 +12,4 @@ dependencies: - movingpandas - geoviews - hvplot + - geopandas==1.0.1 From fefa64dce6914f8c674a1f932a238be02469202b Mon Sep 17 00:00:00 2001 From: mar3021 Date: Wed, 14 Aug 2024 16:10:19 +0200 Subject: [PATCH 2/6] Add explore() function examples --- 2-analysis-examples/horse-collar.ipynb | 348 +++++++++++++++++++------ 2-analysis-examples/iceberg.ipynb | 38 ++- 2-analysis-examples/osm-traces.ipynb | 101 +++++-- 2-analysis-examples/ship-data.ipynb | 342 ++++++++++++++++++++---- 4 files changed, 666 insertions(+), 163 deletions(-) diff --git a/2-analysis-examples/horse-collar.ipynb b/2-analysis-examples/horse-collar.ipynb index 9c85e67..3707b23 100644 --- a/2-analysis-examples/horse-collar.ipynb +++ b/2-analysis-examples/horse-collar.ipynb @@ -35,7 +35,7 @@ "import geopandas as gpd\n", "import movingpandas as mpd\n", "import shapely as shp\n", - "import hvplot.pandas \n", + "import hvplot.pandas\n", "import matplotlib.pyplot as plt\n", "\n", "from pyproj import CRS\n", @@ -47,11 +47,14 @@ "from urllib.request import urlretrieve\n", "\n", "import warnings\n", - "warnings.filterwarnings('ignore')\n", "\n", - "plot_defaults = {'linewidth':5, 'capstyle':'round', 'figsize':(9,3), 'legend':True}\n", - "opts.defaults(opts.Overlay(active_tools=['wheel_zoom'], frame_width=300, frame_height=400))\n", - "hvplot_defaults = {'tiles':None, 'cmap':'Viridis', 'colorbar':True}\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "plot_defaults = {\"linewidth\": 5, \"capstyle\": \"round\", \"figsize\": (9, 3), \"legend\": True}\n", + "opts.defaults(\n", + " opts.Overlay(active_tools=[\"wheel_zoom\"], frame_width=300, frame_height=400)\n", + ")\n", + "hvplot_defaults = {\"tiles\": None, \"cmap\": \"Viridis\", \"colorbar\": True}\n", "\n", "mpd.show_versions()" ] @@ -78,9 +81,9 @@ "metadata": {}, "outputs": [], "source": [ - "df = read_file('../data/horse_collar.gpkg')\n", - "df['t'] = pd.to_datetime(df['timestamp'])\n", - "df = df.set_index('t').tz_localize(None)\n", + "df = read_file(\"../data/horse_collar.gpkg\")\n", + "df[\"t\"] = pd.to_datetime(df[\"timestamp\"])\n", + "df = df.set_index(\"t\").tz_localize(None)\n", "print(\"This dataset contains {} records.\\nThe first lines are:\".format(len(df)))\n", "df.head()" ] @@ -100,13 +103,48 @@ "metadata": {}, "outputs": [], "source": [ - "df = df.drop(columns=['LMT_Date', 'LMT_Time',\n", - " 'Origin', 'SCTS_Date', 'SCTS_Time', 'Latitude [?]', 'Longitude [?]',\n", - " 'FixType', 'Main [V]', 'Beacon [V]', 'Sats', 'Sat',\n", - " 'C/N', 'Sat_1', 'C/N_1', 'Sat_2', 'C/N_2', 'Sat_3', 'C/N_3', 'Sat_4',\n", - " 'C/N_4', 'Sat_5', 'C/N_5', 'Sat_6', 'C/N_6', 'Sat_7', 'C/N_7', 'Sat_8',\n", - " 'C/N_8', 'Sat_9', 'C/N_9', 'Sat_10', 'C/N_10', 'Sat_11', 'C/N_11',\n", - " 'Easting', 'Northing',], axis=1)" + "df = df.drop(\n", + " columns=[\n", + " \"LMT_Date\",\n", + " \"LMT_Time\",\n", + " \"Origin\",\n", + " \"SCTS_Date\",\n", + " \"SCTS_Time\",\n", + " \"Latitude [?]\",\n", + " \"Longitude [?]\",\n", + " \"FixType\",\n", + " \"Main [V]\",\n", + " \"Beacon [V]\",\n", + " \"Sats\",\n", + " \"Sat\",\n", + " \"C/N\",\n", + " \"Sat_1\",\n", + " \"C/N_1\",\n", + " \"Sat_2\",\n", + " \"C/N_2\",\n", + " \"Sat_3\",\n", + " \"C/N_3\",\n", + " \"Sat_4\",\n", + " \"C/N_4\",\n", + " \"Sat_5\",\n", + " \"C/N_5\",\n", + " \"Sat_6\",\n", + " \"C/N_6\",\n", + " \"Sat_7\",\n", + " \"C/N_7\",\n", + " \"Sat_8\",\n", + " \"C/N_8\",\n", + " \"Sat_9\",\n", + " \"C/N_9\",\n", + " \"Sat_10\",\n", + " \"C/N_10\",\n", + " \"Sat_11\",\n", + " \"C/N_11\",\n", + " \"Easting\",\n", + " \"Northing\",\n", + " ],\n", + " axis=1,\n", + ")" ] }, { @@ -124,7 +162,7 @@ "metadata": {}, "outputs": [], "source": [ - "collar_id = df['CollarID'].unique()[0]\n", + "collar_id = df[\"CollarID\"].unique()[0]\n", "print(\"There is only one collar with ID {}.\".format(collar_id))" ] }, @@ -134,7 +172,7 @@ "metadata": {}, "outputs": [], "source": [ - "df['Activity'].unique()" + "df[\"Activity\"].unique()" ] }, { @@ -166,7 +204,9 @@ "metadata": {}, "outputs": [], "source": [ - "df.to_crs({'init': 'epsg:4326'}).hvplot(title='Geographic extent of the dataset', geo=True, tiles='OSM')" + "df.to_crs({\"init\": \"epsg:4326\"}).hvplot(\n", + " title=\"Geographic extent of the dataset\", geo=True, tiles=\"OSM\"\n", + ")" ] }, { @@ -184,7 +224,7 @@ "metadata": {}, "outputs": [], "source": [ - "pd.DataFrame(df).sort_values('lat').tail(2)" + "pd.DataFrame(df).sort_values(\"lat\").tail(2)" ] }, { @@ -202,9 +242,17 @@ "metadata": {}, "outputs": [], "source": [ - "df = df[2:].to_crs({'init': 'epsg:4326'})\n", - "( df.hvplot(title='OSM showing paths and fences', size=2, geo=True, tiles='OSM') +\n", - " df.hvplot(title='Imagery showing land cover details', size=2, color='red', geo=True, tiles='EsriImagery') )" + "df = df[2:].to_crs({\"init\": \"epsg:4326\"})\n", + "(\n", + " df.hvplot(title=\"OSM showing paths and fences\", size=2, geo=True, tiles=\"OSM\")\n", + " + df.hvplot(\n", + " title=\"Imagery showing land cover details\",\n", + " size=2,\n", + " color=\"red\",\n", + " geo=True,\n", + " tiles=\"EsriImagery\",\n", + " )\n", + ")" ] }, { @@ -221,10 +269,10 @@ "outputs": [], "source": [ "temp = df.to_crs(CRS(25832))\n", - "temp['geometry'] = temp['geometry'].buffer(5)\n", - "total_area = temp.dissolve(by='CollarID').area \n", - "total_area = total_area[collar_id]/10000\n", - "print('The total area covered by the data is: {:,.2f} ha'.format(total_area))" + "temp[\"geometry\"] = temp[\"geometry\"].buffer(5)\n", + "total_area = temp.dissolve(by=\"CollarID\").area\n", + "total_area = total_area[collar_id] / 10000\n", + "print(\"The total area covered by the data is: {:,.2f} ha\".format(total_area))" ] }, { @@ -240,7 +288,11 @@ "metadata": {}, "outputs": [], "source": [ - "print(\"The dataset covers the time between {} and {}.\".format(df.index.min(), df.index.max()))" + "print(\n", + " \"The dataset covers the time between {} and {}.\".format(\n", + " df.index.min(), df.index.max()\n", + " )\n", + ")" ] }, { @@ -258,7 +310,7 @@ "metadata": {}, "outputs": [], "source": [ - "df['No'].resample('1d').count().hvplot(title='Number of records per day')" + "df[\"No\"].resample(\"1d\").count().hvplot(title=\"Number of records per day\")" ] }, { @@ -285,8 +337,13 @@ "metadata": {}, "outputs": [], "source": [ - "df['Y-M'] = df.index.to_period('M') \n", - "Layout([df[df['Y-M']==i].hvplot(title=str(i), size=2, geo=True, tiles='OSM') for i in df['Y-M'].unique()])" + "df[\"Y-M\"] = df.index.to_period(\"M\")\n", + "Layout(\n", + " [\n", + " df[df[\"Y-M\"] == i].hvplot(title=str(i), size=2, geo=True, tiles=\"OSM\")\n", + " for i in df[\"Y-M\"].unique()\n", + " ]\n", + ")" ] }, { @@ -325,8 +382,13 @@ "source": [ "t = df.reset_index().t\n", "df = df.assign(delta_t=t.diff().values)\n", - "df['delta_t'] = df['delta_t'].dt.total_seconds()/60\n", - "pd.DataFrame(df).hvplot.hist('delta_t', title='Histogram of intervals between consecutive records (in minutes)', bins=60, bin_range=(0, 60))" + "df[\"delta_t\"] = df[\"delta_t\"].dt.total_seconds() / 60\n", + "pd.DataFrame(df).hvplot.hist(\n", + " \"delta_t\",\n", + " title=\"Histogram of intervals between consecutive records (in minutes)\",\n", + " bins=60,\n", + " bin_range=(0, 60),\n", + ")" ] }, { @@ -353,11 +415,15 @@ "metadata": {}, "outputs": [], "source": [ - "tc = mpd.TrajectoryCollection(df, 'CollarID')\n", + "tc = mpd.TrajectoryCollection(df, \"CollarID\")\n", "traj = tc.trajectories[0]\n", "traj.add_speed()\n", "max_speed = traj.df.speed.max()\n", - "print(\"The highest computed speed is {:,.2f} m/s ({:,.2f} km/h)\".format(max_speed, max_speed*3600/1000))" + "print(\n", + " \"The highest computed speed is {:,.2f} m/s ({:,.2f} km/h)\".format(\n", + " max_speed, max_speed * 3600 / 1000\n", + " )\n", + ")" ] }, { @@ -373,7 +439,9 @@ "metadata": {}, "outputs": [], "source": [ - "pd.DataFrame(traj.df).hvplot.hist('speed', title='Histogram of speeds (in meters per second)', bins=90)" + "pd.DataFrame(traj.df).hvplot.hist(\n", + " \"speed\", title=\"Histogram of speeds (in meters per second)\", bins=90\n", + ")" ] }, { @@ -390,7 +458,7 @@ "outputs": [], "source": [ "traj.add_direction(overwrite=True)\n", - "pd.DataFrame(traj.df).hvplot.hist('direction', title='Histogram of directions', bins=90)" + "pd.DataFrame(traj.df).hvplot.hist(\"direction\", title=\"Histogram of directions\", bins=90)" ] }, { @@ -417,8 +485,13 @@ "metadata": {}, "outputs": [], "source": [ - "pd.DataFrame(traj.df).hvplot.heatmap(title='Mean speed by hour of day and month of year', \n", - " x='t.hour', y='t.month', C='speed', reduce_function=np.mean)" + "pd.DataFrame(traj.df).hvplot.heatmap(\n", + " title=\"Mean speed by hour of day and month of year\",\n", + " x=\"t.hour\",\n", + " y=\"t.month\",\n", + " C=\"speed\",\n", + " reduce_function=np.mean,\n", + ")" ] }, { @@ -438,9 +511,14 @@ "metadata": {}, "outputs": [], "source": [ - "traj.df['n'] = 1\n", - "pd.DataFrame(traj.df).hvplot.heatmap(title='Record count by temperature and month of year', \n", - " x='Temp [?C]', y='t.month', C='n', reduce_function=np.sum)" + "traj.df[\"n\"] = 1\n", + "pd.DataFrame(traj.df).hvplot.heatmap(\n", + " title=\"Record count by temperature and month of year\",\n", + " x=\"Temp [?C]\",\n", + " y=\"t.month\",\n", + " C=\"n\",\n", + " reduce_function=np.sum,\n", + ")" ] }, { @@ -449,8 +527,13 @@ "metadata": {}, "outputs": [], "source": [ - "pd.DataFrame(traj.df).hvplot.heatmap(title='Mean speed by temperature and month of year', \n", - " x='Temp [?C]', y='t.month', C='speed', reduce_function=np.mean)" + "pd.DataFrame(traj.df).hvplot.heatmap(\n", + " title=\"Mean speed by temperature and month of year\",\n", + " x=\"Temp [?C]\",\n", + " y=\"t.month\",\n", + " C=\"speed\",\n", + " reduce_function=np.mean,\n", + ")" ] }, { @@ -468,9 +551,21 @@ "metadata": {}, "outputs": [], "source": [ - "traj.df['dir_class'] = ((traj.df['direction']-22.5)/45).round(0)\n", + "traj.df[\"dir_class\"] = ((traj.df[\"direction\"] - 22.5) / 45).round(0)\n", "temp = traj.df\n", - "Layout([temp[temp['dir_class']==i].hvplot(geo=True, tiles='OSM', size=2, width=300, height=300, title=str(int(i*45))+\"°\") for i in sorted(temp['dir_class'].unique())])" + "Layout(\n", + " [\n", + " temp[temp[\"dir_class\"] == i].hvplot(\n", + " geo=True,\n", + " tiles=\"OSM\",\n", + " size=2,\n", + " width=300,\n", + " height=300,\n", + " title=str(int(i * 45)) + \"°\",\n", + " )\n", + " for i in sorted(temp[\"dir_class\"].unique())\n", + " ]\n", + ")" ] }, { @@ -486,14 +581,24 @@ "metadata": {}, "outputs": [], "source": [ - "traj.df['speed_class'] = (traj.df['speed']*2).round(1)\n", + "traj.df[\"speed_class\"] = (traj.df[\"speed\"] * 2).round(1)\n", "temp = traj.df\n", "plots = []\n", - "for i in sorted(temp['speed_class'].unique()):\n", - " filtered = temp[temp['speed_class']==i]\n", - " if len(filtered) < 10: \n", + "for i in sorted(temp[\"speed_class\"].unique()):\n", + " filtered = temp[temp[\"speed_class\"] == i]\n", + " if len(filtered) < 10:\n", " continue\n", - " plots.append(filtered.hvplot(geo=True, tiles='EsriImagery', color='red', size=2, width=300, height=300, title=str(i/2)))\n", + " plots.append(\n", + " filtered.hvplot(\n", + " geo=True,\n", + " tiles=\"EsriImagery\",\n", + " color=\"red\",\n", + " size=2,\n", + " width=300,\n", + " height=300,\n", + " title=str(i / 2),\n", + " )\n", + " )\n", "Layout(plots)" ] }, @@ -530,7 +635,16 @@ "metadata": {}, "outputs": [], "source": [ - "tc.hvplot() " + "tc.hvplot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tc.to_traj_gdf().explore(color=\"orange\", tiles=\"CartoDB positron\")" ] }, { @@ -555,7 +669,7 @@ "metadata": {}, "outputs": [], "source": [ - "daily = mpd.TemporalSplitter(tc).split(mode='day')" + "daily = mpd.TemporalSplitter(tc).split(mode=\"day\")" ] }, { @@ -564,7 +678,14 @@ "metadata": {}, "outputs": [], "source": [ - "Layout([daily.trajectories[i].hvplot(title=daily.trajectories[i].id, c='speed', line_width=2, cmap='RdYlBu') for i in range(0,7)])" + "Layout(\n", + " [\n", + " daily.trajectories[i].hvplot(\n", + " title=daily.trajectories[i].id, c=\"speed\", line_width=2, cmap=\"RdYlBu\"\n", + " )\n", + " for i in range(0, 7)\n", + " ]\n", + ")" ] }, { @@ -581,9 +702,9 @@ "outputs": [], "source": [ "daily_starts = daily.get_start_locations()\n", - "daily_starts.set_index(pd.to_datetime(daily_starts['timestamp']), inplace=True) \n", - "daily_starts['month'] = daily_starts.index.month\n", - "daily_starts.hvplot(c='month', geo=True, tiles='EsriImagery', cmap='autumn')" + "daily_starts.set_index(pd.to_datetime(daily_starts[\"timestamp\"]), inplace=True)\n", + "daily_starts[\"month\"] = daily_starts.index.month\n", + "daily_starts.hvplot(c=\"month\", geo=True, tiles=\"EsriImagery\", cmap=\"autumn\")" ] }, { @@ -606,7 +727,7 @@ "metadata": {}, "outputs": [], "source": [ - "moving = mpd.TrajectoryCollection(traj.df[traj.df['speed'] > 0.05], 'CollarID')\n", + "moving = mpd.TrajectoryCollection(traj.df[traj.df[\"speed\"] > 0.05], \"CollarID\")\n", "moving = mpd.ObservationGapSplitter(moving).split(gap=timedelta(minutes=70))" ] }, @@ -616,7 +737,9 @@ "metadata": {}, "outputs": [], "source": [ - "moving.get_start_locations().hvplot(c='month', geo=True, tiles='EsriImagery', color='red')" + "moving.get_start_locations().hvplot(\n", + " c=\"month\", geo=True, tiles=\"EsriImagery\", color=\"red\"\n", + ")" ] }, { @@ -642,8 +765,8 @@ "metadata": {}, "outputs": [], "source": [ - "daily_lengths = pd.DataFrame(daily_lengths, index=daily_t, columns=['length'])\n", - "daily_lengths.hvplot(title='Daily trajectory length')" + "daily_lengths = pd.DataFrame(daily_lengths, index=daily_t, columns=[\"length\"])\n", + "daily_lengths.hvplot(title=\"Daily trajectory length\")" ] }, { @@ -675,9 +798,12 @@ "metadata": {}, "outputs": [], "source": [ - "daily_areas = [(traj.id, traj.to_crs(CRS(25832)).to_linestring().convex_hull.area/10000) for traj in daily]\n", - "daily_areas = pd.DataFrame(daily_areas, index=daily_t, columns=['id', 'area'])\n", - "daily_areas.hvplot(title='Daily covered area [ha]', y='area')" + "daily_areas = [\n", + " (traj.id, traj.to_crs(CRS(25832)).to_linestring().convex_hull.area / 10000)\n", + " for traj in daily\n", + "]\n", + "daily_areas = pd.DataFrame(daily_areas, index=daily_t, columns=[\"id\", \"area\"])\n", + "daily_areas.hvplot(title=\"Daily covered area [ha]\", y=\"area\")" ] }, { @@ -693,9 +819,12 @@ "metadata": {}, "outputs": [], "source": [ - "daily_areas = [(traj.id, traj.to_crs(CRS(25832)).to_linestring().buffer(15).area/10000) for traj in daily]\n", - "daily_areas = pd.DataFrame(daily_areas, index=daily_t, columns=['id', 'area'])\n", - "daily_areas.hvplot(title='Daily covered area [ha]', y='area')" + "daily_areas = [\n", + " (traj.id, traj.to_crs(CRS(25832)).to_linestring().buffer(15).area / 10000)\n", + " for traj in daily\n", + "]\n", + "daily_areas = pd.DataFrame(daily_areas, index=daily_t, columns=[\"id\", \"area\"])\n", + "daily_areas.hvplot(title=\"Daily covered area [ha]\", y=\"area\")" ] }, { @@ -711,7 +840,7 @@ "metadata": {}, "outputs": [], "source": [ - "daily_areas.sort_values(by='area')[:10]" + "daily_areas.sort_values(by=\"area\")[:10]" ] }, { @@ -727,8 +856,8 @@ "metadata": {}, "outputs": [], "source": [ - "daily_areas = daily_areas.drop(datetime(2018,11,14,12,30,8))\n", - "daily_areas = daily_areas.drop(datetime(2019,11,7,0,0,9))" + "daily_areas = daily_areas.drop(datetime(2018, 11, 14, 12, 30, 8))\n", + "daily_areas = daily_areas.drop(datetime(2019, 11, 7, 0, 0, 9))" ] }, { @@ -744,7 +873,14 @@ "metadata": {}, "outputs": [], "source": [ - "Layout([daily.get_trajectory(i).hvplot(title=i, c='speed', line_width=2, cmap='RdYlBu', width=300, height=300) for i in daily_areas.sort_values(by='area')[:3].id])" + "Layout(\n", + " [\n", + " daily.get_trajectory(i).hvplot(\n", + " title=i, c=\"speed\", line_width=2, cmap=\"RdYlBu\", width=300, height=300\n", + " )\n", + " for i in daily_areas.sort_values(by=\"area\")[:3].id\n", + " ]\n", + ")" ] }, { @@ -767,13 +903,20 @@ "MAX_DIAMETER = 100\n", "MIN_DURATION = timedelta(hours=3)\n", "\n", - "one_day = daily.get_trajectory('30788_2018-11-17 00:00:00')\n", + "one_day = daily.get_trajectory(\"30788_2018-11-17 00:00:00\")\n", "one_day_stops = mpd.TrajectoryStopDetector(one_day).get_stop_segments(\n", - " min_duration=MIN_DURATION, max_diameter=MAX_DIAMETER)\n", + " min_duration=MIN_DURATION, max_diameter=MAX_DIAMETER\n", + ")\n", "\n", - "( one_day.hvplot(title='Stops in Trajectory {}'.format(one_day.id), line_width=3.0, color='slategray') * \n", - " one_day_stops.hvplot(line_width=5, tiles=None, color='deeppink') *\n", - " one_day_stops.get_start_locations().hvplot(geo=True, size=200, color='deeppink') )" + "(\n", + " one_day.hvplot(\n", + " title=\"Stops in Trajectory {}\".format(one_day.id),\n", + " line_width=3.0,\n", + " color=\"slategray\",\n", + " )\n", + " * one_day_stops.hvplot(line_width=5, tiles=None, color=\"deeppink\")\n", + " * one_day_stops.get_start_locations().hvplot(geo=True, size=200, color=\"deeppink\")\n", + ")" ] }, { @@ -790,7 +933,9 @@ "outputs": [], "source": [ "%%time\n", - "stops = mpd.TrajectoryStopDetector(tc).get_stop_points(min_duration=MIN_DURATION, max_diameter=MAX_DIAMETER)\n", + "stops = mpd.TrajectoryStopDetector(tc).get_stop_points(\n", + " min_duration=MIN_DURATION, max_diameter=MAX_DIAMETER\n", + ")\n", "len(stops)" ] }, @@ -809,7 +954,45 @@ }, "outputs": [], "source": [ - "stops.hvplot(geo=True, tiles='OSM', color='deeppink', size=MAX_DIAMETER, alpha=0.2)" + "stops.hvplot(geo=True, tiles=\"OSM\", color=\"deeppink\", size=MAX_DIAMETER, alpha=0.2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Preferred stop locations in another visualization: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stops_gdf = gpd.GeoDataFrame(stops, geometry=\"geometry\", crs=\"EPSG:4326\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stops_gdf.explore(\n", + " color=\"purple\",\n", + " tiles=\"CartoDB positron\",\n", + " tooltip=\"traj_id\",\n", + " popup=True,\n", + " marker_type=\"marker\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, stop duration:" ] }, { @@ -818,8 +1001,12 @@ "metadata": {}, "outputs": [], "source": [ - "stops['duration_h'] = (stops['end_time']-stops['start_time']).dt.total_seconds() / 3600\n", - "pd.DataFrame(stops)['duration_h'].hvplot.hist(title='Stop duration histogram', xlabel='Duration [hours]', ylabel='n', bins=12)" + "stops[\"duration_h\"] = (\n", + " stops[\"end_time\"] - stops[\"start_time\"]\n", + ").dt.total_seconds() / 3600\n", + "pd.DataFrame(stops)[\"duration_h\"].hvplot.hist(\n", + " title=\"Stop duration histogram\", xlabel=\"Duration [hours]\", ylabel=\"n\", bins=12\n", + ")" ] }, { @@ -835,7 +1022,8 @@ "1. [Soccer game](soccer-game.ipynb)\n", "1. [Mars rover & heli](mars-rover.ipynb)\n", "1. [Ever Given](ever-given.ipynb)\n", - "1. [Iceberg](iceberg.ipynb) " + "1. [Iceberg](iceberg.ipynb)\n", + "1. [Pollution data](pollution-data.ipynb)" ] } ], diff --git a/2-analysis-examples/iceberg.ipynb b/2-analysis-examples/iceberg.ipynb index 91d57af..c545f9b 100644 --- a/2-analysis-examples/iceberg.ipynb +++ b/2-analysis-examples/iceberg.ipynb @@ -106,7 +106,8 @@ "import movingpandas as mpd\n", "import zipfile\n", "import warnings\n", - "warnings.filterwarnings('ignore')" + "\n", + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -115,9 +116,9 @@ "metadata": {}, "outputs": [], "source": [ - "zf = zipfile.ZipFile('../data/icebergs_v5.zip') \n", - "df = pd.read_csv(zf.open('consol/uk319.csv'))\n", - "df['t'] = pd.to_datetime(df.date, format='%Y%j')\n", + "zf = zipfile.ZipFile(\"../data/icebergs_v5.zip\")\n", + "df = pd.read_csv(zf.open(\"consol/uk319.csv\"))\n", + "df[\"t\"] = pd.to_datetime(df.date, format=\"%Y%j\")\n", "df" ] }, @@ -127,9 +128,23 @@ "metadata": {}, "outputs": [], "source": [ - "traj = mpd.Trajectory(df, traj_id=1, t='t', x='ascat_2', y='ascat_1')\n", - "traj.hvplot(title='Iceberg trajectory', c='ascat_3', line_width=5, \n", - " cmap=['yellow','blue'], colorbar=True)" + "traj = mpd.Trajectory(df, traj_id=1, t=\"t\", x=\"ascat_2\", y=\"ascat_1\")\n", + "traj.hvplot(\n", + " title=\"Iceberg trajectory\",\n", + " c=\"ascat_3\",\n", + " line_width=5,\n", + " cmap=[\"yellow\", \"blue\"],\n", + " colorbar=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "traj.explore(color=\"#ff00ff\", tiles=\"CartoDB dark_matter\", style_kwds={\"weight\": 4})" ] }, { @@ -162,9 +177,9 @@ }, "outputs": [], "source": [ - "( \n", - " gf.coastline * \n", - " traj.hvplot(title='Iceberg trajectory in SouthPolarStereo', tiles=None)\n", + "(\n", + " gf.coastline\n", + " * traj.hvplot(title=\"Iceberg trajectory in SouthPolarStereo\", tiles=None)\n", ").opts(projection=crs.SouthPolarStereo())" ] }, @@ -183,7 +198,8 @@ "1. [Soccer game](soccer-game.ipynb)\n", "1. [Mars rover & heli](mars-rover.ipynb)\n", "1. [Ever Given](ever-given.ipynb)\n", - "1. [Iceberg](iceberg.ipynb) " + "1. [Iceberg](iceberg.ipynb)\n", + "1. [Pollution data](pollution-data.ipynb)" ] } ], diff --git a/2-analysis-examples/osm-traces.ipynb b/2-analysis-examples/osm-traces.ipynb index a829ee6..ff74c0e 100644 --- a/2-analysis-examples/osm-traces.ipynb +++ b/2-analysis-examples/osm-traces.ipynb @@ -26,7 +26,7 @@ "import geopandas as gpd\n", "import movingpandas as mpd\n", "import shapely as shp\n", - "import hvplot.pandas \n", + "import hvplot.pandas\n", "import matplotlib.pyplot as plt\n", "\n", "from geopandas import GeoDataFrame, read_file\n", @@ -37,11 +37,14 @@ "from urllib.request import urlretrieve\n", "\n", "import warnings\n", - "warnings.filterwarnings('ignore')\n", "\n", - "plot_defaults = {'linewidth':5, 'capstyle':'round', 'figsize':(9,3), 'legend':True}\n", - "opts.defaults(opts.Overlay(active_tools=['wheel_zoom'], frame_width=500, frame_height=400))\n", - "hvplot_defaults = {'tiles':None, 'cmap':'Viridis', 'colorbar':True}\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "plot_defaults = {\"linewidth\": 5, \"capstyle\": \"round\", \"figsize\": (9, 3), \"legend\": True}\n", + "opts.defaults(\n", + " opts.Overlay(active_tools=[\"wheel_zoom\"], frame_width=500, frame_height=400)\n", + ")\n", + "hvplot_defaults = {\"tiles\": None, \"cmap\": \"Viridis\", \"colorbar\": True}\n", "\n", "mpd.show_versions()" ] @@ -59,16 +62,38 @@ "metadata": {}, "outputs": [], "source": [ - "def get_osm_traces(page=0, bbox='16.18,48.09,16.61,48.32'):\n", - " file = 'osm_traces.gpx'\n", - " url = f'https://api.openstreetmap.org/api/0.6/trackpoints?bbox={bbox}&page={page}'\n", + "def get_osm_traces(page=0, bbox=\"16.18,48.09,16.61,48.32\"):\n", + " file = \"osm_traces.gpx\"\n", + " url = f\"https://api.openstreetmap.org/api/0.6/trackpoints?bbox={bbox}&page={page}\"\n", " if not exists(file):\n", " urlretrieve(url, file)\n", - " gdf = gpd.read_file(file, layer='track_points')\n", + " gdf = gpd.read_file(file, layer=\"track_points\")\n", " # OPTIONAL: dropping empty columns\n", - " gdf.drop(columns=['ele', 'course', 'speed', 'magvar', 'geoidheight', 'name', 'cmt', 'desc',\n", - " 'src', 'url', 'urlname', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop',\n", - " 'pdop', 'ageofdgpsdata', 'dgpsid'], inplace=True) \n", + " gdf.drop(\n", + " columns=[\n", + " \"ele\",\n", + " \"course\",\n", + " \"speed\",\n", + " \"magvar\",\n", + " \"geoidheight\",\n", + " \"name\",\n", + " \"cmt\",\n", + " \"desc\",\n", + " \"src\",\n", + " \"url\",\n", + " \"urlname\",\n", + " \"sym\",\n", + " \"type\",\n", + " \"fix\",\n", + " \"sat\",\n", + " \"hdop\",\n", + " \"vdop\",\n", + " \"pdop\",\n", + " \"ageofdgpsdata\",\n", + " \"dgpsid\",\n", + " ],\n", + " inplace=True,\n", + " )\n", " return gdf" ] }, @@ -86,8 +111,8 @@ "outputs": [], "source": [ "gdf = get_osm_traces()\n", - "osm_traces = mpd.TrajectoryCollection(gdf, 'track_fid', t='time')\n", - "print(f'The OSM traces download contains {len(osm_traces)} tracks')" + "osm_traces = mpd.TrajectoryCollection(gdf, \"track_fid\", t=\"time\")\n", + "print(f\"The OSM traces download contains {len(osm_traces)} tracks\")" ] }, { @@ -96,7 +121,8 @@ "metadata": {}, "outputs": [], "source": [ - "for track in osm_traces: print(f'Track {track.id}: length={track.get_length(units=\"km\"):.2f} km')" + "for track in osm_traces:\n", + " print(f'Track {track.id}: length={track.get_length(units=\"km\"):.2f} km')" ] }, { @@ -108,6 +134,15 @@ "track.plot()" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "track.explore(color=\"red\")" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -123,8 +158,24 @@ "metadata": {}, "outputs": [], "source": [ - "osm_traces = mpd.MinTimeDeltaGeneralizer(osm_traces).generalize(tolerance=timedelta(minutes=1))\n", - "osm_traces.hvplot(title='OSM Traces', line_width=7, width=700, height=400)" + "osm_traces = mpd.MinTimeDeltaGeneralizer(osm_traces).generalize(\n", + " tolerance=timedelta(minutes=1)\n", + ")\n", + "osm_traces.hvplot(title=\"OSM Traces\", line_width=7, width=700, height=400)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "osm_traces.explore(\n", + " column=\"track_fid\",\n", + " tiles=\"cartoDB positron\",\n", + " cmap=\"gist_rainbow\",\n", + " style_kwds={\"weight\": 4},\n", + ")" ] }, { @@ -133,10 +184,17 @@ "metadata": {}, "outputs": [], "source": [ - "osm_traces.trajectories[0].add_speed(overwrite=True, units=(\"km\",\"h\"))\n", + "osm_traces.trajectories[0].add_speed(overwrite=True, units=(\"km\", \"h\"))\n", "osm_traces.trajectories[0].hvplot(\n", - " title='Speed (km/h) along track', c='speed', cmap='RdYlBu',\n", - " line_width=7, width=700, height=400, tiles='CartoLight', colorbar=True)" + " title=\"Speed (km/h) along track\",\n", + " c=\"speed\",\n", + " cmap=\"RdYlBu\",\n", + " line_width=7,\n", + " width=700,\n", + " height=400,\n", + " tiles=\"CartoLight\",\n", + " colorbar=True,\n", + ")" ] }, { @@ -152,7 +210,8 @@ "1. [Soccer game](soccer-game.ipynb)\n", "1. [Mars rover & heli](mars-rover.ipynb)\n", "1. [Ever Given](ever-given.ipynb)\n", - "1. [Iceberg](iceberg.ipynb)" + "1. [Iceberg](iceberg.ipynb)\n", + "1. [Pollution data](pollution-data.ipynb)" ] } ], diff --git a/2-analysis-examples/ship-data.ipynb b/2-analysis-examples/ship-data.ipynb index fa9ba89..4ebeee7 100644 --- a/2-analysis-examples/ship-data.ipynb +++ b/2-analysis-examples/ship-data.ipynb @@ -40,8 +40,9 @@ "import geopandas as gpd\n", "import movingpandas as mpd\n", "import shapely as shp\n", - "import hvplot.pandas \n", + "import hvplot.pandas\n", "import matplotlib.pyplot as plt\n", + "import folium\n", "\n", "from geopandas import GeoDataFrame, read_file\n", "from shapely.geometry import Point, LineString, Polygon\n", @@ -51,11 +52,14 @@ "from urllib.request import urlretrieve\n", "\n", "import warnings\n", - "warnings.filterwarnings('ignore')\n", "\n", - "plot_defaults = {'linewidth':5, 'capstyle':'round', 'figsize':(9,3), 'legend':True}\n", - "opts.defaults(opts.Overlay(active_tools=['wheel_zoom'], frame_width=500, frame_height=400))\n", - "hvplot_defaults = {'tiles':None, 'cmap':'Viridis', 'colorbar':True}\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "plot_defaults = {\"linewidth\": 5, \"capstyle\": \"round\", \"figsize\": (9, 3), \"legend\": True}\n", + "opts.defaults(\n", + " opts.Overlay(active_tools=[\"wheel_zoom\"], frame_width=500, frame_height=400)\n", + ")\n", + "hvplot_defaults = {\"tiles\": None, \"cmap\": \"Viridis\", \"colorbar\": True}\n", "\n", "mpd.show_versions()" ] @@ -74,7 +78,7 @@ "outputs": [], "source": [ "%%time\n", - "df = read_file('../data/ais.gpkg')\n", + "df = read_file(\"../data/ais.gpkg\")\n", "print(f\"Finished reading {len(df)}\")" ] }, @@ -116,7 +120,7 @@ "metadata": {}, "outputs": [], "source": [ - "df['SOG'].hist(bins=100, figsize=(15,3))" + "df[\"SOG\"].hist(bins=100, figsize=(15, 3))" ] }, { @@ -133,9 +137,9 @@ "outputs": [], "source": [ "print(f\"Original size: {len(df)} rows\")\n", - "df = df[df.SOG>0]\n", + "df = df[df.SOG > 0]\n", "print(f\"Reduced to {len(df)} rows after removing 0 speed records\")\n", - "df['SOG'].hist(bins=100, figsize=(15,3))" + "df[\"SOG\"].hist(bins=100, figsize=(15, 3))" ] }, { @@ -151,7 +155,7 @@ "metadata": {}, "outputs": [], "source": [ - "df['ShipType'].value_counts().plot(kind='bar', figsize=(15,3))" + "df[\"ShipType\"].value_counts().plot(kind=\"bar\", figsize=(15, 3))" ] }, { @@ -168,8 +172,8 @@ "outputs": [], "source": [ "%%time\n", - "df['t'] = pd.to_datetime(df['Timestamp'], format='%d/%m/%Y %H:%M:%S')\n", - "traj_collection = mpd.TrajectoryCollection(df, 'MMSI', t='t', min_length=100)\n", + "df[\"t\"] = pd.to_datetime(df[\"Timestamp\"], format=\"%d/%m/%Y %H:%M:%S\")\n", + "traj_collection = mpd.TrajectoryCollection(df, \"MMSI\", t=\"t\", min_length=100)\n", "print(f\"Finished creating {len(traj_collection)} trajectories\")" ] }, @@ -179,7 +183,9 @@ "metadata": {}, "outputs": [], "source": [ - "traj_collection = mpd.MinTimeDeltaGeneralizer(traj_collection).generalize(tolerance=timedelta(minutes=1))" + "traj_collection = mpd.MinTimeDeltaGeneralizer(traj_collection).generalize(\n", + " tolerance=timedelta(minutes=1)\n", + ")" ] }, { @@ -197,10 +203,27 @@ "metadata": {}, "outputs": [], "source": [ - "shiptype_to_color = {'Passenger': 'blue', 'HSC': 'green', 'Tanker': 'red', 'Cargo': 'orange', 'Sailing': 'grey', 'Other': 'grey', \n", - " 'Tug': 'grey', 'SAR': 'grey', 'Undefined': 'grey', 'Pleasure': 'grey', 'Dredging': 'grey', 'Law enforcement': 'grey',\n", - " 'Pilot': 'grey', 'Fishing': 'grey', 'Diving':'grey', 'Spare 2': 'grey'}\n", - "traj_collection.plot(column='ShipType', column_to_color=shiptype_to_color, linewidth=1, capstyle='round')" + "shiptype_to_color = {\n", + " \"Passenger\": \"blue\",\n", + " \"HSC\": \"green\",\n", + " \"Tanker\": \"red\",\n", + " \"Cargo\": \"orange\",\n", + " \"Sailing\": \"grey\",\n", + " \"Other\": \"grey\",\n", + " \"Tug\": \"grey\",\n", + " \"SAR\": \"grey\",\n", + " \"Undefined\": \"grey\",\n", + " \"Pleasure\": \"grey\",\n", + " \"Dredging\": \"grey\",\n", + " \"Law enforcement\": \"grey\",\n", + " \"Pilot\": \"grey\",\n", + " \"Fishing\": \"grey\",\n", + " \"Diving\": \"grey\",\n", + " \"Spare 2\": \"grey\",\n", + "}\n", + "traj_collection.plot(\n", + " column=\"ShipType\", column_to_color=shiptype_to_color, linewidth=1, capstyle=\"round\"\n", + ")" ] }, { @@ -209,8 +232,21 @@ "metadata": {}, "outputs": [], "source": [ - "passenger = traj_collection.filter('ShipType', 'Passenger')\n", - "passenger.hvplot(title='Passenger ferries', line_width=2, frame_width=700, frame_height=500)" + "passenger = traj_collection.filter(\"ShipType\", \"Passenger\")\n", + "passenger.hvplot(\n", + " title=\"Passenger ferries\", line_width=2, frame_width=700, frame_height=500\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "passenger.explore(\n", + " column=\"MMSI\", cmap=\"turbo\", tiles=\"CartoDB positron\", tooltip=\"Name\", popup=True\n", + ")" ] }, { @@ -252,7 +288,32 @@ "metadata": {}, "outputs": [], "source": [ - "my_traj.hvplot(title=f'Trajectory {my_traj.id}', frame_width=700, frame_height=500, line_width=5.0, c='NavStatus', cmap='Dark2') " + "my_traj.hvplot(\n", + " title=f\"Trajectory {my_traj.id}\",\n", + " frame_width=700,\n", + " frame_height=500,\n", + " line_width=5.0,\n", + " c=\"NavStatus\",\n", + " cmap=\"Dark2\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or the changes in speed:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_traj.explore(\n", + " column=\"SOG\", cmap=\"plasma\", tiles=\"CartoDB positron\", style_kwds={\"weight\": 5}\n", + ")" ] }, { @@ -269,7 +330,15 @@ "metadata": {}, "outputs": [], "source": [ - "area_of_interest = Polygon([(11.89935, 57.69270), (11.90161, 57.68902), (11.90334, 57.68967), (11.90104, 57.69354), (11.89935, 57.69270)])" + "area_of_interest = Polygon(\n", + " [\n", + " (11.89935, 57.69270),\n", + " (11.90161, 57.68902),\n", + " (11.90334, 57.68967),\n", + " (11.90104, 57.69354),\n", + " (11.89935, 57.69270),\n", + " ]\n", + ")" ] }, { @@ -289,7 +358,23 @@ "outputs": [], "source": [ "bridge_traj = intersecting.trajectories[0]\n", - "bridge_traj.hvplot(title=f'Trajectory {bridge_traj.id}', frame_width=700, frame_height=500, line_width=5.0, c='NavStatus', cmap='Dark2') " + "bridge_traj.hvplot(\n", + " title=f\"Trajectory {bridge_traj.id}\",\n", + " frame_width=700,\n", + " frame_height=500,\n", + " line_width=5.0,\n", + " c=\"NavStatus\",\n", + " cmap=\"Dark2\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bridge_traj.explore(color=\"red\", tiles=\"CartoDB positron\", style_kwds={\"weight\": 5})" ] }, { @@ -322,7 +407,9 @@ "outputs": [], "source": [ "trips = mpd.ObservationGapSplitter(passenger).split(gap=timedelta(minutes=5))\n", - "print(f\"Extracted {len(trips)} individual trips from {len(passenger)} continuous vessel tracks\")" + "print(\n", + " f\"Extracted {len(trips)} individual trips from {len(passenger)} continuous vessel tracks\"\n", + ")" ] }, { @@ -338,7 +425,25 @@ "metadata": {}, "outputs": [], "source": [ - "trips.hvplot(title='Passenger ferry trips', line_width=2, frame_width=700, frame_height=500)" + "trips.hvplot(\n", + " title=\"Passenger ferry trips\", line_width=2, frame_width=700, frame_height=500\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "trips.explore(\n", + " column=\"MMSI\",\n", + " cmap=\"hsv\",\n", + " tiles=\"CartoDB positron\",\n", + " tooltip=\"Name\",\n", + " popup=True,\n", + " legend=False,\n", + ")" ] }, { @@ -362,7 +467,32 @@ "outputs": [], "source": [ "origins = trips.get_start_locations()\n", - "origins.hvplot(title='Trip origins by ship type', c='Name', geo=True, tiles='OSM', frame_width=700, frame_height=500)" + "origins.hvplot(\n", + " title=\"Trip origins by ship type\",\n", + " c=\"Name\",\n", + " geo=True,\n", + " tiles=\"OSM\",\n", + " frame_width=700,\n", + " frame_height=500,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "origins_gdf = gpd.GeoDataFrame(origins, geometry=\"geometry\", crs=4326)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "origins_gdf.explore(column=\"Name\", tiles=\"CartoDB positron\", marker_kwds={\"radius\": 5})" ] }, { @@ -380,7 +510,25 @@ "metadata": {}, "outputs": [], "source": [ - "origins.hvplot(title='Origins by speed', c='SOG', geo=True, tiles='OSM', frame_width=700, frame_height=500)" + "origins.hvplot(\n", + " title=\"Origins by speed\",\n", + " c=\"SOG\",\n", + " geo=True,\n", + " tiles=\"OSM\",\n", + " frame_width=700,\n", + " frame_height=500,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "origins_gdf.explore(\n", + " column=\"SOG\", tiles=\"CartoDB positron\", legend=False, marker_kwds={\"radius\": 5}\n", + ")" ] }, { @@ -397,7 +545,15 @@ "outputs": [], "source": [ "trips = mpd.ObservationGapSplitter(traj_collection).split(gap=timedelta(minutes=5))\n", - "area_of_interest = Polygon([(11.86815, 57.68273), (11.86992, 57.68047), (11.87419, 57.68140), (11.87288, 57.68348), (11.86815, 57.68273)])" + "area_of_interest = Polygon(\n", + " [\n", + " (11.86815, 57.68273),\n", + " (11.86992, 57.68047),\n", + " (11.87419, 57.68140),\n", + " (11.87288, 57.68348),\n", + " (11.86815, 57.68273),\n", + " ]\n", + ")" ] }, { @@ -413,7 +569,12 @@ "metadata": {}, "outputs": [], "source": [ - "departures = [traj for traj in trips if traj.get_start_location().intersects(area_of_interest) and traj.get_length() > 100] \n", + "departures = [\n", + " traj\n", + " for traj in trips\n", + " if traj.get_start_location().intersects(area_of_interest)\n", + " and traj.get_length() > 100\n", + "]\n", "print(f\"Found {len(departures)} departures\")" ] }, @@ -424,7 +585,13 @@ "outputs": [], "source": [ "tc = mpd.TrajectoryCollection(departures)\n", - "tc.hvplot(title=f'Ships departing from Sjöfartsverket', line_width=3, frame_width=700, frame_height=500, hover_cols=['Name']) " + "tc.hvplot(\n", + " title=f\"Ships departing from Sjöfartsverket\",\n", + " line_width=3,\n", + " frame_width=700,\n", + " frame_height=500,\n", + " hover_cols=[\"Name\"],\n", + ")" ] }, { @@ -441,7 +608,9 @@ "outputs": [], "source": [ "for traj in departures:\n", - " print(f\"{traj.df['ShipType'].iloc[0]} vessel '{traj.df['Name'].iloc[0]}' departed at {traj.get_start_time()}\")" + " print(\n", + " f\"{traj.df['ShipType'].iloc[0]} vessel '{traj.df['Name'].iloc[0]}' departed at {traj.get_start_time()}\"\n", + " )" ] }, { @@ -457,11 +626,17 @@ "metadata": {}, "outputs": [], "source": [ - "arrivals = [traj for traj in trips if traj.get_end_location().intersects(area_of_interest) and traj.get_length() > 100]\n", + "arrivals = [\n", + " traj\n", + " for traj in trips\n", + " if traj.get_end_location().intersects(area_of_interest) and traj.get_length() > 100\n", + "]\n", "print(f\"Found {len(arrivals)} arrivals\")\n", "\n", "for traj in arrivals:\n", - " print(f\"{traj.df['ShipType'].iloc[0]} vessel '{traj.df['Name'].iloc[0]}' arrived at {traj.get_end_time()}\")" + " print(\n", + " f\"{traj.df['ShipType'].iloc[0]} vessel '{traj.df['Name'].iloc[0]}' arrived at {traj.get_end_time()}\"\n", + " )" ] }, { @@ -471,7 +646,13 @@ "outputs": [], "source": [ "tc = mpd.TrajectoryCollection(arrivals)\n", - "tc.hvplot(title=f'Ships arriving in Sjöfartsverket', line_width=3, frame_width=700, frame_height=500, hover_cols=['Name']) " + "tc.hvplot(\n", + " title=f\"Ships arriving in Sjöfartsverket\",\n", + " line_width=3,\n", + " frame_width=700,\n", + " frame_height=500,\n", + " hover_cols=[\"Name\"],\n", + ")" ] }, { @@ -501,9 +682,9 @@ "outputs": [], "source": [ "origins = trips.get_start_locations()\n", - "origins['lat'] = origins.geometry.y\n", - "origins['lon'] = origins.geometry.x\n", - "matrix = origins[['lat','lon']].values" + "origins[\"lat\"] = origins.geometry.y\n", + "origins[\"lon\"] = origins.geometry.x\n", + "matrix = origins[[\"lat\", \"lon\"]].values" ] }, { @@ -522,11 +703,13 @@ "metadata": {}, "outputs": [], "source": [ - "db = DBSCAN(eps=epsilon, min_samples=1, algorithm='ball_tree', metric='haversine').fit(np.radians(matrix))\n", + "db = DBSCAN(eps=epsilon, min_samples=1, algorithm=\"ball_tree\", metric=\"haversine\").fit(\n", + " np.radians(matrix)\n", + ")\n", "cluster_labels = db.labels_\n", "num_clusters = len(set(cluster_labels))\n", "clusters = pd.Series([matrix[cluster_labels == n] for n in range(num_clusters)])\n", - "print(f'Number of clusters: {num_clusters}')" + "print(f\"Number of clusters: {num_clusters}\")" ] }, { @@ -535,7 +718,7 @@ "metadata": {}, "outputs": [], "source": [ - "origins['cluster'] = cluster_labels" + "origins[\"cluster\"] = cluster_labels" ] }, { @@ -548,6 +731,8 @@ " centroid = (MultiPoint(cluster).centroid.x, MultiPoint(cluster).centroid.y)\n", " centermost_point = min(cluster, key=lambda point: great_circle(point, centroid).m)\n", " return Point(tuple(centermost_point)[1], tuple(centermost_point)[0])\n", + "\n", + "\n", "centermost_points = clusters.map(get_centermost_point)" ] }, @@ -557,7 +742,15 @@ "metadata": {}, "outputs": [], "source": [ - "origins.hvplot(title='Clustered origins', c='cluster', geo=True, tiles='OSM', cmap='glasbey_dark', frame_width=700, frame_height=500)" + "origins.hvplot(\n", + " title=\"Clustered origins\",\n", + " c=\"cluster\",\n", + " geo=True,\n", + " tiles=\"OSM\",\n", + " cmap=\"glasbey_dark\",\n", + " frame_width=700,\n", + " frame_height=500,\n", + ")" ] }, { @@ -566,12 +759,12 @@ "metadata": {}, "outputs": [], "source": [ - "origins_by_cluster = pd.DataFrame(origins).groupby(['cluster'])\n", - "summary = origins_by_cluster['ShipType'].unique().to_frame(name='types')\n", - "summary['n'] = origins_by_cluster.size()\n", - "summary['sog'] = origins_by_cluster['SOG'].mean()\n", - "summary['geometry'] = centermost_points\n", - "summary = summary[summary['n']>1].sort_values(by='n', ascending=False)\n", + "origins_by_cluster = pd.DataFrame(origins).groupby([\"cluster\"])\n", + "summary = origins_by_cluster[\"ShipType\"].unique().to_frame(name=\"types\")\n", + "summary[\"n\"] = origins_by_cluster.size()\n", + "summary[\"sog\"] = origins_by_cluster[\"SOG\"].mean()\n", + "summary[\"geometry\"] = centermost_points\n", + "summary = summary[summary[\"n\"] > 1].sort_values(by=\"n\", ascending=False)\n", "summary.head()" ] }, @@ -582,8 +775,14 @@ "outputs": [], "source": [ "cluster_of_interest_id = 28\n", - "origins[origins['cluster']==cluster_of_interest_id].hvplot(\n", - " title=f'Cluster {cluster_of_interest_id}', c='ShipType', geo=True, tiles='OSM', frame_width=700, frame_height=500)" + "origins[origins[\"cluster\"] == cluster_of_interest_id].hvplot(\n", + " title=f\"Cluster {cluster_of_interest_id}\",\n", + " c=\"ShipType\",\n", + " geo=True,\n", + " tiles=\"OSM\",\n", + " frame_width=700,\n", + " frame_height=500,\n", + ")" ] }, { @@ -592,11 +791,51 @@ "metadata": {}, "outputs": [], "source": [ - "( trips.hvplot(title='Origin clusters by speed', color='gray', line_width=1, frame_width=700, frame_height=500) *\n", - " GeoDataFrame(summary, crs=4326).hvplot(c='sog', size=np.sqrt(dim('n'))*3, geo=True, cmap='RdYlGn')\n", + "(\n", + " trips.hvplot(\n", + " title=\"Origin clusters by speed\",\n", + " color=\"gray\",\n", + " line_width=1,\n", + " frame_width=700,\n", + " frame_height=500,\n", + " )\n", + " * GeoDataFrame(summary, crs=4326).hvplot(\n", + " c=\"sog\", size=np.sqrt(dim(\"n\")) * 3, geo=True, cmap=\"RdYlGn\"\n", + " )\n", ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "summary_gdf = gpd.GeoDataFrame(summary, crs=4326)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = trips.explore(name=\"Trips\", style_kwds={\"weight\": 1})\n", + "\n", + "summary_gdf.explore(\n", + " m=m,\n", + " column=\"sog\",\n", + " legend=False,\n", + " style_kwds={\"style_function\": lambda x: {\"radius\": x[\"properties\"][\"n\"]}},\n", + " name=\"Clusters\",\n", + ")\n", + "\n", + "folium.TileLayer(\"CartoDB positron\").add_to(m)\n", + "folium.LayerControl().add_to(m)\n", + "\n", + "m" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -610,7 +849,8 @@ "1. [Soccer game](soccer-game.ipynb)\n", "1. [Mars rover & heli](mars-rover.ipynb)\n", "1. [Ever Given](ever-given.ipynb)\n", - "1. [Iceberg](iceberg.ipynb)" + "1. [Iceberg](iceberg.ipynb)\n", + "1. [Pollution data](pollution-data.ipynb)" ] } ], From 62b84eb0b4223eee3d62cde6baafeb923eec865d Mon Sep 17 00:00:00 2001 From: mar3021 Date: Wed, 14 Aug 2024 16:53:05 +0200 Subject: [PATCH 3/6] Add explore() function examples --- .../5-intersecting-with-polygons.ipynb | 84 +++++++++++++++---- 1 file changed, 70 insertions(+), 14 deletions(-) diff --git a/1-tutorials/5-intersecting-with-polygons.ipynb b/1-tutorials/5-intersecting-with-polygons.ipynb index 87a1f0d..b407b59 100644 --- a/1-tutorials/5-intersecting-with-polygons.ipynb +++ b/1-tutorials/5-intersecting-with-polygons.ipynb @@ -26,7 +26,8 @@ "import geopandas as gpd\n", "import movingpandas as mpd\n", "import shapely as shp\n", - "import hvplot.pandas \n", + "import hvplot.pandas\n", + "import folium\n", "\n", "from geopandas import GeoDataFrame, read_file\n", "from shapely.geometry import Point, LineString, Polygon\n", @@ -34,9 +35,12 @@ "from holoviews import opts\n", "\n", "import warnings\n", - "warnings.filterwarnings('ignore')\n", "\n", - "opts.defaults(opts.Overlay(active_tools=['wheel_zoom'], frame_width=500, frame_height=400))\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "opts.defaults(\n", + " opts.Overlay(active_tools=[\"wheel_zoom\"], frame_width=500, frame_height=400)\n", + ")\n", "\n", "mpd.show_versions()" ] @@ -47,8 +51,8 @@ "metadata": {}, "outputs": [], "source": [ - "gdf = read_file('../data/geolife_small.gpkg')\n", - "tc = mpd.TrajectoryCollection(gdf, 'trajectory_id', t='t')" + "gdf = read_file(\"../data/geolife_small.gpkg\")\n", + "tc = mpd.TrajectoryCollection(gdf, \"trajectory_id\", t=\"t\")" ] }, { @@ -65,12 +69,14 @@ "metadata": {}, "outputs": [], "source": [ - "xmin, xmax, ymin, ymax = 116.365035,116.3702945,39.904675,39.907728\n", - "polygon = Polygon([(xmin,ymin), (xmin,ymax), (xmax,ymax), (xmax,ymin), (xmin,ymin)])\n", + "xmin, xmax, ymin, ymax = 116.365035, 116.3702945, 39.904675, 39.907728\n", + "polygon = Polygon(\n", + " [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin), (xmin, ymin)]\n", + ")\n", "\n", "my_traj = tc.trajectories[2]\n", "intersections = my_traj.clip(polygon)\n", - " \n", + "\n", "print(\"Found {} intersections\".format(len(intersections)))" ] }, @@ -81,8 +87,24 @@ "outputs": [], "source": [ "ax = my_traj.plot()\n", - "gpd.GeoSeries(polygon).plot(ax=ax, color='lightgray')\n", - "intersections.plot(ax=ax, color='red', linewidth=5)" + "gpd.GeoSeries(polygon).plot(ax=ax, color=\"lightgray\")\n", + "intersections.plot(ax=ax, color=\"red\", linewidth=5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = my_traj.explore(color=\"blue\", style_kwds={\"weight\": 4}, name=\"Trajectory\")\n", + "\n", + "intersections.explore(m=m, color=\"red\", style_kwds={\"weight\": 4}, name=\"Intersection\")\n", + "\n", + "folium.TileLayer(\"OpenStreetMap\").add_to(m)\n", + "folium.LayerControl().add_to(m)\n", + "\n", + "m" ] }, { @@ -120,6 +142,20 @@ "clipped.plot()" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "clipped.explore(\n", + " column=\"trajectory_id\",\n", + " cmap=\"cool\",\n", + " tiles=\"CartoDB positron\",\n", + " style_kwds={\"weight\": 4},\n", + ")" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -134,10 +170,7 @@ "metadata": {}, "outputs": [], "source": [ - "polygon_feature = {\n", - " \"geometry\": polygon,\n", - " \"properties\": {'field1': 'abc'}\n", - "}" + "polygon_feature = {\"geometry\": polygon, \"properties\": {\"field1\": \"abc\"}}" ] }, { @@ -160,6 +193,15 @@ "intersections.plot()" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "intersections.explore(color=\"blue\", style_kwds={\"weight\": 4})" + ] + }, { "cell_type": "code", "execution_count": null, @@ -195,6 +237,20 @@ "intersections.plot()" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "intersections.explore(\n", + " column=\"trajectory_id\",\n", + " cmap=\"autumn\",\n", + " tiles=\"CartoDB positron\",\n", + " style_kwds={\"weight\": 4},\n", + ")" + ] + }, { "cell_type": "code", "execution_count": null, From ebb01a8ba5cffdd39f3d3eb949408f53afdae7df Mon Sep 17 00:00:00 2001 From: mar3021 Date: Mon, 26 Aug 2024 10:22:32 +0200 Subject: [PATCH 4/6] Add explore() function examples --- 2-analysis-examples/horse-collar.ipynb | 1 - 1 file changed, 1 deletion(-) diff --git a/2-analysis-examples/horse-collar.ipynb b/2-analysis-examples/horse-collar.ipynb index 3707b23..2990bd9 100644 --- a/2-analysis-examples/horse-collar.ipynb +++ b/2-analysis-examples/horse-collar.ipynb @@ -980,7 +980,6 @@ "outputs": [], "source": [ "stops_gdf.explore(\n", - " color=\"purple\",\n", " tiles=\"CartoDB positron\",\n", " tooltip=\"traj_id\",\n", " popup=True,\n", From e889424f1da9152f470fb80130ec08f7156b1d7d Mon Sep 17 00:00:00 2001 From: mar3021 Date: Mon, 26 Aug 2024 10:46:53 +0200 Subject: [PATCH 5/6] Add explore() function examples --- 2-analysis-examples/ship-data.ipynb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/2-analysis-examples/ship-data.ipynb b/2-analysis-examples/ship-data.ipynb index 4ebeee7..f3c9096 100644 --- a/2-analysis-examples/ship-data.ipynb +++ b/2-analysis-examples/ship-data.ipynb @@ -245,7 +245,12 @@ "outputs": [], "source": [ "passenger.explore(\n", - " column=\"MMSI\", cmap=\"turbo\", tiles=\"CartoDB positron\", tooltip=\"Name\", popup=True\n", + " column=\"MMSI\",\n", + " cmap=\"turbo\",\n", + " tiles=\"CartoDB positron\",\n", + " tooltip=\"Name\",\n", + " popup=True,\n", + " legend=False,\n", ")" ] }, From b9be7285bcc9aa6717e9204bb1281020a333fca9 Mon Sep 17 00:00:00 2001 From: mar3021 Date: Mon, 26 Aug 2024 14:44:23 +0200 Subject: [PATCH 6/6] Update environment.yml --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index e1c31e6..5b555f4 100644 --- a/environment.yml +++ b/environment.yml @@ -4,6 +4,7 @@ channels: dependencies: - python>=3.12 - pip + - geopandas - jupyter_client<8 - notebook - pandas @@ -12,4 +13,3 @@ dependencies: - movingpandas - geoviews - hvplot - - geopandas==1.0.1