How to split a LineString to segments
Asked Answered
K

3

6

My data set consists of a LineString and I want to filter out the individual line segments of this LineString. More precisely, every single street segment.

Until now I have extracted the individual points from the data set and saved them in a separate list. Furthermore, I would like to collect these points again and create individual LineStrings from them to store them into a Geodataframe. The data has this form:

LINESTRING (3275.284016199762 340555.8579582386, 3241.504528076811 340504.1348617533, 3245.415803206172 340501.457084205, 3280.414559049542 340552.7138220053, 3285.19053022

My problem with this is that I would have to create and explicitly save a separate LineString for each iteration. Can anyone help me with this? Is there a better method for this?

from shapely.geometry import Point, LineString

#Loop over LineString and gather Points
c=[]

for i in range(0,end):
    c.append(Point(route1.coords[i]))


iterator=len(c)
max=len(c)-1

#Loop to store LineStrings - got stuck here
for i in np.arange(0,iterator):
    if i<max:
        LineString([c[i], c[i+1]]).wkt

    else:
        break;

The output should look like this:

Linestring(Point A, Point B)  
Linestring(Point B, Point C)  
Linestring(Point C, Point D)  
...  
Linestring(Point Y, Point Z)
Kiger answered 27/5, 2020 at 21:33 Comment(0)
C
10

Speaking of Shapely, it doesn't provide a function to split a curve object (LineString or LinearRing) to segments, so you have to write your own. Here is an example of how you could do it using zip to iterate over pairs of coordinates and map them to LineString's:

from shapely.geometry import LineString, LinearRing


def segments(curve):
    return list(map(LineString, zip(curve.coords[:-1], curve.coords[1:])))


line = LineString([(0, 0), (1, 1), (2, 2)])
ring = LinearRing([(0, 0), (1, 0), (1, 1), (0, 1)])

line_segments = segments(line)
for segment in line_segments:
    print(segment)
# LINESTRING (0 0, 1 1)
# LINESTRING (1 1, 2 2)

ring_segments = segments(ring)
for segment in ring_segments:
    print(segment)
# LINESTRING (0 0, 1 0)
# LINESTRING (1 0, 1 1)
# LINESTRING (1 1, 0 1)
# LINESTRING (0 1, 0 0)
Candless answered 28/5, 2020 at 9:32 Comment(0)
L
1

Welcome to SO.

You don't have to write code to split a LineString. Let PostGIS do the job :) The quick&dirty example below uses a CTE to ST_DumpPoints from your LineString, and subsequently creates another line with path,path+1 using the window function lead():

WITH j AS (
  SELECT ST_DumpPoints('LINESTRING (10 10, 12 12, 14 14, 16 16, 18 18)') AS g
)
SELECT ST_AsText(point) FROM (
  SELECT ST_MakeLine((g).geom, lead((g).geom,1) OVER ()) AS point
  FROM j) i
WHERE i.point IS NOT NULL

            st_astext        
-------------------------
 LINESTRING(10 10,12 12)
 LINESTRING(12 12,14 14)
 LINESTRING(14 14,16 16)
 LINESTRING(16 16,18 18)
(4 Zeilen)
Linolinocut answered 28/5, 2020 at 6:40 Comment(0)
P
0

You can use .segmentize to insert vertices, then zip the coordinates into line segments, .explode:

import numpy as np, geopandas as gpd
from shapely.geometry import LineString
n = 10
x1s = np.random.random(n)
x2s = np.random.random(n)
x3s = np.random.random(n)
y1s = np.random.random(n)
y2s = np.random.random(n)
y3s = np.random.random(n)
lines = [LineString(((x1, y1), (x2, y2), (x3, y3))) for x1, y1, x2, y2, x3, y3 in zip(x1s, y1s, x2s, y2s, x3s, y3s)]
df = gpd.GeoDataFrame(geometry=lines)

def splitlines(dataframe, segment_length):
    """Function to return a list of an input line, split at each vertex"""
    line = dataframe.geometry  #Extract the geometry
    segmentized = line.segmentize(segment_length) #Insert vertices every x distance
    line_segments = [LineString([p1,p1]) for p1, p2 in zip(segmentized.coords, segmentized.coords[1:])] #zip the coordinates and create linestrings
    return line_segments #Return the list of line segments

df["line_segments"] = df.apply(lambda x: splitlines(dataframe=x, segment_length=0.1), axis=1)
#print(df.shape)
#(10, 2)

df2 = df.explode(column="line_segments") #Create rows from the list of line segments
#print(df2.shape)
#(112, 2)
Purslane answered 5/8 at 7:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.