Esempio n. 1
0
    def test_meters_to_decimal_degrees(self):
        input_result_dict = {
            1.0: {
                0: 111320,
                23: 102470,
                45: 78710,
                67: 43496
            },
            0.1: {
                0: 11132,
                23: 10247,
                45: 7871,
                67: 4349.6
            },
            0.01: {
                0: 1113.2,
                23: 1024.7,
                45: 787.1,
                67: 434.96
            },
            0.001: {
                0: 111.32,
                23: 102.47,
                45: 78.71,
                67: 43.496
            },
        }

        for degree, lat_output in input_result_dict.items():
            for lat, meters in lat_output.items():
                decimal_degree_output = meters_to_decimal_degrees(meters, lat)
                assert np.isclose(decimal_degree_output, degree, atol=0.1)
Esempio n. 2
0
def findPlaces(stps, dataName, minDist, minPoints):
    """
    Used the result from findStayPoints() to cluster places with DBSCAN   

    Parameters
    ----------
    stps : gdf - staypoints, found by the algorithm of trackintel
    dataName: str - ID of participant
    minDist, minPoints: float - Thresholds for the DBSCAN

    Returns
    -------
    plcs : gdf - The clustered places defined by the DBSCAN

    """
    # Find places
    plcs = stm.cluster_staypoints(stps,
                                  method='dbscan',
                                  epsilon=meters_to_decimal_degrees(
                                      minDist, 47.5),
                                  num_samples=minPoints)
    return plcs
