Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

idelovski

macrumors regular
Original poster
Sep 11, 2008
235
0
I typed the compass example from "Sams Teach Yourself iPhone Application Development in 24 Hours" that is supposed to point me to the direction of Chapel Hill, N. Carolina in US. I am in Europe, so I expected it to point to the west, but is pointing somewhere north instead.

I even tried the original project that can be downloaded and it works the same. The code can be downloaded here, it is in the chapter 22 folder, ChapelHill project.

The problem could be with the method that calculates heading for a location from current location. Maybe there's somethin related with radians and degrees.
Code:
/*
 * According to Ask Dr. Math:
 * http://mathforum.org/library/drmath/view/55417.html
 *
 * y = sin(lon2-lon1)*cos(lat2)
 * x = cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1)
 * if y > 0 then
 *    if x > 0 then tc1 = arctan(y/x)
 *    if x < 0 then tc1 = 180 - arctan(-y/x)
 *    if x = 0 then tc1 = 90
 * if y < 0 then
 *    if x > 0 then tc1 = -arctan(-y/x)
 *    if x < 0 then tc1 = arctan(y/x)-180
 *    if x = 0 then tc1 = 270
 *    if y = 0 then
 * if x > 0 then tc1 = 0
 *    if x < 0 then tc1 = 180
 *    if x = 0 then [the 2 points are the same]
 */
That was the algorithm, and here's the actual code:
Code:
-(double)headingToLocation:(CLLocationCoordinate2D)desired
                  current:(CLLocationCoordinate2D)current {

    // Gather the variables needed by the heading algorithm
    double lat1 = current.latitude;
    double lat2 = desired.latitude;
    double lon1 = current.longitude;
    double lon2 = desired.longitude;
    double y = sin(lon2-lon1)*cos(lat2);
    double x = cos(lat1)*sin(lat2) - sin(lat1)*cos(lat2)*cos(lon2-lon1);
    
    double heading = -1;
    if (y > 0) {
        if (x > 0) heading = atan(y/x);
        else if (x < 0) heading = 180 - atan((-1 * y)/x);
        else heading = 90;
    } else if (y < 0) {
        if (x > 0) heading = -1 * atan((-1 * y)/x);
        else if (x < 0) heading = atan(y/x) - 180;
        else heading = 270;
    } else {
        if (x > 0) heading = 0;
        else heading = 180;
    }
    return heading;
}

That method is then used in this compass delegate:
Code:
- (void)locationManager:(CLLocationManager *)manager
       didUpdateHeading:(CLHeading *)newHeading {

    if (self.recentLocation != nil && newHeading.headingAccuracy >= 0) {
        CLLocation *chapelHill = [[[CLLocation alloc]
                                   initWithLatitude:kChapelHillLatitude
                                   longitude:kChapelHillLongitude] autorelease];
        double course = [self headingToLocation:chapelHill.coordinate
                                        current:recentLocation.coordinate];
        double delta = newHeading.trueHeading - course;
        if (abs(delta) <= 10) {
            directionArrow.image = [UIImage imageNamed:@"up_arrow.png"];
        } else {
            if (delta > 180) directionArrow.image = 
                                    [UIImage imageNamed:@"right_arrow.png"];
            else if (delta > 0) directionArrow.image = 
                [UIImage imageNamed:@"left_arrow.png"];
            else if (delta > -180) directionArrow.image = 
                [UIImage imageNamed:@"right_arrow.png"];
            else directionArrow.image = [UIImage imageNamed:@"left_arrow.png"];
        }
        directionArrow.hidden = NO;
    } else {
        directionArrow.hidden = YES;
    }
}

Can someone please point me to some sort of a solution.

EDIT: And I am running this code on my 3Gs, original Compass application seem to work ok, Maps can locate me just fine.
 
The trig functions require their arguments to be in radians while Lat/long are in degrees. Try multiplying your lat/long by
Code:
M_PI/180
to convert them to radians. M_PI is a constant for Pi that should be provided by the SDK.

Like so:
Code:
double lat1 = current.latitude * (M_PI/180);
 
CLLocationCoordinate2D has units of degrees. AFAIK, the trig functions sin(), cos(), atan(), etc. only take units of radians. So it looks to me like the code is mixing units without proper conversions.

It's pretty easy to write test code to verify what units the trig functions are using.

