I am attempting to use Python's .sort() with a lambda function to spatially sort an existing list of Polygon objects, to then write to a feature class with an InsertCursor. Sorting will be based on user-supplied specifications (north to south first, followed by east to west as a secondary sort, for example). This snippet is part of a larger script that has been pared down so it's easier to look at (the list of polygons and the output feature class are created at an earlier stage, not shown here).
# User supplied sort priorities.
sort_1 = 'EW'
sort_2 = 'SN'
# Example: for N to S sorting, sort polygon using centroid.Y attribute, descending.
sort_dict = {"NS": (-1, 'Y'),
"SN": ( 1, 'Y'),
"WE": ( 1, 'X'),
"EW": (-1, 'X'),}
# Getting the direction and axes (primary and secondary) to sort by.
dir1, axis1 = sort_dict[sort_1]
dir2, axis2 = sort_dict[sort_2]
# An existing list of polygon objects, sorted using specifications above.
# "dir" handles ASC/DSC, getattr() gets the appropriate x or y value from the
# centroid.
all_sheet_polys.sort(key=lambda poly: (dir1 * getattr(poly.centroid, axis1),
dir2 * getattr(poly.centroid, axis2)))
# Write sorted polygons to an existing blank feature class.
with ap.da.InsertCursor(out_fc, ['SHAPE@', 'PageNumber']) as icurs:
sheet_count = 1
for sheet in all_sheet_polys:
icurs.insertRow([sheet, sheet_count])
sheet_count += 1
The problem is that the two-part lambda sort does not always work. If the FIRST sorting is along the x-axis (EW/WE), it will. The first image is east to west, then south to north. The second is west to east, then north to south. Both are created as expected.
The problem occurs when x-axis sorting is done as a SECONDARY priority. Here is an example using inputs "SN" and "WE" as inputs, for south to north followed by west to east. The primary sort, south to north, works fine. However, the west to east does not, and the ordering appears arbitrary.
Any ideas would be appreciated. Seems like something silly I am overlooking, but it has got me stumped.
Solved! Go to Solution.
Have you checked the Y components of the centroids? Are they exactly equal for every polygon in each row? If not, the N-S sorting will override the W-E sorting
This appears to be it! I had checked this, but only out to about 6 decimal places, so I was not handling float precision appropriately.
I changed the sorting function to round both the X and the Y values, to avoid any issues with tiny differences in position:
all_sheet_polys.sort(key=lambda poly: (dir1 * round(getattr(poly.centroid, axis1), 2),
dir2 * round(getattr(poly.centroid, axis2), 2)))
Here are the "actual" coordinate values of the bottom row (X-values truncated for display), shown in the following image. When those values are sorted (using raw SN-WE coordinate values), the ordering was somewhat ambiguous along a given row because of your point about precision.
1 X 11,768,434 Y 7,022,867.011901856400
2 X 11,767,927 Y 7,022,867.011901855469
3 X 11,767,421 Y 7,022,867.011901854537
4 X 11,766,914 Y 7,022,867.011901856400
5 X 11,766,407 Y 7,022,867.011901855469
6 X 11,765,901 Y 7,022,867.011901854537
7 X 11,765,394 Y 7,022,867.011901856400
8 X 11,764,887 Y 7,022,867.011901855469
9 X 11,764,381 Y 7,022,867.011901854537
10 X 11,763,874 Y 7,022,867.011901856400
11 X 11,763,367 Y 7,022,867.011901855469
After the rounding, problem solved (example of bottom row):
Thanks a ton, I appreciate the assistance.
Have you checked the Y components of the centroids? Are they exactly equal for every polygon in each row? If not, the N-S sorting will override the W-E sorting
This appears to be it! I had checked this, but only out to about 6 decimal places, so I was not handling float precision appropriately.
I changed the sorting function to round both the X and the Y values, to avoid any issues with tiny differences in position:
all_sheet_polys.sort(key=lambda poly: (dir1 * round(getattr(poly.centroid, axis1), 2),
dir2 * round(getattr(poly.centroid, axis2), 2)))
Here are the "actual" coordinate values of the bottom row (X-values truncated for display), shown in the following image. When those values are sorted (using raw SN-WE coordinate values), the ordering was somewhat ambiguous along a given row because of your point about precision.
1 X 11,768,434 Y 7,022,867.011901856400
2 X 11,767,927 Y 7,022,867.011901855469
3 X 11,767,421 Y 7,022,867.011901854537
4 X 11,766,914 Y 7,022,867.011901856400
5 X 11,766,407 Y 7,022,867.011901855469
6 X 11,765,901 Y 7,022,867.011901854537
7 X 11,765,394 Y 7,022,867.011901856400
8 X 11,764,887 Y 7,022,867.011901855469
9 X 11,764,381 Y 7,022,867.011901854537
10 X 11,763,874 Y 7,022,867.011901856400
11 X 11,763,367 Y 7,022,867.011901855469
After the rounding, problem solved (example of bottom row):
Thanks a ton, I appreciate the assistance.