I have a streets line dataset and a GPS Ping point dataset which contains speed. I'm trying to join the points to the closest street within 15m to get an average speed along each segment.

More details: The larger intent of the project is to train a model to predict the speed for each segment based on a number of attributes in the streets dataset. From a processing perspective, this is a pretty large dataset with roughly 1 million line segments, and 400,000 GPS Ping points.

A small example area looks like this:

Points and lines

I have added a 15m buffer around each point. I don't want to include the ones further than 15m from a line segment, and if a point is within 15 of multiple segments it is attached to the closest one.

So far my code looks like this:

import geopandas as gpd

gdb = r"C:\aaa\harry\model_training\data.gdb"
rtsFC = "RTS_Pings_cut2"
streetsFC = "streets_cut2"

rtsGDF = gpd.read_file(gdb, driver='FileGDB', layer=rtsFC)
streetsGDF = gpd.read_file(gdb, driver='FileGDB', layer=streetsFC)

Then I am lost.

  • Welcome to GIS SE! We're a little different from other sites. We're a Q&A site, not a discussion forum. For questions that involve code we ask that you show us where you are stuck with your own code by including a code snippet in your question. There is an edit button beneath your question which will enable you to do that and a {} button that enables you to format any highlighted code nicely. Please check out our short tour for more about how the site works. Thanks.
    – PolyGeo
    Commented Jun 9, 2021 at 7:13
  • How many points and lines are there? Can you add some sample data and whatever code attempt you have to your question?
    – Bera
    Commented Jun 9, 2021 at 8:23

2 Answers 2


Assuming your two dataframes look similar to the following:

geom = [LineString(((-97.932083, 29.886283), (-96.922304, 30.886503))), LineString(((-97.520304, 30.290503), (-97.938405, 30.691903)))]
streetsGDF = gpd.GeoDataFrame({}, geometry=geom)

geom = [Point(-97.82208, 29.97628), Point(-97.539304, 30.592503), Point(-97.322304, 30.406503), Point(-97.222304, 30.886503)]
rtsGDF = gpd.GeoDataFrame({'speed': [15.32, 7.88, 10, 18.61]}, geometry=geom)

plot of dataframes

You can apply the closest_line function to each of your GPS measurements, in order to calculate the closest street. Then group by streets and calculate the mean. And lastly merge the mean speed into the streets dataframe.

def closest_line(point):
    buffer = 25000 # buffer in meter, I chose 25000 because the points are far from the lines in my example data
    distances = []
    for street in streetsGDF['geometry']:
        closest_point = street.interpolate(street.project(point))
        geod = Geod(ellps="WGS84") # use GRS80 as ellipse for your crs of epsg:3111
        dist = geod.geometry_length(LineString([point, closest_point])) # returns the geodesic length (meters) of the lineString connecting both points
        if dist > buffer:
            dist = np.nan
    return None if np.isnan(distances).all() else np.nanargmin(distances)

rtsGDF['street_index'] = rtsGDF.apply(lambda x: closest_line(x['geometry']), axis=1)
mean_df = rtsGDF.groupby('street_index', as_index=False).mean()
streetsGDF = streetsGDF.merge(mean_df, how='inner', left_index=True, right_on='street_index').drop('street_index', axis=1)

>>> streetsGDF
                                            geometry      speed
0  LINESTRING (-97.93208 29.88628, -96.92230 30.8...  14.643333
1  LINESTRING (-97.52030 30.29050, -97.93841 30.6...   7.880000

using sindex.nearest seems to be the easiest solution. It returns a 0 based index (order in the df)

def get_segment(x):
    # get 2 closest segments from lion
    ind = lines.sindex.nearest( x.geometry, max_distance=50 )
    return ind[1][0]

points.apply( lambda x: get_segment(x),axis=1 )

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.