1

I'm developing an iOS app, which has to show POIs (points of interest) depending on device heading. I used CLLocationManager to get user's location and heading. I have one pair of destination's coordinates. Based on this I'm calculating which quarter is that and returning float value of deviation from south (0 degrees) in degrees. I have -/+180 degrees in the north and 0 in the south. Here is code snippet:

-(float)updateTargetLongitude:(float)lon Latitude:(float)lat{
//    //longitude = x
//    //latitude = y
      NSLog(@"current location = (%.5f, %.5f)", [[NSUserDefaults standardUserDefaults] doubleForKey:@"currentLongitude"], [[NSUserDefaults standardUserDefaults] doubleForKey:@"currentLatitude"]);
      float x = lon - [[NSUserDefaults standardUserDefaults] doubleForKey:@"currentLongitude"];
      float y = lat - [[NSUserDefaults standardUserDefaults] doubleForKey:@"currentLatitude"];
      float angle;
      NSLog(@"Searching angle from source (%.5f, %.5f) to destination (%.5f, %.5f)", locationManager.location.coordinate.longitude, locationManager.location.coordinate.latitude, lon, lat);
      if (x == 0 && y == 0) {
          NSLog(@"you're there already!");
          return -0.1;
      }
      if(x == 0 && y > 0){
          NSLog(@"look north");
          angle = 180.0;
      }
      if (x == 0 && y < 0) {
          NSLog(@"look south");
          angle = 0;
      }
      if (x > 0 && y == 0) {
          NSLog(@"look east");
          angle = 90.0;
      }
      if (x < 0 && y == 0) {
          NSLog(@"look west");
          angle = -90;
      }
      if (x > 0 && y > 0) {
          NSLog(@"first quarter");
          angle = -atan2f(y, x) - M_PI_2;
      }
      if (x < 0 && y > 0) {
          NSLog(@"second quarter");
          angle = atan2f(y, x) + M_PI_2;
      }
      if (x < 0 && y < 0) {
          NSLog(@"third quarter");
          angle = atan2f(x, y);
      }
      if (x > 0 && y < 0) {
          NSLog(@"fourth quarter");
          angle = -atan2f(x, y);
      }
      NSLog(@"returning radians angle = %.4f for (%.5f, %.5f) :: degrees = %.3f", angle, y, x, angle * 180 / M_PI);
      return angle * 180 / M_PI ;
}

Somehow I have situation, when target is in fourth quarter, but is -93 degrees from south. I'm lost and I don't have any idea how to fix that...

edit: by quarter I mean Cartesian coordinate system, where +y is north, +x is east and so on!

p.s.: I've read that iPhone compass is really bad, but if so how app like Google maps is working properly?

edit2: I made a mistake with angles. Oficially o have -90 degrees in east and 90 in west.

raistlin
  • 4,298
  • 5
  • 32
  • 46
  • 1
    Side note: Testing floating points for equality might not give you the results you expect. – John May 23 '13 at 14:40
  • yes, but looking exactly N/S/E/W is with device accuracy almost imposible, so this was strictly for "mathematical" purpose – raistlin May 23 '13 at 14:42

3 Answers3

2

If I see it correctly, the formula

angle = atan2(x, -y) * 180.0/M_PI;

should work in all quadrants, making all the if statements unnecessary.


atan2(y, x) returns the angle between the vector (x, y) and the positive x-axis, the return value is always between -pi and pi.

Replacing (y, x) by (x, -y) in the arguments means that the vector is rotated by 90 degrees, therefore the result of the above formula is the angle measured to the negative y-axis, which is what you wanted.


Update (according to "edit2" in the question): If the requirement is "south = 0 deg", "east = -90 deg", "west = +90 deg" then the formula would be

angle = atan2(-x, -y) * 180.0/M_PI;
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • facing south I have 0 degrees, to east is -, to west is +, so I guess it should be finally atan2(-x,-y), right? – raistlin May 24 '13 at 10:23
  • @FilipChwastowski: For x > 0, y == 0 my formula gives angle = 90 degrees, I thought that is what you wanted. – Martin R May 24 '13 at 10:50
  • nah, I did mention that I have -/+180 degrees in north ;) please edit your answer so nobody will get lost where finally +/- degree direction is so I can accept ;) – raistlin May 24 '13 at 11:01
  • @FilipChwastowski: Sorry, I am confused now. In your question you have the code `if (x > 0 && y == 0) { NSLog(@"look east"); angle = 90.0; }`, so I based my answer on that. So you actually want angle = -90 for the east direction? – Martin R May 24 '13 at 11:05
  • my bad, sorry - of course you are right, once again apology :) answer accepted ;) – raistlin May 24 '13 at 11:07
  • I edited my question, so nobody will get confuse ;) could you can now edit answer so it will fit :) thx again! – raistlin May 24 '13 at 11:10
1

The atan2 function already takes quadrant into account. In other words, it "knows" that you're in the third quadrant if both x and y are negative. Knowing that, you can see what the angle output for atan2(y, x) is, and then change it to how you want it displayed.

The main reason why Google maps works even with a relatively inaccurate compass is that the structures of the roads give you hints, so you can get by with a bigger error than if you didn't know where the roads were.

John
  • 15,990
  • 10
  • 70
  • 110
  • are you sure it "knows"? what if x * y > 0 ? it's possible with situations: both < 0 or both > 0 ... – raistlin May 23 '13 at 14:31
  • Yes. That's why you supply atan2 with two arguments. If `x` and `y` are both negative, you're in quadrant III. If they're both positive, you're in quadrant I, etc. The output of atan2 will be between pi and -pi (I think). That's the whole circle. – John May 23 '13 at 14:39
  • arctg(alfa) is always between -pi/2 and pi/2 ;) – raistlin May 23 '13 at 14:43
  • That's exactly the difference between arctg(alpha) and atan2(y, x). The arctangent of 1 *can be* either 45 degrees (or 225 degrees, or 405 degrees, ...) but arctg(1) is pi/4 (it chooses the one between -pi and pi). But then atan2(1, 1) != atan2(-1, -1). atan2(1, 1) is pi/4, but atan2(-1, -1) is -3pi/4. – John May 23 '13 at 15:05
0

Actually, after deciding what quadrant it is I changed code to always calculate atan2 with positive values of x and y - now it works!

raistlin
  • 4,298
  • 5
  • 32
  • 46