mk8efz mk8efz - 3 months ago 10
Python Question

Simple, ugly function to produce an orientation from an angle.

I wrote a function that takes a degree and returns the orientation as 'N', 'NE', ...etc. Very simple, but it's ugly - is there any way to rewrite this to make it...prettier?

def orientation(tn):
if 23 <= tn <= 67:
o = 'NE'
elif 68 <= tn <= 113:
o = 'E'
elif 114 <= tn <= 158:
o = 'SE'
elif 159 <= tn <= 203:
o = 'S'
elif 204 <= tn <= 248:
o = 'SW'
elif 249 <= tn <= 293:
o = 'W'
elif 294 <= tn <= 338:
o = 'NW'
else:
o = 'N'
return o

Answer

Use bisection:

from bisect import bisect_left

directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N']
boundaries = [22,  67,   113, 158,  203, 248,  293, 338,  360]

def orientation(tn):
    return directions[bisect_left(boundaries, tn)]

bisect_left() (very efficiently) finds the index into which you'd insert tn into the boundaries list; that index is then mapped into the directions list to translate to a string.

Bisection only takes up to 4 steps to find the right boundary (log2(len(boundaries))).

You could also add 22 and divide the value modulo 360 by 45:

directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N']

def orientation(tn):
    index = ((tn + 22) % 360) // 45
    return directions[index]

However, your original boundaries were not evenly distributed at 45 degrees each, so this gives a slightly different result (your N boundaries span 44 degrees, while E is allotted 46 degrees). Bisection doesn't care about such exceptions; you can shift the boundaries all you like.