def plot_positionfixes(positionfixes, out_filename=None, plot_osm=False, axis=None): """Plots positionfixes (optionally to a file). If you specify ``plot_osm=True`` this will use ``osmnx`` to plot streets below the positionfixes. Depending on the extent of your data, this might take a long time. The data gets transformed to wgs84 for the plotting. Parameters ---------- positionfixes : GeoDataFrame (as trackintel positionfixes) The positionfixes to plot. out_filename : str, optional The file to plot to, if this is not set, the plot will simply be shown. plot_osm : bool, default False If this is set to True, it will download an OSM street network and plot below the staypoints. axis : matplotlib.pyplot.Artist, optional axis on which to draw the plot Examples -------- >>> pfs.as_positionfixes.plot('output.png', plot_osm=True) """ if axis is None: _, ax = regular_figure() else: ax = axis _, positionfixes = check_gdf_planar(positionfixes, transform=True) if plot_osm: west = positionfixes.geometry.x.min() east = positionfixes.geometry.x.max() north = positionfixes.geometry.y.max() south = positionfixes.geometry.y.min() plot_osm_streets(north, south, east, west, ax) positionfixes.plot(ax=ax, markersize=0.5, zorder=2) ax.set_aspect("equal", adjustable="box") if out_filename is not None: save_fig(out_filename, formats=["png"]) elif axis is None: plt.show()
def _predict_transport_mode_simple_coarse(triplegs_in, categories): """ Predict a transport mode out of three coarse classes. Implements a simple speed based heuristic (over the whole tripleg). As such, it is very fast, but also very simple and coarse. Parameters ---------- triplegs_in : GeoDataFrame (as trackintel triplegs) The triplegs for the transport mode prediction. categories : dict, optional The categories for the speed classification {upper_boundary:'category_name'}. The unit for the upper boundary is m/s. The default is {15/3.6: 'slow_mobility', 100/3.6: 'motorized_mobility', np.inf: 'fast_mobility'}. Raises ------ ValueError In case the boundaries of the categories are not in ascending order. Returns ------- triplegs : trackintel triplegs GeoDataFrame the triplegs with added column mode, containing the predicted transport modes. For additional documentation, see :func:`trackintel.analysis.transport_mode_identification.predict_transport_mode`. """ if not (_check_categories(categories)): raise ValueError("the categories must be in increasing order") triplegs = triplegs_in.copy() if_planer_crs = check_gdf_planar(triplegs) # if not if_planer_crs: triplegs["distance"] = calculate_haversine_length(triplegs) else: triplegs["distance"] = triplegs.length def identify_mode(tripleg, categories): """ Identify the mode based on the (overall) tripleg speed. Parameters ---------- tripleg : trackintel triplegs GeoDataFrame the tripleg to analyse wgs : bool whether the tripleg is in WGS84 or not. categories : dict the upper boundaries (as keys) and the names of the categories as values. Returns ------- str the identified mode. """ duration = (tripleg["finished_at"] - tripleg["started_at"]).total_seconds() speed = tripleg["distance"] / duration # The unit of the speed is m/s for bound in categories: if speed < bound: return categories[bound] triplegs["mode"] = triplegs.apply(identify_mode, args=((categories,)), axis=1) return triplegs
def plot_triplegs(triplegs, out_filename=None, positionfixes=None, staypoints=None, staypoints_radius=100, plot_osm=False, axis=None): """Plot triplegs (optionally to a file). You can specify several other datasets to be plotted beneath the triplegs, as well as if the OSM streets should be drawn. The data gets transformed to wgs84 for the plotting. Parameters ---------- triplegs : GeoDataFrame (as trackintel triplegs) The triplegs to plot. out_filename : str, optional The file to plot to, if this is not set, the plot will simply be shown. positionfixes : GeoDataFrame (as trackintel positionfixes), optional If available, some positionfixes that can additionally be plotted. staypoints : GeoDataFrame (as trackintel staypoints), optional If available, some staypoints that can additionally be plotted. staypoints_radius : float, default 100 (meter) The radius in meter with which circles around staypoints should be drawn. plot_osm : bool, default False If this is set to True, it will download an OSM street network and plot below the triplegs. axis : matplotlib.pyplot.Artist, optional axis on which to draw the plot Example ------- >>> tpls.as_triplegs.plot('output.png', positionfixes=pfs, staypoints=sp, plot_osm=True) """ if axis is None: _, ax = regular_figure() else: ax = axis _, triplegs = check_gdf_planar(triplegs, transform=True) if staypoints is not None: staypoints.as_staypoints.plot(radius=staypoints_radius, positionfixes=positionfixes, plot_osm=plot_osm, axis=ax) elif positionfixes is not None: positionfixes.as_positionfixes.plot(plot_osm=plot_osm, axis=ax) elif plot_osm: triplegs_bounds = triplegs.bounds west = min(triplegs_bounds.minx ) - 0.03 # TODO: maybe a relative value instead of 0.03 east = max(triplegs_bounds.maxx) + 0.03 north = max(triplegs_bounds.maxy) + 0.03 south = min(triplegs_bounds.miny) - 0.03 plot_osm_streets(north, south, east, west, ax) triplegs.plot(ax=ax, cmap="viridis") ax.set_aspect("equal", adjustable="box") if out_filename is not None: save_fig(out_filename, formats=["png"]) else: plt.show()
def calculate_modal_split(tpls_in, freq=None, metric="count", per_user=False, norm=False): """Calculate the modal split of triplegs Parameters ---------- tpls_in : GeoDataFrame (as trackintel triplegs) triplegs require the column `mode`. freq : str frequency string passed on as `freq` keyword to the pandas.Grouper class. If `freq=None` the modal split is calculated on all data. A list of possible values can be found `here <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset -aliases>`_. metric : {'count', 'distance', 'duration'} Aggregation used to represent the modal split. 'distance' returns in the same unit as the crs. 'duration' returns values in seconds. per_user : bool, default: False If True the modal split is calculated per user norm : bool, default: False If True every row of the modal split is normalized to 1 Returns ------- modal_split : DataFrame The modal split represented as pandas Dataframe with (optionally) a multi-index. The index can have the levels: `('user_id', 'timestamp')` and every mode as a column. Notes ------ `freq='W-MON'` is used for a weekly aggregation that starts on mondays. If `freq=None` and `per_user=False` are passed the modal split collapses to a single column. The modal split can be visualized using :func:`trackintel.visualization.modal_split.plot_modal_split` Examples -------- >>> triplegs.calculate_modal_split() >>> tripleg.calculate_modal_split(freq='W-MON', metric='distance') """ tpls = tpls_in.copy() # precalculate distance and duration if required if metric == "distance": if_planer_crs = check_gdf_planar(tpls) if not if_planer_crs: tpls["distance"] = calculate_haversine_length(tpls) else: tpls["distance"] = tpls.length elif metric == "duration": tpls["duration"] = tpls["finished_at"] - tpls["started_at"] # create grouper if freq is None: if per_user: tpls_grouper = tpls.groupby(["user_id", "mode"]) else: tpls_grouper = tpls.groupby(["mode"]) else: tpls.set_index("started_at", inplace=True) tpls.index.name = "timestamp" if per_user: tpls_grouper = tpls.groupby(["user_id", "mode", pd.Grouper(freq=freq)]) else: tpls_grouper = tpls.groupby(["mode", pd.Grouper(freq=freq)]) # aggregate if metric == "count": modal_split = tpls_grouper["mode"].count() elif metric == "distance": modal_split = tpls_grouper["distance"].sum() elif metric == "duration": modal_split = tpls_grouper["duration"].sum() modal_split = modal_split.dt.total_seconds() # move 'mode' to columns modal_split.name = "modal_split" modal_split = pd.DataFrame(modal_split) # if mode is the only index, we replace it with zeros so that everything gets aggregated into a single row modal_split["mode"] = modal_split.index.get_level_values("mode") if modal_split.index.nlevels == 1: modal_split.index = 0 * np.arange(0, modal_split.shape[0]) else: modal_split = modal_split.droplevel("mode") # transform Dataframe such that: # - unique mode names are column names # - time/user_id are the indices modal_split = modal_split.pivot_table(index=modal_split.index, columns="mode", values="modal_split") modal_split.fillna(0, inplace=True) if norm: # norm rows to 1 modal_split = modal_split.div(modal_split.sum(axis=1), axis=0) return modal_split
def plot_staypoints(staypoints, out_filename=None, radius=100, positionfixes=None, plot_osm=False, axis=None): """Plot staypoints (optionally to a file). You can specify the radius with which each staypoint should be drawn, as well as if underlying positionfixes and OSM streets should be drawn. The data gets transformed to wgs84 for the plotting. Parameters ---------- staypoints : GeoDataFrame (as trackintel staypoints) The staypoints to plot. out_filename : str, optional The file to plot to, if this is not set, the plot will simply be shown. radius : float, default 100 (meter) The radius in meter with which circles around staypoints should be drawn. positionfixes : GeoDataFrame (as trackintel positionfixes), optional If available, some positionfixes that can additionally be plotted. plot_osm : bool, default False If this is set to True, it will download an OSM street network and plot below the staypoints. axis : matplotlib.pyplot.Artist, optional axis on which to draw the plot Examples -------- >>> sp.as_staypoints.plot('output.png', radius=100, positionfixes=pfs, plot_osm=True) """ if axis is None: _, ax = regular_figure() else: ax = axis name_geocol = staypoints.geometry.name _, staypoints = check_gdf_planar(staypoints, transform=True) if positionfixes is not None: positionfixes.as_positionfixes.plot(plot_osm=plot_osm, axis=ax) else: west = staypoints.geometry.x.min() - 0.03 east = staypoints.geometry.x.max() + 0.03 north = staypoints.geometry.y.max() + 0.03 south = staypoints.geometry.y.min() - 0.03 if plot_osm: plot_osm_streets(north, south, east, west, ax) ax.set_xlim([west, east]) ax.set_ylim([south, north]) center_latitude = (ax.get_ylim()[0] + ax.get_ylim()[1]) / 2 radius = meters_to_decimal_degrees(radius, center_latitude) for pt in staypoints.to_dict("records"): circle = mpatches.Circle((pt[name_geocol].x, pt[name_geocol].y), radius, facecolor="none", edgecolor="g", zorder=3) ax.add_artist(circle) ax.set_aspect("equal", adjustable="box") if out_filename is not None: save_fig(out_filename, formats=["png"]) elif axis is None: plt.show()
def plot_locations( locations, out_filename=None, radius=150, positionfixes=None, staypoints=None, staypoints_radius=100, plot_osm=False, axis=None, ): """Plot locations (optionally to a file). Optionally, you can specify several other datasets to be plotted beneath the locations. Parameters ---------- locations : GeoDataFrame (as trackintel locations) The locations to plot. out_filename : str, optional The file to plot to, if this is not set, the plot will simply be shown. radius : float, default 150 (meter) The radius in meter with which circles around locations should be drawn. positionfixes : GeoDataFrame (as trackintel positionfixes), optional If available, some positionfixes that can additionally be plotted. staypoints : GeoDataFrame (as trackintel staypoints), optional If available, some staypoints that can additionally be plotted. staypoints_radius : float, default 100 (meter) The radius in meter with which circles around staypoints should be drawn. plot_osm : bool, default False If this is set to True, it will download an OSM street network and plot below the staypoints. axis : matplotlib.pyplot.Artist, optional axis on which to draw the plot Examples -------- >>> locs.as_locations.plot('output.png', radius=200, positionfixes=pfs, staypoints=sp, plot_osm=True) """ if axis is None: _, ax = regular_figure() else: ax = axis _, locations = check_gdf_planar(locations, transform=True) if staypoints is not None: staypoints.as_staypoints.plot(radius=staypoints_radius, positionfixes=positionfixes, plot_osm=plot_osm, axis=ax) elif positionfixes is not None: positionfixes.as_positionfixes.plot(plot_osm=plot_osm, axis=ax) elif plot_osm: west = locations["center"].x.min() - 0.03 east = locations["center"].x.max() + 0.03 north = locations["center"].y.max() + 0.03 south = locations["center"].y.min() - 0.03 plot_osm_streets(north, south, east, west, ax) center_latitude = (ax.get_ylim()[0] + ax.get_ylim()[1]) / 2 radius = meters_to_decimal_degrees(radius, center_latitude) for pt in locations.to_dict("records"): circle = mpatches.Circle((pt["center"].x, pt["center"].y), radius, facecolor="none", edgecolor="r", zorder=4) ax.add_artist(circle) ax.set_aspect("equal", adjustable="box") if out_filename is not None: save_fig(out_filename, formats=["png"]) else: plt.show()