I'm trying geopandas to manipulate some points data. My final GeoDataFrame is represented there:
In order to use an other Python module which calculates the shortest road between two points with OSM data, I must sort my points like a tour.
If not, the next Python module which calculates shortest road, but not necessarily between the nearest points. And the main problem is the constraint of a tour.
If my points were only in a line, a basic sorting function on latitudes and longitudes of each point should be enough, like:
df1 = pd.read_csv("file.csv", sep = ",")
df1 = df1.sort_values(['Latitude','Longitude'], ascending = [1,1])
# (I'm starting with pandas df before GeoDataFrame conversion)
If we start from the "upper" point of previous picture following this sorting, the second point of DataFrame will be the nearest of it, etc... Until the fifth point, which is on the right of the picture (so not the nearest anymore)...
So my question is: does someone know how achieve this special kind of sorting, or must I change my index manually?
If I understand your question correctly, you want to rearrange the order of points in a way that they would create the shortest possible path.
I have run into the same problem also. Here is the function that accepts regular dataframe (= with separate fields for each coordinate. I am sure you will be able to modify either function in order to accept geodataframe or dataframe in order to split geometry field into x and y fields.
def autoroute_points_df(points_df, x_col="e",y_col="n"):
'''
Function, that converts a list of random points into ordered points, searching for the shortest possible distance between the points.
Author: Marjan Moderc, 2016
'''
points_list = points_df[[x_col,y_col]].values.tolist()
# arrange points in by ascending Y or X
points_we = sorted(points_list, key=lambda x: x[0])
points_sn = sorted(points_list, key=lambda x: x[1])
# Calculate the general direction of points (North-South or West-East) - In order to decide where to start the path!
westmost_point = points_we[0]
eastmost_point = points_we[-1]
deltay = eastmost_point[1] - westmost_point[1]
deltax = eastmost_point[0] - westmost_point[0]
alfa = math.degrees(math.atan2(deltay, deltax))
azimut = (90 - alfa) % 360
# If main directon is towards east (45°-135°), take westmost point as starting line.
if (azimut > 45 and azimut < 135):
points_list = points_we
elif azimut > 180:
raise Exception("Error while computing the azimuth! It cant be bigger then 180 since first point is west and second is east.")
else:
points_list = points_sn
# Create output (ordered df) and populate it with the first one already.
ordered_points_df = pd.DataFrame(columns=points_df.columns)
ordered_points_df = ordered_points_df.append(points_df.ix[(points_df[x_col]==points_list[0][0]) & (points_df[y_col]==points_list[0][1])])
for iteration in range(0, len(points_list) - 1):
already_ordered = ordered_points_df[[x_col,y_col]].values.tolist()
current_point = already_ordered[-1] # current point
possible_candidates = [i for i in points_list if i not in already_ordered] # list of candidates
distance = 10000000000000000000000
best_candidate = None
for candidate in possible_candidates:
current_distance = Point(current_point).distance(Point(candidate))
if current_distance < distance:
best_candidate = candidate
distance = current_distance
ordered_points_df = ordered_points_df.append(points_df.ix[(points_df[x_col]==best_candidate[0]) & (points_df[y_col]==best_candidate[1])])
return ordered_points_df
Hope it solves your problem!