It should also be pretty easy to write test code to pass specific values in CLLocationCoordinate2D, and see if the method calculates the correct heading. I would try points due north, east, south, and west of Chapel Hill, at a distance of say 10 degrees. The results should be unambiguous.
 
Well, I tried something about degrees and radians.

Code:
#define  rad2deg(r) (180. * (r) / M_PI)
#define  deg2rad(d) (M_PI * (d) / 180.)

-(double)headingToLocation:(CLLocationCoordinate2D)desired
                   current:(CLLocationCoordinate2D)current
{
   // Gather the variables needed by the heading algorithm

   // Values in comments from debugger

   double  lon1 = deg2rad(current.longitude);  // 15.93372404 ->  0.278095466
   double  lon2 = deg2rad(desired.longitude);  // -79.0557029 -> -1.379782308

   double  lat1 = deg2rad(current.latitude);   // 45.780882769999998 -> 0.7990271388
   double  lat2 = deg2rad(desired.latitude);   // 35.913150100000003 -> 0.6266802714
   
   double  y = sin (lon2-lon1) * cos (lat2);
   double  x = (cos (lat1) * sin (lat2)) - (sin (lat1) * cos (lat2) * cos (lon2-lon1));
   
   double  heading = -1.;
   
   if (y > 0)  {
      if (x > 0)
         heading = rad2deg (atan (y/x));
      else if (x < 0)
         heading = 180. - rad2deg (atan ((-1 * y)/x));
      else
         heading = 90.;
   }
   else if (y < 0)  {
      if (x > 0)
         heading = -1 * rad2deg (atan ((-1 * y)/x));  // This case is used!
      else if (x < 0)
         heading = rad2deg (atan (y/x)) - 180.;
      else
         heading = 270.;
   }
   else  {
      if (x > 0)
         heading = 0.;
      else
         heading = 180.;
   }
   
   return (heading);
}

As return value, heading is -60 degrees. Just thinking about it, the number doesn't feel right. Should be like 90 to 100 degrees to the west. Greenland is like -60 degrees I suppose.
 
The convention in navigation is 0 degrees is due north, and degrees increase going clockwise (so due east is +90). This convention has opposite sign and a 1/4-circle offset from conventional 2D cartesian coordinates.
 
Yes, when I said "Should be like 90 to 100 degrees to the west" I meant -90 to -100 degrees.

From where I am, Berlin is like at 12 o'clock, Rome is at 6 o'clock, Beijing at 3 o'clock, and Chapel Hill at 9 o'clock, maybe 8:30 or something like that.
 
I typed the compass example from "Sams Teach Yourself iPhone Application Development in 24 Hours" that is supposed to point me to the direction of Chapel Hill, N. Carolina in US. I am in Europe, so I expected it to point to the west, but is pointing somewhere north instead.

Actually, the direction from Europe to North Carolina is more north/northwest. If you look at a map NC appears to be to the west but an ordinary map is not an accurate projection of the earth. If you look at a globe you'll see that the direction is more north/northwest.
 
Thanks,

Just downloaded that Google Earth plug-in and saw it. Looks so obvious now. And explains why Titanic was so up to the north when it hit that ice.

So, second version of that method works well and -60 degrees (or 300 degrees) it returns as general direction from my place to Chapel Hill is valid. Cool.
 
idelovski, where exactly in Europe are you? The latitude of Chapel Hill is 35.913N. Therefore, anything around that latitude in Europe is going to give you a heading of about -90º. But Europe is a large place that stretches from about 36N to 71N. So, pretty much all of Europe lies north of Chapel Hill and thus you should expect a heading to be more southwest than northwest.
 
dejo,

you can see those 36N to 71N (with more precision) in the comments of my second version of -headingToLocation:current: method.

I am at 45North and that is why I expected the direction to be just over -90 degrees, maybe closer to -100, as you say southwest, something like that. But when I looked at Google Earth, it was obvious that the shortest line on Eart's surface would go a bit more to the north.

edit: coordinets from the book are
Lat: 35.91315010
Long: -79.055702
I just typed them as they were presented in the book. I suppose they are correct.
 
Well, author of the book gave that method its name. Me, I would have called it -degreesToLocation:fromCurrentLocation:.
 
Well, english is not my first language, so I can never be 100% sure about these things. I had to look it up in a dictionary just to see all of its meanings. For me, bearing was that thing in cars, trains, bikes, ... related to wheels.

Found this: Direction, especially angular direction measured from one position to another using geographical or celestial reference lines.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.