Is there a way to "sort" the points of a MultiLineString such that they follow a more sequential order? Let me illustrate what I mean with a graphical example:
Input
Suppose I have the following MultiLineString: MULTILINESTRING ((120 105, 140 98),(160 103, 140 98),(100 100, 120 105))
Note how, to our human eyes, it seems out of order: the first (orange) segment is in the middle, the second (green) segment is coded backwards in relation to the other segments (i.e., pointing to the left instead of to the right).
Desired Output
The desired output would be the following LineString: LINESTRING (100 100, 120 105, 140 98 160 103)
Note how the output is now looks much more sensible. It's one continuous LineString
, the segments are all nicely ordered from left to right and the green segment itself is now oriented from left to right.
Is there a simple way to perform this kind of "reordering" inside MultiLineString
s?
What I've done so far
I'm trying to develop a solution using Python's Shapely
library. My approach has two different steps:
- Extract unique coordinates
- Sort unique coordinates
I've been able to do the first part quite easily, but it's not clear to me how to reorder the points.
I thought about a heuristic that would find the point that is cumulatively furthest to all other points, label that as the starting point, add all other points to a pool of unsorted points and then sequentially find the next closest point until I run out of unsorted points.
However, this sorting heuristic would fail in A BUNCH of cases: consider a geometry that describes a spiral. Or a strong zig-zagging pattern. Or a geometry with a bunch of self-intersections. This heuristic would just fail completely.
Therefore, I suspect this approach of extracting unique points and sorting them isn't the right one.
Back to the start
So, I'm back to my main question: how can I sort the coordinates of a MultiLineString
such that they make more intuitive sense?
Reproducible example and code
Here's the Python code for my very incomplete solution thus far.
import shapely
# Write the MultiLineString WKT
mls_wkt = 'MULTILINESTRING ((120 105, 140 98),(160 103, 140 98),(100 100, 120 105))'
# Generate the shapely geometry
mls = shapely.wkt.loads(mls_wkt)
# Function that extracts unique coordinate values
def get_unique_points(input_multilinestring):
# Gets a nested list of coordinates
coords_nested = [list(this_geom.coords) for this_geom in input_multilinestring.geoms]
# Flattens the list
coords = [item for sub_list in coords_nested for item in sub_list]
# Gets only the unique coords
coords_set = set()
coords_final = []
for i,coord in enumerate(coords):
if coord not in coords_set:
coords_set.add(coord)
coords_final.append(coord)
return coords_final
Best Answer
You might be able to just use
shapely.ops.linemerge
. I haven't used it much, and cannot speak to how it handles potential floating point differences where the geometries in the MultiLineString meet.