Search code examples
iosmathgpscore-locationdirection

Calculate degrees for position


I am building an application that should show an arrow to a certain location. Forinstance, if I am at position X and the target is set to position Y, it should show an arrow pointing directly to that position.

After searching and searching I found that there are some fancy formulas of calculating this, however I seem to get it wrong over and over.

This is my problem. Although it seems to find the correct initial position, as soon as I turn my device the "arrow" turns in the opposite direction or what it should. So, if I turn my device clockwise, the arrow turns also clockwise... and visa versa.

This is some of my code:

@implementation CompassViewController

BOOL firstPositionFound = NO;
float lastPosition = 0;
float currentHeading = 0;

[...]

#pragma mark - 
#pragma mark Core Location Methods

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    if (newHeading.headingAccuracy > 0) {
        CLLocationDirection theHeading = newHeading.magneticHeading;

        currentHeading = theHeading;

        [self fixPointer:theHeading];
    }
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
    self.currentLocation = newLocation;

    [self fixPointer:currentHeading];
}

- (void)fixPointer:(float)heading
{
    float degree = [self calculateDegree:self.currentLocation];

    degree = heading - degree;

    NSLog(@"heading: %f, Degree: %f", heading, degree);

    NSLog(@"Degree 2: %f", degree);

    self.arrowView.transform = CGAffineTransformMakeRotation(degreesToRadians(degree));
}

#pragma mark -
#pragma mark Delegate methods

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

#pragma mark -
#pragma mark Other methods

- (float)calculateDegree:(CLLocation *)newLocation
{
    CLLocationCoordinate2D from = newLocation.coordinate;
    CLLocationCoordinate2D to;
    to.latitude = APP_DESTINATION_LAT;
    to.longitude = APP_DESTINATION_LON;

    float res = atan2(sin((to.longitude*M_PI/180)-(from.longitude*M_PI/180))*cos(to.latitude*M_PI/180),
                      cos(from.latitude*M_PI/180)*sin(to.latitude*M_PI/180)-sin(from.latitude*M_PI/180)*cos(to.latitude*M_PI/180)*cos((to.longitude*M_PI/180)-(from.longitude*M_PI/180)));

    res = res/M_PI*180;

    if (res < 0)
        res = 360.0f + res;

    return res;
}

#pragma mark -

I am just lost, could someone please point out to me where I went wrong? I guess its some simple thing that I currently am to blind for to see.


Solution

  • Have a look at the answer in this Stack Overflow question:

    CLLocation Category for Calculating Bearing w/ Haversine function

    specifically, this part:

    If you are getting negative bearings, add 2*M_PI to the final result in radiansBearing (or 360 if you do it after converting to degrees). atan2 returns the result in the range -M_PI to M_PI (-180 to 180 degrees), so you might want to convert it to compass bearings, using something like the following code

    if(radiansBearing < 0.0)
        radiansBearing += 2*M_PI;**
    

    Also, heading info needs to be adjusted for device orientation and angle, unless you are always holding your device in portrait. Turn you device slowly to landscape mode and you will see your heading value change by 90°.