5

Is there a generic way to flip/invert/reverse the order of the coordinates of a shapely geometry?

Here are a couple of examples of what I mean.

Example 1

Input: LINESTRING (0 0, 1 1, 3 1, -3 3)

Output: LINESTRING (-3 3, 3 1, 1 1, 0 0)

Example 2

Input: MULTILINESTRING ((120 105, 140 98),(160 103, 140 98),(100 100, 120 105))

Output: MULTILINESTRING ((120 105, 100 100), (140 98, 160 103), (140 98, 120 105))

My implementation (small reproducible example)

I was able to put together a small implementation for LineStrings and MultiLineStrings. Note that it is not efficient at all and it doesn't account for the vast amount of shape types (polygons, multipolygons, etc).

import shapely

# Shape inverter
def invert_coords(input_geom):
    if input_geom.type.lower() == 'linestring':
        coords = [tuple(coord) for coord in list(input_geom.coords)][::-1]
        out_geom = shapely.geometry.LineString(coords)
    elif input_geom.type.lower() == 'multilinestring':
        coords = [list(this_geom.coords)[::-1] for this_geom in input_geom.geoms][::-1]
        out_geom = shapely.geometry.MultiLineString(coords)
    return out_geom

# Write the WKTs
ls_wkt = 'LINESTRING (0 0, 1 1, 3 1, -3 3)'
mls_wkt = 'MULTILINESTRING ((120 105, 140 98),(160 103, 140 98),(100 100, 120 105))'

# Generate the shapely geometries
ls = shapely.wkt.loads(ls_wkt)
mls = shapely.wkt.loads(mls_wkt)

# Inverting shapes
ls_inv = invert_coords(ls)
mls_inv = invert_coords(mls)

print(ls_inv.wkt)
# LINESTRING (-3 3, 3 1, 1 1, 0 0)

print(mls_inv.wkt)
# MULTILINESTRING ((120 105, 100 100), (140 98, 160 103), (140 98, 120 105))

Back to the main question

Is there a generic yet straightforward way of inverting the order of the coordinates for any shapely geometry?

4 Answers 4

6

Maybe explore using shapely.ops.transform():

Applies func to all coordinates of geom and returns a new geometry of the same type from the transformed coordinates.

It does the work of parsing out the coordinate sequences from the given geometry type, and lets you operate on each axis as an iterable.

import shapely.wkt
import shapely.ops


def reverse_geom(geom):
    def _reverse(x, y, z=None):
        if z:
            return x[::-1], y[::-1], z[::-1]
        return x[::-1], y[::-1]

    return shapely.ops.transform(_reverse, geom)


ls_wkt = 'LINESTRING (0 0, 1 1, 3 1, -3 3)'
lsz_wkt = 'LINESTRING (0 0 0, 1 1 1, 3 1 1, -3 3 2)'
mls_wkt = 'MULTILINESTRING ((120 105, 140 98),(160 103, 140 98),(100 100, 120 105))'

ls = shapely.wkt.loads(ls_wkt)
lsz = shapely.wkt.loads(lsz_wkt)
mls = shapely.wkt.loads(mls_wkt)

print(ls)
print(reverse_geom(ls))

print(lsz)
print(reverse_geom(lsz))

print(mls)
print(reverse_geom(mls))

There's also shapely.geometry.polygon.orient() which will let you obtain a specific polygon ring ordering

3
  • Very nice! Thank you! Is there a way to modify this so that it can handle 3D cases as well (x, y and z coordinates)?
    – Felipe D.
    Commented Nov 8, 2021 at 19:40
  • 1
    Indeed there is, just gotta change the signature. Updated!
    – mikewatt
    Commented Nov 8, 2021 at 19:44
  • Fantastic! Thank you!!! This is great =)
    – Felipe D.
    Commented Nov 8, 2021 at 19:46
4

This is now very easy with the .reverse method:

import shapely

line = shapely.wkt.loads("LINESTRING (0 0, 1 1, 3 1, -3 3)")
multiline = shapely.wkt.loads("MULTILINESTRING ((120 105, 140 98),(160 103, 140 98),(100 100, 120 105))")

print(line.reverse().wkt)
#'LINESTRING (-3 3, 3 1, 1 1, 0 0)'

print(multiline.reverse().wkt)
#MULTILINESTRING ((140 98, 120 105), (140 98, 160 103), (120 105, 100 100))
2

There is an open issue about reversing geometries https://github.com/geopandas/geopandas/issues/1682. There does not seem to be a native Shapely implementation yet but for example pygeos has such function https://pygeos.readthedocs.io/en/latest/constructive.html#pygeos.constructive.reverse

2
  • Awesome, thanks for the tip! Shifting back and forth between shapely and pygeos geometries can be a little cumbersome, but it gets the job done! shapely.wkt.loads(pygeos.to_wkt(pygeos.reverse(pygeos.Geometry(shapely_geom.wkt)))) Thanks again for the tip!
    – Felipe D.
    Commented Nov 8, 2021 at 19:45
  • Since Shapely and PyGEOS are merging into Shapely 2.0, shifting back and forth will hopefully not be needed for much longer.
    – Erik
    Commented May 19, 2022 at 9:36
2

The substring operation in Shapely can be used for this.

From the substring docs:

If the start distance is actually past the end distance, then the reversed substring is returned such that the start distance is at the first coordinate.

Thus we can do:

from shapely.ops import substring
ls = LineString([(0,0), (1,0), (2,0)])

reversed_ls = substring(ls, 1, 0, normalized=True)
# or
reversed_ls = substring(ls, ls.length, 0)

substring only works on LineStrings though, not MultiLineStrings.

If you want a convenience function reverse, you can wrap substring using functools.partial:

from functools import partial
reverse = partial(substring, start_dist=1, end_dist=0, normalized=True)
reversed_ls = reverse(ls)
2
  • 1
    Awesome! Thanks for the suggestion!!! You can also avoid using ls.length by instead setting the normalized parameter to True. Here's the full command: substring(ls, 1, 0, normalized=True).
    – Felipe D.
    Commented May 19, 2022 at 15:13
  • 1
    Thanks, that's nicer! Edited my answer.
    – Erik
    Commented May 20, 2022 at 9:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.