How to get the endpoint of a LineString in Shapely
Question:
Linestring1 = LINESTRING (51.2176008 4.4177154, 51.21758 4.4178548, **51.2175729 4.4179023**, *51.21745162000732 4.41871738126533*)
Linestring2 = LINESTRING (*51.21745162000732 4.41871738126533*, **51.2174025 4.4190475**, 51.217338 4.4194807, 51.2172511 4.4200562, 51.2172411 4.4201077, 51.2172246 4.4201654, 51.2172067 4.420205, 51.2171806 4.4202355, 51.2171074 4.4202929, 51.2170063 4.4203409, 51.2169564 4.4203641, 51.2168076 4.4204243, 51.2166588 4.4204833, 51.2159018 4.420431, 51.2154117 4.4203843)
Considering these two linestrings were cut from a bigger linestring, how to get the endpoint of a LineString?
– Point(51.21745162000732 4.41871738126533) removed
– The new last element of linestring 1 = “ 51.2175729 4.4179023
– The new first element of linestring 2 = “ 51.2174025 4.4190475
In short, I want to get the new last value of the first part (linestring1) and the new first value of the second part (linestring2), but without the point where I cut them. How can I make this work?
Answers:
Solved with two routines that allows a Linesstring to be split as follows.
-
Function: split_first
returns first point, and LineString of points without the first
-
Function: split_last
return last point, and LineString of points from first not including the last
Code
from shapely.ops import nearest_points
from shapely.geometry import Point
from shapely.geometry import LineString
def split_first(linestring):
" returns first point and linestring without first point "
coords = list(linestring.coords)
p, *x = coords
return Point(p), LineString(x)
def split_last(linestring):
" returns first point and linestring without first point "
*x, p = list(linestring.coords) = list(linestring.coords)
return Point(p), LineString(x)
Test
Data
linestring = LineString([(51.2176008,4.4177154), (51.21758,4.4178548), (51.2175729,4.4179023), (51.21745162000732,4.41871738126533)])
First point, and linestring not including the first
p, l = split_first(linestring)
print(p)
print(l)
Out
POINT (51.2176008 4.4177154)
LINESTRING (51.21758 4.4178548, 51.2175729 4.4179023, 51.21745162000732 4.41871738126533)
First last point, and linestring not including the last
p, l = split_last(linestring)
print(p)
print(l)
Out
POINT (51.21745162000732 4.41871738126533)
LINESTRING (51.2176008 4.4177154, 51.21758 4.4178548, 51.2175729 4.4179023)
To get endpoints of a LineString
, you just need to access its boundary
property:
from shapely.geometry import LineString
line = LineString([(0, 0), (1, 1), (2, 2)])
endpoints = line.boundary
print(endpoints)
# MULTIPOINT (0 0, 2 2)
first, last = line.boundary
print(first, last)
# POINT (0 0) POINT (2 2)
Alternatively, you can get the first and the last points from the coords
cordinate sequence:
from shapely.geometry import Point
first = Point(line.coords[0])
last = Point(line.coords[-1])
print(first, last)
# POINT (0 0) POINT (2 2)
In your specific case, though, as you want to remove the last point of the first line, and the first point of the second line, and only after that get the endpoints, you should construct new LineString
objects first using the same coords
property:
from shapely.wkt import loads
first_line = loads("LINESTRING (51.2176008 4.4177154, 51.21758 4.4178548, 51.2175729 4.4179023, 51.21745162000732 4.41871738126533)")
second_line = loads("LINESTRING (51.21745162000732 4.41871738126533, 51.2174025 4.4190475, 51.217338 4.4194807, 51.2172511 4.4200562, 51.2172411 4.4201077, 51.2172246 4.4201654, 51.2172067 4.420205, 51.2171806 4.4202355, 51.2171074 4.4202929, 51.2170063 4.4203409, 51.2169564 4.4203641, 51.2168076 4.4204243, 51.2166588 4.4204833, 51.2159018 4.420431, 51.2154117 4.4203843)")
first_line = LineString(first_line.coords[:-1])
second_line = LineString(second_line.coords[1:])
print(first_line.boundary[1], second_line.boundary[0])
# POINT (51.2175729 4.4179023) POINT (51.2174025 4.4190475)
Similar to Georgy‘s solution, you can get their coords
by unpacking the one you want and ignoring the rest using *_
.
from shapely.geometry import LineString
linestring1 = LineString([(51.2176008, 4.4177154), (51.21758, 4.4178548),
(51.2175729, 4.4179023),
(51.21745162000732, 4.41871738126533)])
linestring2 = LineString([(51.21745162000732, 4.41871738126533),
(51.2174025, 4.4190475), (51.217338, 4.4194807),
(51.2172511, 4.4200562), (51.2172411, 4.4201077),
(51.2172246, 4.4201654), (51.2172067, 4.420205),
(51.2171806, 4.4202355), (51.2171074, 4.4202929),
(51.2170063, 4.4203409), (51.2169564, 4.4203641),
(51.2168076, 4.4204243), (51.2166588, 4.4204833),
(51.2159018, 4.420431), (51.2154117, 4.4203843)])
*_, last_new_1, last_1 = linestring1.coords
first_2, first_new_2, *_ = linestring2.coords
print(last_new_1)
print(first_new_2)
# (51.2175729, 4.4179023)
# (51.2174025, 4.4190475)
Note: The syntax changes as of shapely 2.0
Per Georgy’s answer, .boundary still works (as of Dec 2022) but is being deprecated and throws a warning.
For multiparts, .geoms is preferred moving forward.
For accessing single points from a coordinate sequence, use numpy.
So, instead of:
first = Point(line.coords[0])
last = Point(line.coords[-1])
use:
first = np.array(line.coords)[0]
last = np.array(line.coords)[-1]
Linestring1 = LINESTRING (51.2176008 4.4177154, 51.21758 4.4178548, **51.2175729 4.4179023**, *51.21745162000732 4.41871738126533*)
Linestring2 = LINESTRING (*51.21745162000732 4.41871738126533*, **51.2174025 4.4190475**, 51.217338 4.4194807, 51.2172511 4.4200562, 51.2172411 4.4201077, 51.2172246 4.4201654, 51.2172067 4.420205, 51.2171806 4.4202355, 51.2171074 4.4202929, 51.2170063 4.4203409, 51.2169564 4.4203641, 51.2168076 4.4204243, 51.2166588 4.4204833, 51.2159018 4.420431, 51.2154117 4.4203843)
Considering these two linestrings were cut from a bigger linestring, how to get the endpoint of a LineString?
– Point(51.21745162000732 4.41871738126533) removed
– The new last element of linestring 1 = “ 51.2175729 4.4179023
– The new first element of linestring 2 = “ 51.2174025 4.4190475
In short, I want to get the new last value of the first part (linestring1) and the new first value of the second part (linestring2), but without the point where I cut them. How can I make this work?
Solved with two routines that allows a Linesstring to be split as follows.
-
Function: split_first
returns first point, and LineString of points without the first -
Function: split_last
return last point, and LineString of points from first not including the last
Code
from shapely.ops import nearest_points
from shapely.geometry import Point
from shapely.geometry import LineString
def split_first(linestring):
" returns first point and linestring without first point "
coords = list(linestring.coords)
p, *x = coords
return Point(p), LineString(x)
def split_last(linestring):
" returns first point and linestring without first point "
*x, p = list(linestring.coords) = list(linestring.coords)
return Point(p), LineString(x)
Test
Data
linestring = LineString([(51.2176008,4.4177154), (51.21758,4.4178548), (51.2175729,4.4179023), (51.21745162000732,4.41871738126533)])
First point, and linestring not including the first
p, l = split_first(linestring)
print(p)
print(l)
Out
POINT (51.2176008 4.4177154)
LINESTRING (51.21758 4.4178548, 51.2175729 4.4179023, 51.21745162000732 4.41871738126533)
First last point, and linestring not including the last
p, l = split_last(linestring)
print(p)
print(l)
Out
POINT (51.21745162000732 4.41871738126533)
LINESTRING (51.2176008 4.4177154, 51.21758 4.4178548, 51.2175729 4.4179023)
To get endpoints of a LineString
, you just need to access its boundary
property:
from shapely.geometry import LineString
line = LineString([(0, 0), (1, 1), (2, 2)])
endpoints = line.boundary
print(endpoints)
# MULTIPOINT (0 0, 2 2)
first, last = line.boundary
print(first, last)
# POINT (0 0) POINT (2 2)
Alternatively, you can get the first and the last points from the coords
cordinate sequence:
from shapely.geometry import Point
first = Point(line.coords[0])
last = Point(line.coords[-1])
print(first, last)
# POINT (0 0) POINT (2 2)
In your specific case, though, as you want to remove the last point of the first line, and the first point of the second line, and only after that get the endpoints, you should construct new LineString
objects first using the same coords
property:
from shapely.wkt import loads
first_line = loads("LINESTRING (51.2176008 4.4177154, 51.21758 4.4178548, 51.2175729 4.4179023, 51.21745162000732 4.41871738126533)")
second_line = loads("LINESTRING (51.21745162000732 4.41871738126533, 51.2174025 4.4190475, 51.217338 4.4194807, 51.2172511 4.4200562, 51.2172411 4.4201077, 51.2172246 4.4201654, 51.2172067 4.420205, 51.2171806 4.4202355, 51.2171074 4.4202929, 51.2170063 4.4203409, 51.2169564 4.4203641, 51.2168076 4.4204243, 51.2166588 4.4204833, 51.2159018 4.420431, 51.2154117 4.4203843)")
first_line = LineString(first_line.coords[:-1])
second_line = LineString(second_line.coords[1:])
print(first_line.boundary[1], second_line.boundary[0])
# POINT (51.2175729 4.4179023) POINT (51.2174025 4.4190475)
Similar to Georgy‘s solution, you can get their coords
by unpacking the one you want and ignoring the rest using *_
.
from shapely.geometry import LineString
linestring1 = LineString([(51.2176008, 4.4177154), (51.21758, 4.4178548),
(51.2175729, 4.4179023),
(51.21745162000732, 4.41871738126533)])
linestring2 = LineString([(51.21745162000732, 4.41871738126533),
(51.2174025, 4.4190475), (51.217338, 4.4194807),
(51.2172511, 4.4200562), (51.2172411, 4.4201077),
(51.2172246, 4.4201654), (51.2172067, 4.420205),
(51.2171806, 4.4202355), (51.2171074, 4.4202929),
(51.2170063, 4.4203409), (51.2169564, 4.4203641),
(51.2168076, 4.4204243), (51.2166588, 4.4204833),
(51.2159018, 4.420431), (51.2154117, 4.4203843)])
*_, last_new_1, last_1 = linestring1.coords
first_2, first_new_2, *_ = linestring2.coords
print(last_new_1)
print(first_new_2)
# (51.2175729, 4.4179023)
# (51.2174025, 4.4190475)
Note: The syntax changes as of shapely 2.0
Per Georgy’s answer, .boundary still works (as of Dec 2022) but is being deprecated and throws a warning.
For multiparts, .geoms is preferred moving forward.
For accessing single points from a coordinate sequence, use numpy.
So, instead of:
first = Point(line.coords[0])
last = Point(line.coords[-1])
use:
first = np.array(line.coords)[0]
last = np.array(line.coords)[-1]