3

I am trying to find the distance between every consecutive points in a GeoPandas dataframe. Let's say we have the following data frame:

import pandas as pd
import geopandas

data=pd.DataFrame()
data['lat']=[42,42.1,43,43.2,43.9]
data['lon']=[-120,-119,-118.2,-118,-117.2]
data_gpd=geopandas.GeoDataFrame(data,geometry=geopandas.points_from_xy(data.lon,data.lat),crs={'init' :'epsg:4326'})
data_gpd=data_gpd.to_crs({'init': 'epsg:6340'})  #converted to a meter based CRS

Now I want to find the distance between consecutive points and add it to the data to have something like this (the values are not actual and are only examples):

data['distance_from_previous']=['nan',12000,13000,25000,11000]
  • 1. What you want to do is iterate over the rows of the DF and get two points, 2. Calculate the distance between 1,2 3. Add that number to a collection, 4. Replace point 1 with 2, repeat. – Dheeraj Chand Jan 17 '20 at 2:36
3

There are many solutions to iterate over (Geo)DataFrame row pairs (see Pandas iterate over DataFrame row pairs for example)

1) by iteration

for (indx1,row1),(indx2,row2) in zip(data_gpd[:-1].iterrows(),data_gpd[1:].iterrows()):
   print(indx1, indx2, row1['geometry'].distance(row2['geometry']))
 0 1 83538.37731261192
 1 2 119611.60785001986
 2 3 27537.56741184871
 3 4 101094.9180373711

or

data_gpd['dist_prev'] = 0
for i in data_gpd.index[:-1]:
      data_gpd.loc[i+1, 'dist_prev'] = data_gpd.loc[i, 'geometry'].distance(data_gpd.loc[i+1, 'geometry'])
print(data_gpd)

    lat    lon                    geometry                    dist_prev
0  42.0 -120.0   POINT (251535.0792857883 4654130.891206848)      0
1  42.1 -119.0   POINT (334620.8849107226 4662814.749086842)  83538.377313
2  43.0 -118.2   POINT (402189.4662092684 4761513.399299338)  119611.607850
3  43.2 -118.0   POINT (418756.2450509258 4783510.204925923)  27537.567412
4  43.9 -117.2   POINT (483938.2692632438 4860785.596691785)  101094.918037

2) with pandas.concat

import pandas as pd
from shapely.geometry import shape
df_merged = pd.concat([data_gpd, data_gpd.shift(1).add_prefix('pre_')], axis=1)
df_merged["distance"] = df_merged[:-1].apply (lambda row: row.geometry.distance(shape(row.pre_geometry)), axis=1)
print(df_merged)
   lat    lon           geometry                           pre_lat pre_lon           pre_geometry                      distance
0  42    -120    POINT (251535.0792857883 4654130.891206848)  nan    nan               nan                                nan
1  42.1  -119    POINT (334620.8849107226 4662814.749086842)  42    -120    POINT (251535.0792857883 4654130.891206848)  83538.4
2  43    -118.2  POINT (402189.4662092684 4761513.399299338)  42.1  -119    POINT (334620.8849107226 4662814.749086842)  119612
3  43.2  -118    POINT (418756.2450509258 4783510.204925923)  43    -118.2  POINT (402189.4662092684 4761513.399299338)  27537.6
4  43.9  -117.2  POINT (483938.2692632438 4860785.596691785)  43.2  -118    POINT (418756.2450509258 4783510.204925923)  101095

print(df_merged["distance"]) 
0              NaN
1     83538.377313
2    119611.607850
3     27537.567412
4    101094.918037
Name: distance, dtype: float64

3) groupby by "adjacency" (Perform function on pairs of rows in Pandas dataframe)

g = data_gpd.groupby(data_gpd.index // 2)
# first group
print(g.geometry.get_group(0))
0    POINT (251535.0792857883 4654130.891206848)
1    POINT (334620.8849107226 4662814.749086842)
Name: geometry, dtype: object
# therefore
print(g.geometry.get_group(0)[0].distance(g.geometry.get_group(0)[1]))
83538.37731261192

The problem of this approach is that not all pairs of points are recognized

print(g.groups)
{0: Int64Index([0, 1], dtype='int64'), 1: Int64Index([2, 3], dtype='int64'), 2: Int64Index([4], dtype='int64')}

The distances between points 1 and 2 and 3 and 4 are not calculated (groups different)

    -
4

This is how I would do it

import pandas as pd
import geopandas as gpd

df = pd.DataFrame()
df['lat'] = [42, 42.1, 43, 43.2, 43.9]
df['lon'] = [-120, -119, -118.2, -118, -117.2]

gdf = gpd.GeoDataFrame(geometry=gpd.points_from_xy(df.lon, df.lat),
                       crs="EPSG:4326").to_crs("EPSG:6340")
df['distance_from_previous'] = gdf.distance(gdf.shift(1))

With version 0.8.1 of geopandas, you will get the following warning, which you can ignore as it is a bug (https://github.com/geopandas/geopandas/issues/1648)

UserWarning: CRS mismatch between the CRS of left geometries and the CRS of right geometries. Use to_crs() to reproject one of the input geometries to match the CRS of the other.

The problem seems to come from the fact that the shift operation creates a gdf with the same crs as the original but the geometry of the created gdf is not set properly which triggers the warning. If you want to get rid of the warning, do:

gdf_prev = gdf.shift(1)
gdf_prev.geometry.crs = gdf.crs
df['distance_from_previous'] = gdf.distance(gdf_prev)
New contributor
guibar is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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