Esempio n. 3
0
def generate_locations(staypoints,
                       method='dbscan',
                       epsilon=100,
                       num_samples=1,
                       distance_matrix_metric='euclidean',
                       agg_level='user'):
    """
    Generate locations from the staypoints.

    Parameters
    ----------
    staypoints : GeoDataFrame (as trackintel staypoints)
        The staypoints have to follow the standard definition for staypoints DataFrames.

    method : {'dbscan'}
        Method to create locations.
        
        - 'dbscan' : Uses the DBSCAN algorithm to cluster staypoints.

    epsilon : float, default 100
        The epsilon for the 'dbscan' method. if 'distance_matrix_metric' is 'haversine' 
        or 'euclidean', the unit is in meters.

    num_samples : int, default 1
        The minimal number of samples in a cluster. 

    distance_matrix_metric: {'haversine', 'euclidean'}
        The distance matrix used by the applied method. Any mentioned below are possible: 
        https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise_distances.html
        
    agg_level: {'user','dataset'}
        The level of aggregation when generating locations:
        
        - 'user'      : locations are generated independently per-user.
        - 'dataset'   : shared locations are generated for all users.
    
    Returns
    -------
    ret_sp: GeoDataFrame (as trackintel staypoints)
        The original staypoints with a new column ``[`location_id`]``.
        
    ret_loc: GeoDataFrame (as trackintel locations)
        The generated locations. 
        
    Examples
    --------
    >>> spts.as_staypoints.generate_locations(method='dbscan', epsilon=100, num_samples=1)
    """
    if agg_level not in ['user', 'dataset']:
        raise AttributeError(
            "The parameter agg_level must be one of ['user', 'dataset'].")
    if method not in ['dbscan']:
        raise AttributeError("The parameter method must be one of ['dbscan'].")

    # initialize the return GeoDataFrames
    ret_stps = staypoints.copy()

    if method == 'dbscan':

        if distance_matrix_metric == 'haversine':
            # The input and output of sklearn's harvarsine metrix are both in radians,
            # see https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise.haversine_distances.html
            # here the 'epsilon' is directly applied to the metric's output.
            # convert to radius
            db = DBSCAN(eps=epsilon / 6371000,
                        min_samples=num_samples,
                        algorithm='ball_tree',
                        metric=distance_matrix_metric)
        else:
            db = DBSCAN(eps=epsilon,
                        min_samples=num_samples,
                        algorithm='ball_tree',
                        metric=distance_matrix_metric)

        if agg_level == 'user':
            location_id_counter = 0
            # TODO: change into groupby
            for user_id_this in ret_stps["user_id"].unique():
                # Slice staypoints array by user. This is not a copy!
                user_staypoints = ret_stps[ret_stps["user_id"] == user_id_this]

                if distance_matrix_metric == 'haversine':
                    # the input is converted to list of (lat, lon) tuples in radians unit
                    p = np.array([[radians(g.y), radians(g.x)]
                                  for g in user_staypoints.geometry])
                else:
                    p = np.array([[g.x, g.y]
                                  for g in user_staypoints.geometry])
                labels = db.fit_predict(p)

                # enforce unique lables across all users without changing noise labels
                max_label = np.max(labels)
                labels[
                    labels != -1] = labels[labels != -1] + location_id_counter
                if max_label > -1:
                    location_id_counter = location_id_counter + max_label + 1

                # add staypoint - location matching to original staypoints
                ret_stps.loc[user_staypoints.index, 'location_id'] = labels
        else:
            if distance_matrix_metric == 'haversine':
                # the input is converted to list of (lat, lon) tuples in radians unit
                p = np.array([[radians(g.y), radians(g.x)]
                              for g in ret_stps.geometry])
            else:
                p = np.array([[g.x, g.y] for g in ret_stps.geometry])
            labels = db.fit_predict(p)

            ret_stps['location_id'] = labels

        ### create locations as grouped staypoints
        temp_sp = ret_stps[['user_id', 'location_id', ret_stps.geometry.name]]
        if agg_level == 'user':
            # directly dissolve by 'user_id' and 'location_id'
            ret_loc = temp_sp.dissolve(by=['user_id', 'location_id'],
                                       as_index=False)
        else:
            ## generate user-location pairs with same geometries across users
            # get user-location pairs
            ret_loc = temp_sp.dissolve(by=['user_id', 'location_id'],
                                       as_index=False).drop(columns={'geom'})
            # get location geometries
            geom_df = temp_sp.dissolve(
                by=['location_id'], as_index=False).drop(columns={'user_id'})
            # merge pairs with location geometries
            ret_loc = ret_loc.merge(geom_df, on='location_id', how='left')

        # filter stps not belonging to locations
        ret_loc = ret_loc.loc[ret_loc['location_id'] != -1]

        ret_loc['center'] = None  # initialize
        # locations with only one staypoints is of type "Point"
        point_idx = ret_loc.geom_type == 'Point'
        if not ret_loc.loc[point_idx].empty:
            ret_loc.loc[point_idx, 'center'] = ret_loc.loc[point_idx, 'geom']
        # locations with multiple staypoints is of type "MultiPoint"
        if not ret_loc.loc[~point_idx].empty:
            ret_loc.loc[~point_idx,
                        'center'] = ret_loc.loc[~point_idx,
                                                'geom'].apply(lambda p: Point(
                                                    np.array(p)[:, 0].mean(),
                                                    np.array(p)[:, 1].mean()))

        # extent is the convex hull of the geometry
        ret_loc['extent'] = None  # initialize
        if not ret_loc.empty:
            ret_loc['extent'] = ret_loc['geom'].apply(lambda p: p.convex_hull)
            # convex_hull of one point would be a Point and two points a Linestring,
            # we change them into Polygon by creating a buffer of epsilon around them.
            pointLine_idx = (ret_loc['extent'].geom_type == 'LineString') | (
                ret_loc['extent'].geom_type == 'Point')

            if not ret_loc.loc[pointLine_idx].empty:
                # Perform meter to decimal conversion if the distance metric is haversine
                if distance_matrix_metric == 'haversine':
                    ret_loc.loc[pointLine_idx,
                                'extent'] = ret_loc.loc[pointLine_idx].apply(
                                    lambda p: p['extent'].buffer(
                                        meters_to_decimal_degrees(
                                            epsilon, p['center'].y)),
                                    axis=1)
                else:
                    ret_loc.loc[pointLine_idx,
                                'extent'] = ret_loc.loc[pointLine_idx].apply(
                                    lambda p: p['extent'].buffer(epsilon),
                                    axis=1)

        ret_loc = ret_loc.set_geometry('center')
        ret_loc = ret_loc[['user_id', 'location_id', 'center', 'extent']]

        # index management
        ret_loc.rename(columns={'location_id': 'id'}, inplace=True)
        ret_loc.set_index('id', inplace=True)

    # stps not linked to a location receive np.nan in 'location_id'
    ret_stps.loc[ret_stps['location_id'] == -1, 'location_id'] = np.nan

    ## dtype consistency
    # locs id (generated by this function) should be int64
    ret_loc.index = ret_loc.index.astype('int64')
    # location_id of spts can only be in Int64 (missing values)
    ret_stps['location_id'] = ret_stps['location_id'].astype('Int64')
    # user_id of ret_loc should be the same as ret_stps
    ret_loc['user_id'] = ret_loc['user_id'].astype(ret_stps['user_id'].dtype)

    return ret_stps, ret_loc
