2

This code below was modified from the one I found here which splits a shapely Linestring into two segments at a point defined along the line. I have also checked other questions but they don't address my query directly. However I will like to extend it to split the line into multiple segments (at multiple points), all my attempts to do that so far has failed. How can it be modified to split the string into any given number of segments or at multiple points say ((4,5),(9,18) and (6,5)).

input: 

line = LineString([(1,2),(8,7),(4,5),(2,4),(4,7),(8,5),(9,18),(1,2),(12,7),(4,5),(6,5),(4,9)])
       breakPoint = Point(2,4)

from shapely.geometry import Point,LineString

def make_line_segment(line_string, breakPoint):
    geoLoc = line_string.coords
    j = None
    for i in range(len(geoLoc) - 1):
        if LineString(geoLoc[i:i + 2]).intersects(breakPoint):
           j = i
           break
    assert j is not None
    # Make sure to always include the point in the first group
    if Point(geoLoc[j + 1:j + 2]).equals(breakPoint):
        return geoLoc[:j + 2], geoLoc[j + 1:]
    else:
        return geoLoc[:j + 1], geoLoc[j:]

line1,line2 = make_line_segment(line,breakPoint)
line1 = LineString(line1)
line2 = LineString(line2)
print line1, line2

output: `LINESTRING (1 2, 8 7, 4 5, 2 4) LINESTRING (2 4, 4 7, 8 5, 9 18, 1 2, 12 7, 4 5, 6 5, 4 9)`
Community
  • 1
  • 1
Nobi
  • 1,113
  • 4
  • 23
  • 41
  • 1
    this might be of interest: http://stackoverflow.com/questions/34754777/shapely-split-linestrings-at-intersections-with-other-linestrings – ewcz Sep 10 '16 at 11:04
  • You haven't provided us with much to be going on with. For example, is the break point always on the line? Also it's not clear what you are having trouble with. Why not just split `line2` at the second point and so on? Provide some more detail on why this is difficult. – strubbly Sep 10 '16 at 11:12

2 Answers2

7

The projection and interpolate lineString methods are usually handy for this kind of operations.

from shapely.geometry import Point, LineString

def cut(line, distance):
    # Cuts a line in two at a distance from its starting point
    # This is taken from shapely manual
    if distance <= 0.0 or distance >= line.length:
        return [LineString(line)]
    coords = list(line.coords)
    for i, p in enumerate(coords):
        pd = line.project(Point(p))
        if pd == distance:
            return [
                LineString(coords[:i+1]),
                LineString(coords[i:])]
        if pd > distance:
            cp = line.interpolate(distance)
            return [
                LineString(coords[:i] + [(cp.x, cp.y)]),
                LineString([(cp.x, cp.y)] + coords[i:])]

def split_line_with_points(line, points):
    """Splits a line string in several segments considering a list of points.

    The points used to cut the line are assumed to be in the line string 
    and given in the order of appearance they have in the line string.

    >>> line = LineString( [(1,2), (8,7), (4,5), (2,4), (4,7), (8,5), (9,18), 
    ...        (1,2),(12,7),(4,5),(6,5),(4,9)] )
    >>> points = [Point(2,4), Point(9,18), Point(6,5)]
    >>> [str(s) for s in split_line_with_points(line, points)]
    ['LINESTRING (1 2, 8 7, 4 5, 2 4)', 'LINESTRING (2 4, 4 7, 8 5, 9 18)', 'LINESTRING (9 18, 1 2, 12 7, 4 5, 6 5)', 'LINESTRING (6 5, 4 9)']

    """
    segments = []
    current_line = line
    for p in points:
        d = current_line.project(p)
        seg, current_line = cut(current_line, d)
        segments.append(seg)
    segments.append(current_line)
    return segments

if __name__ == "__main__":
    import doctest
    doctest.testmod()
eguaio
  • 3,754
  • 1
  • 24
  • 38
  • `Cut` returns only one element (a list), but the call of it in `split_line_with_points` expects two, so it always will fail. – ImanolUr Apr 15 '20 at 09:42
  • If a list `l` contains *exactly* two elements, the following won't fail: `a, b = l`. In fact, it is an implicit way of raising an exception if the list has not two elements. – eguaio Apr 17 '20 at 22:22
  • Oh, i had never seen it. Somehow it failed for me when i tried to run it. But anyway, thanks for the tip. – ImanolUr Apr 24 '20 at 09:44
-1

Here in this approach, first I split the list of points in the beginning and form the list of lines as below,

# List of coordinates
coords = [(1,2),(8,7),(4,5),(2,4),(4,7),(8,5),(9,18),(1,2),(12,7),(4,5),(6,5),(4,9)]



no_seg = 3 #Number of segments, you need from 'coords'
coords_sub = np.array_split(coords, no_seg)

list_lines = [] # empty list

for i in range(len(X_sub)):
    list_lines.append(LineString(X_sub[i]))

list_lines[1] # Displays the second line of the list