Sort a polygon feature class spatially using Python

I was manually adjusting a custom grid for a map series this morning, a task which I have not had to do for several years now, when the time came to export the maps. By this point, my grid numbers were completely messed up because of all the shifting and reshaping I had to do.

It would make sense to number the grid from left to right, top to bottom. However, the ability to spatially sort a feature class in ArcGIS is hidden behind an Advanced Licence in the Sort tool.

License: For the Field(s) parameter, sorting by the Shape field or by multiple fields is only available with an Desktop Advanced license. Sorting by any single attribute field (excluding Shape) is available at all license levels.

I had this prickly feeling in the back of my head that I had found a workaround for this before. I went searching through the blog (since I have posted about things twice because I forgot about the first post), but I didn’t find it.

Undeterred, I searched through my gists (unrelated note – why does using the “user:<username> <search term> ” trick in the GitHub search bar now flag as spam?!) and I found it!

#
# @date 13/07/2015
# @author Cindy Williams
#
# Sorts a polygon feature class by descending X and ascending Y,
# to obtain the spatial order of the polys from left to right,
# top to bottom. Bypasses the Advanced licence needed to
# do this in the Sort tool.
#
# For use in the Python window in ArcMap.
#
import arcpy
mxd = arcpy.mapping.MapDocument("CURRENT")
lyr = arcpy.mapping.ListLayers(mxd, "DDP")[0]
# Get the extent of each feature, and store as tuple (name, x coord of top right, y coord of top right)
ddp = [(row[0], row[1].extent.XMax, row[1].extent.YMax) for row in arcpy.da.SearchCursor(lyr, ("Name", "SHAPE@"))]
# Sort by x descending
ddp.sort(key=lambda row:row[1], reverse=True)
# Sort by y ascending
ddp.sort(key=lambda row:row[2])
# Reverse the list to get the L->R, T->B order
dct = {}
# Must still be optimised
for i, j in enumerate(reversed(ddp)):
# Key is name, value is index + 1
dct[j[0]] = i + 1
# Write values back to feature class
with arcpy.da.UpdateCursor(lyr, ("Name", "MapNum")) as cursor:
for row in cursor:
row[1] = dct[row[0]]
cursor.updateRow(row)

Link for mobile users is here.

I’m pretty sure there is a much better way to do this, but I need this so rarely that I don’t see the need to rewrite it. It also would work much better as a function. It takes a polygon feature class and sorts the selected features from left to right, top to bottom.

Description of this hastily drawn, blurry PowerPoint graphic: Chaotic, unsorted grid on the left. Slightly less chaotic, sorted grid on the right.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.