6

I'm looking for an equivalent of densify tool in ArcGIS, but with GeoPandas or Shapely. I have a line that would like to be densified with a specific distance; how to add vertices of a line using Python?


Suppose I have a line that has 2 vertices (WKT) LINESTRING (0 0, 9 0). how do I divide the line into an equal distance of 1 such that it returns the same line, but with instead of 2 vertices, it has 10 vertices? the result I expect:
LINESTRING (0 0, 1 0, 2 0, 3 0, 4 0, 5 0, 6 0, 7 0, 8 0, 9 0). I find this post which is similar to what I want, but it uses ArcGIS instead of GeoPandas and Shapely.

5
  • 1
    I am not sure why people voted to close this. Could you clarify how you want to decide at which steps the vertices should be added? Is it a specific distance? Or a percentage/fraction of the length? Or a certain number of vertices? Commented Sep 1, 2020 at 19:01
  • I'm looking for a specific distance. Just like ArcGIS densify tool, although it provides more options than a specific distance.
    – sutan
    Commented Sep 2, 2020 at 2:02
  • 1
    Because there is no code attempt. Try interpolate
    – Bera
    Commented Sep 2, 2020 at 5:57
  • 1
    If you want the points to be equidistant you might try this answer. It uses interpolate like BERA suggested. If you want better time complexity, you might try splitting large linestrings several times beforehand. At one point I had written my own interpolate function in python. But the linestrings needed to be very large and the interpolate distance pretty small in order for it to actually be faster than using shapely's interpolate over and over again. And now I realize it would have been much faster just to split a couple times. Commented Sep 2, 2020 at 12:16
  • 1
    I see thank you so much. The problem is solved, I used loop function on interpolate, then split function and finalized it with linemerge. @JeremiahEngland @BERA
    – sutan
    Commented Sep 3, 2020 at 3:57

2 Answers 2

4

I crafted a function for you. Here is your line, which I call old_line:

old_line=LineString([(0,0), (9,0)]) # your line with 2 vertices

Here is the function that takes a line geometry as main input, you assign a step (i.e the spacing along the old line) and and additional Coordinate Reference System (crs) to georeference the new densified line.

   def densify_geometry (line_geometry, step, crs=None):
        
        # crs: epsg code of a coordinate reference system you want your line to be georeferenced with
        # step: add a vertice every step in whatever unit your coordinate reference system use.
    
        length_m=line_geometry.length # get the length
    
        xy=[] # to store new tuples of coordinates
    
        for distance_along_old_line in np.arange(0,int(length_m),step): 
    
            point = line_geometry.interpolate(distance_along_old_line) # interpolate a point every step along the old line
            xp,yp = point.x, point.y # extract the coordinates
    
            xy.append((xp,yp)) # and store them in xy list
    
        new_line=LineString(xy) # Here, we finally create a new line with densified points.
        
        if crs != None:  #  If you want to georeference your new geometry, uses crs to do the job.
            new_line_geo=gpd.geoseries.GeoSeries(new_line,crs=crs) 
            return new_line_geo
    
        else:
            return new_line

Now, running:

densify_geometry(line_geometry=old_line, step= 0.5, crs= 32754)

returns:

LINESTRING (0.00000 0.00000, 0.50000 0.00000, ...
dtype: geometry

If you want to .apply it to a whole GeoDataFrame geometry column:

kwargs_dict={'step':0.5,
             'crs':32754}

your_gdf["new_geometry"]=your_gdf.geometry.apply(densify_geometry, kwargs=**kwargs_dict)

This should do the job.

2
  • wow thanks! it does do the job, but it should be noted that this function deletes the last point. so, the new_line is one vertex shorter. Anyway, this is great, thanks.
    – sutan
    Commented Sep 7, 2020 at 5:05
  • If it is 1 vertex shorter, try to add "1 step" in the np.arange part, like this: np.arange(0, int(length_m)+step, step). I haven't tested it but it could be as easy as that. Commented Feb 28, 2021 at 3:27
2

I'm late to the party, but for future reference, Shapely now has this as a builtin feature called segmentize().

Just pass in a linestring geometry and the maximum segment length that you want to see. Requires GEOS >= 3.10.0

line_geometry = loads(
    "LINESTRING (-122.341611276025 47.6821613269801,-122.341891356399 47.6824837063215,-122.342138308987 47.6827979735732)")

new_line = shapely.segmentize(line_geometry, 0.0001)
# Output:
# LINESTRING (-122.341611276025 47.6821613269801, -122.3416672920998 47.68222580284838, -122.34172330817461 47.68229027871666, -122.3417793242494 47.68235475458494, -122.34183534032421 47.68241923045322, -122.341891356399 47.6824837063215, -122.341953094546 47.68256227313442, -122.342014832693 47.682640839947354, -122.34207657084 47.68271940676028, -122.342138308987 47.6827979735732)

https://shapely.readthedocs.io/en/stable/reference/shapely.segmentize.html

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.