Skip to main content
deleted 2 characters in body
Source Link
Shawn
  • 1.9k
  • 12
  • 23
import pandas as pd
import geopandas as gpd
import shapely

from sklearn.cluster import AgglomerativeClustering

def cluster_shapes_by_distance(geodf, distance, check_crs=False):
    """
    Make groups for all shapes within a defined distance. For a shape to be 
    excluded from a group, it must be greater than the defined distance
    from *all* shapes in the group.
    Distances are calculated using shape centroids.

    Parameters
    ----------
    geodf : data.frame
        A geopandas data.frame of polygons. Should be a projected CRS where the
        unit is in meters. 
    distance : float
        Maximum distance between elements. In meters.
    check_crs : bool
        Confirm that the CRS of the geopandas dataframe is projected. This 
        function should not be run with lat/lon coordinates. 

    Returns
    -------
    np.array
        Array of numeric labels assigned to each row in geodf.

    """
    if check_crs:
        assert geodf.crs.is_projected, 'geodf should be a projected crs with meters as the unit'
        
    centers = [p.centroid for p in geodf.geometry]
    centers_xy = [[c.x, c.y] for c in centers]
    
    cluster = AgglomerativeClustering(n_clusters=None, 
                                      linkage='single',
                                      affinity='euclidean',
                                      distance_threshold=distance)
    cluster.fit(centers_xy)
    
    return cluster.labels_

#---------------------------

geoms =  ['POINT (-525919.001 7186220.048)', 'POINT (-525950.054 7186212.882)',
          'POINT (-526009.173 7186211.688)', 'POINT (-526009.173 7186211.688)',
          'POINT (-526009.173 7186211.688)']
geoms = [shapely.wkt.loads(s) for s in geoms]

df = gpd.GeoDataFrame(
    {'captured_at': [1632410217000, 1632410219000, 1632410221000, 1632410223000, 1632410225000]},
    geometry = gpd.GeoSeries(geoms),
    )

df['group'] = cluster_shapes_by_distance(df, distance=20) # 125km20m

# Within each group keep the most recent.
df = df.groupby('group').apply(lambda x: x.iloc[x.captured_at.argmax()])

import pandas as pd
import geopandas as gpd
import shapely

from sklearn.cluster import AgglomerativeClustering

def cluster_shapes_by_distance(geodf, distance, check_crs=False):
    """
    Make groups for all shapes within a defined distance. For a shape to be 
    excluded from a group, it must be greater than the defined distance
    from *all* shapes in the group.
    Distances are calculated using shape centroids.

    Parameters
    ----------
    geodf : data.frame
        A geopandas data.frame of polygons. Should be a projected CRS where the
        unit is in meters. 
    distance : float
        Maximum distance between elements. In meters.
    check_crs : bool
        Confirm that the CRS of the geopandas dataframe is projected. This 
        function should not be run with lat/lon coordinates. 

    Returns
    -------
    np.array
        Array of numeric labels assigned to each row in geodf.

    """
    if check_crs:
        assert geodf.crs.is_projected, 'geodf should be a projected crs with meters as the unit'
        
    centers = [p.centroid for p in geodf.geometry]
    centers_xy = [[c.x, c.y] for c in centers]
    
    cluster = AgglomerativeClustering(n_clusters=None, 
                                      linkage='single',
                                      affinity='euclidean',
                                      distance_threshold=distance)
    cluster.fit(centers_xy)
    
    return cluster.labels_

#---------------------------

geoms =  ['POINT (-525919.001 7186220.048)', 'POINT (-525950.054 7186212.882)',
          'POINT (-526009.173 7186211.688)', 'POINT (-526009.173 7186211.688)',
          'POINT (-526009.173 7186211.688)']
geoms = [shapely.wkt.loads(s) for s in geoms]

df = gpd.GeoDataFrame(
    {'captured_at': [1632410217000, 1632410219000, 1632410221000, 1632410223000, 1632410225000]},
    geometry = gpd.GeoSeries(geoms),
    )

df['group'] = cluster_shapes_by_distance(df, distance=20) # 125km

# Within each group keep the most recent.
df = df.groupby('group').apply(lambda x: x.iloc[x.captured_at.argmax()])

import pandas as pd
import geopandas as gpd
import shapely

