3

I'm comparing the orientation of straight lines.

I need to handle the case where the lines have the same orientation but one is drawn in the opposite direction of the other, so for example the first line's orientation is 0 and the second line's orientation is 180.

Is there an elegant way to convert angles to a common orientation in this fashion?

if angle >= 180:
    then angle - 180
else:
    angle

seems like it should be sufficient

I'm concerned I'm not thinking it through all the way and missing a case where it won't work though.

Hugh_Kelley
  • 133
  • 4
  • Your code will leave 180 as is. Also, what happens if your angle is 370 degrees? – Vasili Feb 27 '23 at 20:55
  • the function I'm using to calculate the angle of the line returns a value 0 <= x < 360 and I can check for angle >= 180 instead of using >, that was just a typo, but I'm wondering whether there is a general formula for doing this conversion in trigonometry rather than writing my own by just thinking through the various cases. – Hugh_Kelley Feb 27 '23 at 21:14

2 Answers2

3

I am pretty sure you can just do:

angle1 %= 180
angle2 %= 180
return min(abs(angle1 - angle2),abs(180-abs(angle1 - angle2)))

Your answer would work for all angles 360 degrees or less.

Dylan Levine
  • 1,634
  • i like the modulus solution, it seems strange I haven't been able to find anything solving this by searching for it. Makes me think I'm not using the correct word for what i'm trying to do or misunderstanding something. – Hugh_Kelley Feb 27 '23 at 21:20
  • additionally, the data I'm working with is not necessarily "square" so there are frequently lines with orientation 179 and 1, that will not be matched by orientation with %180 even though they are very very similar in non-directional orientation – Hugh_Kelley Feb 27 '23 at 21:41
  • 1
    So you want 179 and 1 to both be returned as the same angle? I am a little confused as to what you mean by "matched by orientation" – Dylan Levine Feb 27 '23 at 21:44
  • 179 - 1 = 178 indicates that the lines are as different as they can be but in reality a line with orientation 1 is pretty similar to a line with orientation 179 if the direction is not important, only 2 degrees. min( (angle % 180), abs((angle % 180) - 180) seems to be an improvement – Hugh_Kelley Feb 27 '23 at 22:15
  • That might work (I edited the solution) – Dylan Levine Feb 27 '23 at 22:33
  • thanks, i'm surprised I haven't been able to find a generally accepted way of handling this. a colleague suggested using radians instead of degrees and the formula ACOS(ABS(COS(bearing_1 - bearing_2))) but I haven't tested that out – Hugh_Kelley Feb 27 '23 at 22:44
1

From what I understand what you're trying to do is equivalent to: treat numbers that differ by a multiple of $180$ as equivalent, and consider the distance of $x$ and $y$ to be the minimum of $|x' - y'|$ such that $x'$ is equivalent to $x$ and $y'$ is equivalent to $y$.

The modulo operation is designed so that x % 180 returns the unique number $x'$ so that $0 \le x' < 180$ (at least if $x \ge 0$, conventions might differ if $x$ is allowed to be negative). For $0 \le x < 360$, the code in the question is performing the exactly same operation. Applying this operation ensures that the outputs are equal exactly when the inputs are equivalent. This more or less solves the first problem, but complicates the second a little.

The issue, as you've identified, is that for numbers near but on either side of the wraparound threshold, their shifted copies may not be close to each other. The direct distance between $x$ and $y$ is $|x - y|$, while the distance while wrapping around is $180 - |x - y|$ (if you're not convinced of this, see below for a proof). So $$\min (|x - y|, 180 - |x -y|)$$ is a correct formula for the distance you want. Your other formula involving $\cos$ and $\arccos$ is actually equivalent to this, using that $\cos(180 - \theta) = - \cos(\theta)$ and $\cos(\theta) = \cos(|\theta|)$ (where $\theta$ is in degrees).

A proof that the formula is correct

To start with an example, when comparing $179$ with $1$, we want to actually compare $179 \equiv 359 \equiv 539 \equiv -1 \equiv \dots$ with $1 \equiv 181 \equiv 361 \equiv 541 \equiv -179 \equiv \dots$. But because the distance is at most $180$ anyway, it is enough to actually just compare $179$ with $1$, $179$ with $181$ and $359$ with $1$ (and take the minimum distance).

In general, to compare $x$ with $y$ (which are in the range $0 \le x, y < 180$), you want to compare $x$ with $y$, $x + 180$ with $y$ and $x$ with $y + 180$ in absolute value. But we can find a single formula that deals with the last two cases. Using $180 - x \ge 0$ and $180 - y \ge 0$, $$|(x + 180) - y | = |x + (180 - y)| = x + 180 - y = 180 + (x - y)$$ and $$|x - ( y + 180)| = |-180 + x - y| = |180 - x + y| = |(180 - x) + y| = 180 - x + y = 180 - (x-y).$$ So these two values are $180 \pm |x - y|$ in some order. Obviously the smaller of these is $180 - |x - y|$, so since we're looking for the minimum, we can replace this pair with the single formula $180 - |x - y|$.

ronno
  • 11,390