Split a list of Azimuth into two groups equally

217 Views Asked by At

I'm trying to make a program to get azimuth value from two points using this code:

import math


def azimuth(x1, x2, y1, y2):
    temp = (y2 - y1) / (x2 - x1)
    res = math.degrees(abs(math.atan(temp)))
    if x2 > x1 and y2 > y1:    #Q1
        res = 90 - res
    elif x2 < x1 and y2 > y1:  #Q2
        res = (90 - res) * -1
    elif x2 < x1 and y2 < y1:  #Q3
        res = (90 + res) * -1
    elif x2 > x1 and y2 < y1:  #Q4
        res += 90
    else:
        raise ValueError('No point difference.')
    return res

I've been able to get azimuth value with range (-180)-180. Next, I need to split it into two groups of azimuth value equaly based on range of value. The goal is to get two group which get the closest azimuth value.

The problem is that pairs of points on quadrant III ((-180)-(-90)) and quadrant IV (90-180) logically can be consider as close (e.g:(-179) and 179). About how to split it equally, I've been thinking of using sorted list of azimuth and using index idx = int(math.ceil((len(lst)-1)/2)) as the median to split the list. This is how I do it on code:

lst = [-60, -50, -40, -30, -20, -10, 10, 20, 30, 40, 50, 60]
def split_list(lst):
    lst.sort()
    idx = int(math.ceil((len(lst)-1)/2))
    left = []
    right = []
    for i in lst:
        if i < lst[idx]:
            left.append(i)
        else:
            right.append(i)
    print(left)
    print(right)
    return left, right

split_list(lst)

The code above will return list [-60, -50, -40, -30, -20, -10] and [10, 20, 30, 40, 50, 60] as the result.

Is there any idea of how to do this? With the current condition, I don't have any idea of how to make quadrant III and IV considered as close.

2

There are 2 best solutions below

0
On

Your problem can be reduced to finding a distance between two phase points. I say phase because phase is wrapped (in your case % 360). Thus, you need to compare the distance both ways: clockwise and counter-clockwise. The simplest might be:

dist = min(abs(A2-A1), abs(360+A1-A2))  # requiring A1 <= A2

If the clockwise distance is the smallest, then the first term in min() will be your solution. If counter-clockwise, then the second term in min() will be your solution. To do grouping, you'd then calculate the distances between desired points. Note that arrays don't need to be sorted for this to work, just that A1 <= A2.

0
On

You might be interested in math.atan2(y, x).

The point of atan2() is that the signs of both inputs are known to it, so it can compute the correct quadrant for the angle.

Which means that you can remove all the quadrant logic from your code, and basically replace the whole function with math.degrees(math.atan2(y2 - y1, x2 - x1)). It also avoids a division by 0 if x1 == x2.

If you have two azimuths, you can calculate the minimum rotation between both with:

def shortest_rotation(start_angle, end_angle):
    return (end_angle - start_angle + 180) % 360 - 180

You can split the list of azimuths depending on the sign of the output.

Just for fun, here's a small script to split azimuths left and right of split_at_azimuth, and display them on a polar plot:

def shortest_rotation(start_angle, end_angle):
    return (end_angle - start_angle + 180) % 360 - 180

azimuths = list(range(0, 370, 10))
split_at_azimuth = 60

left, right = [], []
for azimuth in azimuths:
    (left, right)[shortest_rotation(azimuth, split_at_azimuth) >= 0].append(azimuth)

import numpy as np
import matplotlib.pyplot as plt

t1 = [np.pi * a / 180 for a in right]
t2 = [np.pi * a / 180 for a in left]

fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
ax.plot(t1, [1 for _ in t1], '+')
ax.plot(t2, [1 for _ in t2], '+')
ax.set_rmax(2)
ax.set_rticks([])
ax.grid(True)

ax.set_title("Left and right azimuth", va='bottom')
plt.show()

enter image description here