# Geopandas: How to associate a Point to a Linestring using the original Linestring order

Using Geopandas, Shapely

Suppose I have a Linestring that represents a (cornered) street:

LineString([(1, 1), (2, 2), (3, 1)]

Note that the order of points in this linestring matters because LineString([(1, 1), (3, 1), (2, 2)] would represent a very different street.

Now, suppose I have list of points that belong to my street:

Point((1.9, 1.9))

Point((1.5, 1.5))

Point((2.5, 1.5))

Point((1.2, 1.2))

I want to create a new Linestring where all the Points are "merged" with the original street coordinates. This "merge" mechanism has to maintain the original street shape as follows:

LineString([(1, 1), (1.2, 1.2), (1.5, 1.5), (1.9, 1.9), (2, 2), (2.5, 1.5). (3, 1)]

Any ideas how to approach this?

``````import geopandas as gpd
from shapely.geometry import Point, LineString
street = gpd.GeoDataFrame({'street': ['st'], 'geometry': LineString([(1, 1), (2, 2), (3, 1)])})
pp = gpd.GeoDataFrame({'geometry': [Point((1.9, 1.9)), Point((1.5, 1.5)), Point((2.5, 1.5)), Point((1.2, 1.2))]})
print(street)
print(pp)
``````
• Take a look at the shapely's `LineString.project()` method. You can use it to sort all your points (external points and street points) in terms of the street's total length. You can also use it in tandem with the `LineString.interpolate()` method to figure out if the point really does belong to the street or not. Commented Oct 12, 2021 at 15:56
• Thanks Felipe. Not quite what i need. As i said, my points BELONG to linestring. So interpolating Point(1.2, 1.2) with the original linestring will return Point(1.2, 1.2). What im looking for is to create a NEW linestring that would contain newly introduced points in such way that the original shape would not change. Commented Oct 12, 2021 at 16:32

Here's a lengthy-ish approach if you already know which points belong to which streets:

``````import pandas as pd
import geopandas as gpd
import shapely

street = shapely.geometry.LineString([(1, 1), (2, 2), (3, 1)])
pp = gpd.GeoDataFrame({'geometry': [shapely.geometry.Point(pt) for pt in [(1.9, 1.9),
(1.5, 1.5),
(2.5, 1.5),
(1.2, 1.2)]],
'origin':'external'},
geometry='geometry')

ps = gpd.GeoDataFrame({'origin':'street',
'geometry':[shapely.geometry.Point(pt) for pt in list(street.coords)]},
geometry='geometry')

p_all = pd.concat([pp, ps], ignore_index=True)

p_all['dist_along_line'] = p_all['geometry'].apply(lambda p: street.project(p))

p_all = p_all.sort_values(by='dist_along_line')

final_geom = shapely.geometry.LineString(p_all['geometry'].values)

print(p_all)

print(final_geom.wkt)
``````

Note how, in my code, the `street` variable isn't a GeoDataFrame but a simple shapely `Linestring` instead.

The `final_geom` variable will contain the sorted points as you wanted, while the `p_all` variable will contain a GeoDataFrame with the sorted list of all the points. I've also added a "origin" column so you can tell which points came from the original `street` geometry and which ones came from the `pp` GeoDataFrame of external points.

# Caveat

Note that this approach will only work with `LineString`s. If some streets are coded as `MultiLineString`s, you'll have to take care of them differently in the `ps = ....` statement.

• Thank you F. Learning.... Commented Oct 12, 2021 at 16:51
• You're very welcome! =) If this answers your question, please consider upvoting the answer and checking the green check-mark below the upvote arrow. Thanks! Commented Oct 12, 2021 at 16:53
• Not enough reputation lol. Did what i could. Commented Oct 12, 2021 at 17:04
• Much appreciated!! =) Also, I've just added a caveat you should pay attention to regarding `LineString`s and `MultiLineString`s. Commented Oct 12, 2021 at 17:07
• Gotcha. Well, the "sort by distance" approach is the idea. The rest is a question of implementation. Good stuff Commented Oct 12, 2021 at 17:28