Beispiel #1
0
 def test_crs_warning(self):
     """Check if warning is raised for data without crs."""
     file = os.path.join("tests", "data", "positionfixes.csv")
     pfs = ti.read_positionfixes_csv(file,
                                     sep=";",
                                     crs=None,
                                     index_col=None)
     with pytest.warns(UserWarning):
         check_gdf_crs(pfs)
Beispiel #2
0
 def test_transformation(self):
     """Check if data gets transformed."""
     file = os.path.join("tests", "data", "positionfixes.csv")
     pfs = ti.read_positionfixes_csv(file,
                                     sep=";",
                                     crs="EPSG:4326",
                                     index_col=None)
     pfs_2056 = pfs.to_crs("EPSG:2056")
     _, pfs_4326 = check_gdf_crs(pfs_2056, transform=True)
     assert_geodataframe_equal(pfs, pfs_4326, check_less_precise=True)
Beispiel #3
0
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_crs(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()
Beispiel #4
0
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_crs(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
Beispiel #5
0
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_crs(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
Beispiel #6
0
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
    --------
    >>> stps.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_crs(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()
Beispiel #7
0
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=stps, plot_osm=True)
    """
    if axis is None:
        _, ax = regular_figure()
    else:
        ax = axis
    _, triplegs = check_gdf_crs(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()
Beispiel #8
0
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=stps, plot_osm=True)
    """
    if axis is None:
        _, ax = regular_figure()
    else:
        ax = axis
    _, locations = check_gdf_crs(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()