I have a shapely LineString
and have defined a shapely Point
which lies along the LineString
.
How can I find the vertices of the LineString
which lie either side of the point? (split the line in two)
Locate the line segment in the LineString
where the point lies. Then split in two groups the vertices of the LineString
accordingly. To locate the line segment, simply apply a point / line segment intersection test to each segment.
from shapely.geometry import Point,LineString
def split(line_string, point):
coords = line_string.coords
j = None
for i in range(len(coords) - 1):
if LineString(coords[i:i + 2]).intersects(point):
j = i
break
assert j is not None
# Make sure to always include the point in the first group
if Point(coords[j + 1:j + 2]).equals(point):
return coords[:j + 2], coords[j + 1:]
else:
return coords[:j + 1], coords[j:]
Newer versions of Shapely (>=1.6.0 (2017-08-21)) provide split
function that can split a line by a point:
from shapely.geometry import LineString, Point
from shapely.ops import split
line = LineString([(0, 0), (1, 1), (2, 1)])
point = Point(1.5, 1)
print(split(line, point))
# GEOMETRYCOLLECTION (LINESTRING (0 0, 1 1, 1.5 1), LINESTRING (1.5 1, 2 1))
One must take care, though, that due to precision errors this sometimes doesn't work properly:
line = LineString([(0, 0), (3, 2)])
point = Point(1, 2 / 3)
print(split(line, point))
# GEOMETRYCOLLECTION (LINESTRING (0 0, 3 2))
print(point.distance(line))
# 0.0
One way to deal with this is to construct a new LineString
that will contain the splitting point.
new_line = LineString([line.coords[0], point.coords[0], line.coords[1]])
print(split(new_line, point))
# GEOMETRYCOLLECTION (LINESTRING (0 0, 1 0.6666666666666666), LINESTRING (1 0.6666666666666666, 3 2))
or if you don't want to do it manually:
from itertools import chain
all_points_coords = chain(line.coords, point.coords)
all_points = map(Point, all_points_coords)
new_line = LineString(sorted(all_points, key=line.project))
print(split(new_line, point))
# GEOMETRYCOLLECTION (LINESTRING (0 0, 1 0.6666666666666666), LINESTRING (1 0.6666666666666666, 3 2))