Compass example from a book

Discussion in 'iOS Programming' started by idelovski, May 11, 2010.

  1. idelovski macrumors regular

    Joined:
    Sep 11, 2008
    #1
    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.
     
  2. neoserver macrumors 6502

    Joined:
    Apr 24, 2003
    #2
    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);
    
     
  3. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #3
    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.
     
  4. idelovski thread starter macrumors regular

    Joined:
    Sep 11, 2008
    #4
    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.
     
  5. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #5
    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.
     
  6. idelovski thread starter macrumors regular

    Joined:
    Sep 11, 2008
    #6
    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.
     
  7. bredell macrumors regular

    Joined:
    Mar 30, 2008
    Location:
    Uppsala, Sweden
    #7
    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.
     
  8. idelovski thread starter macrumors regular

    Joined:
    Sep 11, 2008
    #8
    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.
     
  9. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #9
    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.
     
  10. idelovski thread starter macrumors regular

    Joined:
    Sep 11, 2008
    #10
    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.
     
  11. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #11
    Shortest line to destination != heading.
     
  12. idelovski thread starter macrumors regular

    Joined:
    Sep 11, 2008
    #12
    Well, author of the book gave that method its name. Me, I would have called it -degreesToLocation:fromCurrentLocation:.
     
  13. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #13
    Maybe I am confusing heading with bearing. Sorry. Just ignore me. :D
     
  14. idelovski thread starter macrumors regular

    Joined:
    Sep 11, 2008
    #14
    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.
     

Share This Page