from sklearn.cluster import AgglomerativeClustering

def cluster_shapes_by_distance(geodf, distance, check_crs=False):
    """
    Make groups for all shapes within a defined distance. For a shape to be 
    excluded from a group, it must be greater than the defined distance
    from *all* shapes in the group.
    Distances are calculated using shape centroids.

    Parameters
    ----------
    geodf : data.frame
        A geopandas data.frame of polygons. Should be a projected CRS where the
        unit is in meters. 
    distance : float
        Maximum distance between elements. In meters.
    check_crs : bool
        Confirm that the CRS of the geopandas dataframe is projected. This 
        function should not be run with lat/lon coordinates. 

    Returns
    -------
    np.array
        Array of numeric labels assigned to each row in geodf.

    """
    if check_crs:
        assert geodf.crs.is_projected, 'geodf should be a projected crs with meters as the unit'
        
    centers = [p.centroid for p in geodf.geometry]
    centers_xy = [[c.x, c.y] for c in centers]
    
    cluster = AgglomerativeClustering(n_clusters=None, 
                                      linkage='single',
                                      affinity='euclidean',
                                      distance_threshold=distance)
    cluster.fit(centers_xy)
    
    return cluster.labels_

#---------------------------

geoms =  ['POINT (-525919.001 7186220.048)', 'POINT (-525950.054 7186212.882)',
          'POINT (-526009.173 7186211.688)', 'POINT (-526009.173 7186211.688)',
          'POINT (-526009.173 7186211.688)']
geoms = [shapely.wkt.loads(s) for s in geoms]

df = gpd.GeoDataFrame(
    {'captured_at': [1632410217000, 1632410219000, 1632410221000, 1632410223000, 1632410225000]},
    geometry = gpd.GeoSeries(geoms),
    )

df['group'] = cluster_shapes_by_distance(df, distance=20) # 20m

# Within each group keep the most recent.
df = df.groupby('group').apply(lambda x: x.iloc[x.captured_at.argmax()])

Source Link
Shawn
  • 1.9k
  • 12
  • 23

Pretty sure this is what you're looking for. I used clustering from the sklearn package since this is essentially the same problem as this question: Split polygons in a shapefile based on distance

import pandas as pd
import geopandas as gpd
import shapely

from sklearn.cluster import AgglomerativeClustering

def cluster_shapes_by_distance(geodf, distance, check_crs=False):
    """
    Make groups for all shapes within a defined distance. For a shape to be 
    excluded from a group, it must be greater than the defined distance
    from *all* shapes in the group.
    Distances are calculated using shape centroids.

    Parameters
    ----------
    geodf : data.frame
        A geopandas data.frame of polygons. Should be a projected CRS where the
        unit is in meters. 
    distance : float
        Maximum distance between elements. In meters.
    check_crs : bool
        Confirm that the CRS of the geopandas dataframe is projected. This 
        function should not be run with lat/lon coordinates. 

    Returns
    -------
    np.array
        Array of numeric labels assigned to each row in geodf.

    """
    if check_crs:
        assert geodf.crs.is_projected, 'geodf should be a projected crs with meters as the unit'
        
    centers = [p.centroid for p in geodf.geometry]
    centers_xy = [[c.x, c.y] for c in centers]
    
    cluster = AgglomerativeClustering(n_clusters=None, 
                                      linkage='single',
                                      affinity='euclidean',
                                      distance_threshold=distance)
    cluster.fit(centers_xy)
    
    return cluster.labels_

#---------------------------

geoms =  ['POINT (-525919.001 7186220.048)', 'POINT (-525950.054 7186212.882)',
          'POINT (-526009.173 7186211.688)', 'POINT (-526009.173 7186211.688)',
          'POINT (-526009.173 7186211.688)']
geoms = [shapely.wkt.loads(s) for s in geoms]

df = gpd.GeoDataFrame(
    {'captured_at': [1632410217000, 1632410219000, 1632410221000, 1632410223000, 1632410225000]},
    geometry = gpd.GeoSeries(geoms),
    )

df['group'] = cluster_shapes_by_distance(df, distance=20) # 125km

# Within each group keep the most recent.
df = df.groupby('group').apply(lambda x: x.iloc[x.captured_at.argmax()])