Esempio n. 4
0
def generate_locations(
    staypoints,
    method="dbscan",
    epsilon=100,
    num_samples=1,
    distance_metric="haversine",
    agg_level="user",
    print_progress=False,
    n_jobs=1,
):
    """
    Generate locations from the staypoints.

    Parameters
    ----------
    staypoints : GeoDataFrame (as trackintel staypoints)
        The staypoints have to follow the standard definition for staypoints DataFrames.

    method : {'dbscan'}
        Method to create locations.

        - 'dbscan' : Uses the DBSCAN algorithm to cluster staypoints.

    epsilon : float, default 100
        The epsilon for the 'dbscan' method. if 'distance_metric' is 'haversine'
        or 'euclidean', the unit is in meters.

    num_samples : int, default 1
        The minimal number of samples in a cluster.

    distance_metric: {'haversine', 'euclidean'}
        The distance metric used by the applied method. Any mentioned below are possible:
        https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise_distances.html

    agg_level: {'user','dataset'}
        The level of aggregation when generating locations:
        - 'user'      : locations are generated independently per-user.
        - 'dataset'   : shared locations are generated for all users.

    print_progress : bool, default False
        If print_progress is True, the progress bar is displayed

    n_jobs: int, default 1
        The maximum number of concurrently running jobs. If -1 all CPUs are used. If 1 is given, no parallel
        computing code is used at all, which is useful for debugging. See
        https://joblib.readthedocs.io/en/latest/parallel.html#parallel-reference-documentation
        for a detailed description

    Returns
    -------
    sp: GeoDataFrame (as trackintel staypoints)
        The original staypoints with a new column ``[`location_id`]``.

    locs: GeoDataFrame (as trackintel locations)
        The generated locations.

    Examples
    --------
    >>> sp.as_staypoints.generate_locations(method='dbscan', epsilon=100, num_samples=1)
    """
    if agg_level not in ["user", "dataset"]:
        raise AttributeError(
            "The parameter agg_level must be one of ['user', 'dataset'].")
    if method not in ["dbscan"]:
        raise AttributeError("The parameter method must be one of ['dbscan'].")

    # initialize the return GeoDataFrames
    sp = staypoints.copy()
    sp = sp.sort_values(["user_id", "started_at"])
    geo_col = sp.geometry.name

    if method == "dbscan":

        if distance_metric == "haversine":
            # The input and output of sklearn's harvarsine metrix are both in radians,
            # see https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise.haversine_distances.html
            # here the 'epsilon' is directly applied to the metric's output.
            # convert to radius
            db = DBSCAN(eps=epsilon / 6371000,
                        min_samples=num_samples,
                        algorithm="ball_tree",
                        metric=distance_metric)
        else:
            db = DBSCAN(eps=epsilon,
                        min_samples=num_samples,
                        algorithm="ball_tree",
                        metric=distance_metric)

        if agg_level == "user":
            sp = applyParallel(
                sp.groupby("user_id", as_index=False),
                _generate_locations_per_user,
                n_jobs=n_jobs,
                print_progress=print_progress,
                geo_col=geo_col,
                distance_metric=distance_metric,
                db=db,
            )

            # keeping track of noise labels
            sp_non_noise_labels = sp[sp["location_id"] != -1]
            sp_noise_labels = sp[sp["location_id"] == -1]

            # sort so that the last location id of a user = max(location id)
            sp_non_noise_labels = sp_non_noise_labels.sort_values(
                ["user_id", "location_id"])

            # identify start positions of new user_ids
            start_of_user_id = sp_non_noise_labels[
                "user_id"] != sp_non_noise_labels["user_id"].shift(1)

            # calculate the offset (= last location id of the previous user)
            # multiplication is to mask all positions where no new user starts and addition is to have a +1 when a
            # new user starts
            loc_id_offset = sp_non_noise_labels["location_id"].shift(
                1) * start_of_user_id + start_of_user_id

            # fill first nan with 0 and create the cumulative sum
            loc_id_offset = loc_id_offset.fillna(0).cumsum()

            sp_non_noise_labels["location_id"] = sp_non_noise_labels[
                "location_id"] + loc_id_offset
            sp = gpd.GeoDataFrame(pd.concat(
                [sp_non_noise_labels, sp_noise_labels]),
                                  geometry=geo_col)
            sp.sort_values(["user_id", "started_at"], inplace=True)

        else:
            if distance_metric == "haversine":
                # the input is converted to list of (lat, lon) tuples in radians unit
                p = np.array([[radians(g.y), radians(g.x)]
                              for g in sp.geometry])
            else:
                p = np.array([[g.x, g.y] for g in sp.geometry])
            labels = db.fit_predict(p)

            sp["location_id"] = labels

        ### create locations as grouped staypoints
        temp_sp = sp[["user_id", "location_id", sp.geometry.name]]
        if agg_level == "user":
            # directly dissolve by 'user_id' and 'location_id'
            locs = temp_sp.dissolve(by=["user_id", "location_id"],
                                    as_index=False)
        else:
            ## generate user-location pairs with same geometries across users
            # get user-location pairs
            locs = temp_sp.dissolve(
                by=["user_id", "location_id"],
                as_index=False).drop(columns={temp_sp.geometry.name})
            # get location geometries
            geom_df = temp_sp.dissolve(
                by=["location_id"], as_index=False).drop(columns={"user_id"})
            # merge pairs with location geometries
            locs = locs.merge(geom_df, on="location_id", how="left")

        # filter staypoints not belonging to locations
        locs = locs.loc[locs["location_id"] != -1]

        locs["center"] = None  # initialize
        # locations with only one staypoints is of type "Point"
        point_idx = locs.geom_type == "Point"
        if not locs.loc[point_idx].empty:
            locs.loc[point_idx, "center"] = locs.loc[point_idx,
                                                     locs.geometry.name]
        # locations with multiple staypoints is of type "MultiPoint"
        if not locs.loc[~point_idx].empty:
            locs.loc[~point_idx, "center"] = locs.loc[
                ~point_idx, locs.geometry.name].apply(lambda p: Point(
                    np.array(p)[:, 0].mean(),
                    np.array(p)[:, 1].mean()))

        # extent is the convex hull of the geometry
        locs["extent"] = None  # initialize
        if not locs.empty:
            locs["extent"] = locs[locs.geometry.name].apply(
                lambda p: p.convex_hull)
            # convex_hull of one point would be a Point and two points a Linestring,
            # we change them into Polygon by creating a buffer of epsilon around them.
            pointLine_idx = (locs["extent"].geom_type == "LineString") | (
                locs["extent"].geom_type == "Point")

            if not locs.loc[pointLine_idx].empty:
                # Perform meter to decimal conversion if the distance metric is haversine
                if distance_metric == "haversine":
                    locs.loc[pointLine_idx,
                             "extent"] = locs.loc[pointLine_idx].apply(
                                 lambda p: p["extent"].buffer(
                                     meters_to_decimal_degrees(
                                         epsilon, p["center"].y)),
                                 axis=1)
                else:
                    locs.loc[pointLine_idx,
                             "extent"] = locs.loc[pointLine_idx].apply(
                                 lambda p: p["extent"].buffer(epsilon), axis=1)

        locs = locs.set_geometry("center")
        locs = locs[["user_id", "location_id", "center", "extent"]]

        # index management
        locs.rename(columns={"location_id": "id"}, inplace=True)
        locs.set_index("id", inplace=True)

    # staypoints not linked to a location receive np.nan in 'location_id'
    sp.loc[sp["location_id"] == -1, "location_id"] = np.nan

    if len(locs) > 0:
        locs.as_locations
    else:
        warnings.warn("No locations can be generated, returning empty locs.")

    ## dtype consistency
    # locs id (generated by this function) should be int64
    locs.index = locs.index.astype("int64")
    # location_id of staypoints can only be in Int64 (missing values)
    sp["location_id"] = sp["location_id"].astype("Int64")
    # user_id of locs should be the same as sp
    locs["user_id"] = locs["user_id"].astype(sp["user_id"].dtype)

    return sp, locs
