tags:

views:

91

answers:

3

Hi All

Given any two points on compass (Start range and End Range) to form a range. Example from 270(Start range) degrees to 45(End range)degrees and given another point say 7 , how can I work out if that point is between Start and End range ?

I'm trying to write some code to work out if the Wind (in the above point 3) is blowing from the sea or from the land , where the land is defind by Start range and End range .

Many Thanks Andy

Update:11/10/2010 18:46BST From @sth's solution the following seems to work for as expected.

#!/usr/bin/perl -w

sub isoffshore {

        my ( $beachstart,$beachend,$wind) = @_;

        if( $beachend < $beachstart) {
                $beachend += 360;
        }

        if ($wind < $beachstart){
                $wind += 360;
        }

        if ($wind <= $beachend){
                print ("Wind is Onshore\n");
                return 0;
        }else{
                print ("Wind is Offshore\n");
                return 1;

        }

}

isoffshore ("0","190","3"); #Should be onshore
isoffshore ("350","10","11"); #Should be offshore
isoffshore ("270","90","180");#Should be offshore
isoffshore ("90","240","0"); #Should be offshore
isoffshore ("270","90","180");#Should be offshore
isoffshore ("0","180","90"); #Should be onshore
isoffshore ("190","0","160"); #Should be offshore
isoffshore ("110","240","9"); #Should be offshore
isoffshore ("0","180","9"); #Should be onshore
isoffshore ("0","180","179"); #Should be onshore

Results

@localhost ~]$ ./offshore2.pl
Wind is Onshore
Wind is Offshore
Wind is Offshore
Wind is Offshore
Wind is Offshore
Wind is Onshore
Wind is Offshore
Wind is Offshore
Wind is Onshore
Wind is Onshore
+1  A: 

This should work if all your points are like 0 <= point < 360:

def between(lower, upper, point):
   if upper < lower:
      upper += 360
   if point < lower:
      point += 360
   return (point <= upper)
sth
This seems right but the upper and lower variable names seem a bit confusing. Maybe startRange and endRange would be a bit more intuitive. The check "upper < lower" just keeps making me lose the plot. :)
Chris
@sth, @Chris: This fails if `lower = 3 * pi /2`, `upper = 3 * pi / 2 - epsilon` and `point = 3 * pi / 2 - epsilon / 2` where `epsilon` is a small positive number. In this case, your algorithm would detect that `upper < lower` is true, would set `upper = 7 * pi / 2 - epsilon` and would then test if `3 * pi / 2 - epsilon / 2` is less than or equal to `7 * pi / 2 - epsilon`. As this is true, it would return `true`. However, `3 * pi / 2 - epsilon / 2` is not on the arc from `3 * pi / 2` to `3 * pi / 2 - epsilon`.
Jason
This works. For Me.
AndyM
@Andy: It's wrong.
Jason
@Jason: No, it would as second step test `point < lower`, and since that's true, it would also increase `point` by `2*pi`. That makes `point` `epsilon/2` greater than `upper` and causes the function to return `false`.
sth
Sorry I'm confused as to why pi and epsilon got into this discussion ?
AndyM
The solution seems to work for me. @Jason can you give an example where it will not work ?
AndyM
My mistake. I inserted an `else` in my head. Please accept my sincerest apologies.
Jason
@AndyM: pi entered the discussion because Jason is using radians instead of degrees. Epsilon is just a variable that was being used to define the points he was talking about with a parameter rather than a static number.
Chris
+1  A: 

By points on a compass, I assume that you mean points on the unit circle. And by "between" two points on the unit circle, you mean that you have described an arc on the unit circle and want to know if a given point is in that arc.

Assume that all points on the unit circle are described by angles and for such an angle t describing a point on the unit circle we require that 0 <= t < 2 * pi.

Let's say that your arc is described as the arc (t_1, t_2) (that is, traverse counterclockwise from the point on the unit circle corresponding to the angle t_1 to the point on the unit circle corresponding to the angle t_2*). Then, given a point on the unit circle with corresponding angle t, it is true that t is on the counterclockwise arc from t_1 to t_2 if t_2 > t_1 and t_1 <= t <= t_2 or t_1 > t_2 and not t_2 <= t <= t_1.

Thus,

public bool IsInArc(double t1, double t2, double t) {
     Guard.Against<ArgumentOutOfRangeException>(t1 < 0 || t1 >= 2 * Math.PI);
     Guard.Against<ArgumentOutOfRangeException>(t2 < 0 || t2 >= 2 * Math.PI);
     Guard.Against<ArgumentOutOfRangeException>(t < 0 || t >= 2 * Math.PI);
     return t2 > t1 ? IsInArcInternal(t1, t2, t) : !IsInArcInternal(t2, t1, t);
}

private bool IsInArcInternal(double t1, double t2, double t) {
     Guard.Against<ArgumentException>(t2 < t1);
     return t1 <= t && t <= t2;
}
Jason
This doesn't seem right. If t1 = 3*pi/2 and t2 = pi/2 and t is 0 then t is in the arc (starting at t1 and going clockwise past zero to t2). However, your technique suggests it is not. I believe your technique is not takign the arc defined clockwise from t1 but always whichever arc does not contain the zero point (ie does not wrap around). The only value of t1,t2 that allows the zero point to be included for yours is t1=0 or t2 = 0.
Chris
Jason
@Paul R: I don't agree. Please see my comment to Chris. Please note the `!` in front of the call to `IsInArcInternal` when `t2 <= t1`.
Jason
@Jason: sorry - I missed the `!` - unfortunately I can't remove the down-vote now as it's too old - apologies
Paul R
@Paul R: Edited.
Jason
@Jason: Ah yes, I did miss the not. Sorry. And this is why I never down vote without giving the person in question a chance to prove me wrong. :) I'll put a nice upvote in now. :)
Chris
A: 

Here's a one line function which uses the modulo (%) operator to handle the wraparound case. Input values are assumed to be in the range 0..359 (degrees):

int inRange(int start, int end, int point)
{
    return (point + 360 - start) % 360 <= (end + 360 - start) % 360;
}

//
// Test harness
//

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    assert(inRange(90, 270, 0) == 0);
    assert(inRange(90, 270, 45) == 0);
    assert(inRange(90, 270, 180) == 1);
    assert(inRange(90, 270, 315) == 0);
    assert(inRange(270, 90, 0) == 1);
    assert(inRange(270, 90, 45) == 1);
    assert(inRange(270, 90, 180) == 0);
    assert(inRange(270, 90, 315) == 1);

    if (argc >= 4)
    {
        int start = atoi(argv[1]);
        int end = atoi(argv[2]);
        int point = atoi(argv[3]);
        int result = inRange(start, end, point);

        printf("start = %d, end = %d, point = %d -> result = %d\n", start, end, point, result);
    }
    return 0;
}

Note that the + 360 term on each side of the test is required in C/C++ due to the unfortunate way that % treats negative values.

Paul R
Very nice. Such a simple solution :-)
AndyM
@AndyM: thanks - I was determined to come up with a branchless solution. It's a pity about the C/C++ % operator though - if it worked properly for negative values then the solution would have been simpler.
Paul R