Esempio n. 5
0
                    level=logging.INFO,
                    filemode='w')

# GPSies trajectory.
pfs = ti.read_positionfixes_csv('examples/data/geolife_trajectory.csv',
                                sep=';')
pfs.as_positionfixes.plot(
    out_filename='examples/out/gpsies_trajectory_positionfixes.png',
    plot_osm=True)

spts = pfs.as_positionfixes.extract_staypoints(method='sliding',
                                               dist_threshold=100,
                                               time_threshold=5 * 60)
spts.as_staypoints.plot(
    out_filename='examples/out/gpsies_trajectory_staypoints.png',
    radius=meters_to_decimal_degrees(100, 47.5),
    positionfixes=pfs,
    plot_osm=True)

plcs = spts.as_staypoints.extract_locations(method='dbscan',
                                            epsilon=meters_to_decimal_degrees(
                                                120, 47.5),
                                            num_samples=3)
plcs.as_locations.plot(
    out_filename='examples/out/gpsies_trajectory_locations.png',
    radius=meters_to_decimal_degrees(120, 47.5),
    positionfixes=pfs,
    staypoints=spts,
    staypoints_radius=meters_to_decimal_degrees(100, 47.5),
    plot_osm=True)
Esempio n. 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()
Esempio n. 7
0
def cluster_staypoints(staypoints,
                       method='dbscan',
                       epsilon=100,
                       num_samples=1,
                       distance_matrix_metric='euclidean',
                       agg_level='user'):
    """Clusters staypoints to get locations.

    Parameters
    ----------
    staypoints : GeoDataFrame
        The staypoints have to follow the standard definition for staypoints DataFrames.

    method : str, {'dbscan'}, default 'dbscan'
        The following methods are available to cluster staypoints into locations:
        'dbscan' : Uses the DBSCAN algorithm to cluster staypoints.

    epsilon : float, default 100
        The epsilon for the 'dbscan' method.

    num_samples : int, default 1
        The minimal number of samples in a cluster. 

    distance_matrix_metric: str, default 'euclidean'
        The distance matrix used by the applied method. Possible metrics
        are: {'haversine', 'euclidean'} or any mentioned in: 
        https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise_distances.html
        
    agg_level: str, {'user' or 'dataset'}, default 'user'
        The level of aggregation when generating locations:
        'user'      : locations are generated independently per-user.
        'dataset'   : shared locations are generated for all users.
    
    Returns
    -------
    GeoDataFrame
        A new GeoDataFrame containing locations that a person visited multiple times.
        
    Examples
    --------
    >>> spts.as_staypoints.cluster_staypoints(method='dbscan', epsilon=100, num_samples=1)
    """

    if agg_level not in ['user', 'dataset']:
        raise AttributeError(
            "The parameter agg_level must be one of ['user', 'dataset'].")

    ret_sp = staypoints.copy()
    if method == 'dbscan':

        if distance_matrix_metric == 'haversine':
            # The input and output of sklearn's harvarsine metrix are both in radians,
            # see https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise.haversine_distances.html
            # here the 'epsilon' is directly applied to the metric's output.
            epsilon = epsilon / 6371000  # convert to radius
        db = DBSCAN(eps=epsilon,
                    min_samples=num_samples,
                    algorithm='ball_tree',
                    metric=distance_matrix_metric)

        if agg_level == 'user':
            location_id_counter = 0
            for user_id_this in ret_sp["user_id"].unique():
                # Slice staypoints array by user. This is not a copy!
                user_staypoints = ret_sp[ret_sp["user_id"] == user_id_this]

                if distance_matrix_metric == 'haversine':
                    # the input is converted to list of (lat, lon) tuples in radians unit
                    p = np.array([[radians(g.y), radians(g.x)]
                                  for g in user_staypoints.geometry])
                else:
                    p = np.array([[g.x, g.y]
                                  for g in user_staypoints.geometry])
                labels = db.fit_predict(p)

                # enforce unique lables across all users without changing noise labels
                max_label = np.max(labels)
                labels[labels !=
                       -1] = labels[labels != -1] + location_id_counter + 1
                if max_label > -1:
                    location_id_counter = location_id_counter + max_label + 1

                # add staypoint - location matching to original staypoints
                ret_sp.loc[user_staypoints.index, 'location_id'] = labels
        else:
            if distance_matrix_metric == 'haversine':
                # the input is converted to list of (lat, lon) tuples in radians unit
                p = np.array([[radians(g.y), radians(g.x)]
                              for g in ret_sp.geometry])
            else:
                p = np.array([[g.x, g.y] for g in ret_sp.geometry])
            labels = db.fit_predict(p)

            # add 1 to match the 'user' level result
            ret_sp['location_id'] = labels + 1

        # create locations as grouped staypoints
        temp_sp = ret_sp[['user_id', 'location_id', ret_sp.geometry.name]]
        ret_loc = temp_sp.dissolve(by=['user_id', 'location_id'],
                                   as_index=False)
        # filter outlier
        ret_loc = ret_loc.loc[ret_loc['location_id'] != -1]

        # locations with only one staypoints is of type "Point"
        point_idx = ret_loc.geom_type == 'Point'
        ret_loc['center'] = 0  # initialize
        ret_loc.loc[point_idx, 'center'] = ret_loc.loc[point_idx, 'geom']
        # locations with multiple staypoints is of type "MultiPoint"
        ret_loc.loc[~point_idx,
                    'center'] = ret_loc.loc[~point_idx,
                                            'geom'].apply(lambda p: Point(
                                                np.array(p)[:, 0].mean(),
                                                np.array(p)[:, 1].mean()))

        # extent is the convex hull of the geometry
        ret_loc['extent'] = ret_loc['geom'].apply(lambda p: p.convex_hull)
        # convex_hull of one point would be a Point and two points a Linestring,
        # we change them into Polygon by creating a buffer of epsilon around them.
        pointLine_idx = (ret_loc['extent'].geom_type == 'LineString') | (
            ret_loc['extent'].geom_type == 'Point')

        # Perform meter to decimal conversion if the distance metric is haversine
        if distance_matrix_metric == 'haversine':
            ret_loc.loc[
                pointLine_idx, 'extent'] = ret_loc.loc[pointLine_idx].apply(
                    lambda p: p['extent'].buffer(
                        meters_to_decimal_degrees(epsilon, p['center'].y)),
                    axis=1)
        else:
            ret_loc.loc[pointLine_idx,
                        'extent'] = ret_loc.loc[pointLine_idx].apply(
                            lambda p: p['extent'].buffer(epsilon), axis=1)

        ret_loc = ret_loc.set_geometry('center')
        ret_loc = ret_loc[['user_id', 'location_id', 'center', 'extent']]
        ret_loc['location_id'] = ret_loc['location_id'].astype('int')

    return ret_sp, ret_loc
Esempio n. 8
0
def plot_center_of_locations(
    locations,
    out_filename=None,
    radius=None,
    positionfixes=None,
    staypoints=None,
    staypoints_radius=None,
    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, optional
        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, optional
        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
    --------
    >>> df.as_locations.plot('output.png', radius=100, positionfixes=pdf,
    >>>                      staypoints=spf, staypoints_radius=80, plot_osm=True)
    """
    if axis is None:
        _, ax = regular_figure()
    else:
        ax = axis
    locations = transform_gdf_to_wgs84(locations)

    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)

    if radius is None:
        radius = 125
    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)
    if out_filename is not None:
        save_fig(out_filename, formats=["png"])
    else:
        plt.show()