6

I am trying to develop a compass for an appliation which has a set of annotations on a map. I would like to be able to choose an annotation and then have the compass point the user to the chosen location.

I have calculated the degress from the user's location to the location of the annotation and I have the magnetic heading and the true heading of the iPhone. Also I know how to rotate an image. But I can't figure out what the next step is.

The degrees between the user's location and the annotation location is calculated like this:

    // get user location
    locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self; 
    locationManager.desiredAccuracy = kCLLocationAccuracyBest; 
    locationManager.distanceFilter = kCLDistanceFilterNone; 
    [locationManager startUpdatingLocation];
    [locationManager startUpdatingHeading];
    CLLocation *location = [locationManager location];
    CLLocationCoordinate2D coordinate = [location coordinate];

    float x1 = coordinate.latitude;
    float y1 = coordinate.longitude;
    float x2 = [annLatitude floatValue];
    float y2 = [annLongitude floatValue];

    float dx = (x2 - x1);
    float dy = (y2 - y1);

    if (dx == 0) {
        if (dy > 0) {
            result = 90;
        }
        else {
            result = 270;
        }
    }
    else {
        result = (atan(dy/dx)) * 180 / M_PI;
    }

    if (dx < 0) {
        result = result + 180;
    }

    if (result < 0) {
        result = result + 360;
    }

The true and the magnetic heading is retrieved like this:

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
    arrow.transform = CGAffineTransformMakeRotation(newHeading.magneticHeading);

    // NSLog(@"magnetic heading: %f", newHeading.magneticHeading);
    // NSLog(@"true heading: %f", newHeading.trueHeading);
}

Can anyone tell me what I should do now to make the arrow point to the location - even if the iPhone is rotated?

simonbs
  • 7,932
  • 13
  • 69
  • 115

1 Answers1

9

I had time to play with this again.

This will do it:

- (void)viewDidLoad {
    [super viewDidLoad];

    locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self; 
    locationManager.desiredAccuracy = kCLLocationAccuracyBest; 
    locationManager.distanceFilter = kCLDistanceFilterNone; 
    [locationManager startUpdatingLocation];
    [locationManager startUpdatingHeading];

    CLLocation *location = [locationManager location];
    CLLocationCoordinate2D user = [location coordinate];

    [self calculateUserAngle:user];
}

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    CLLocationCoordinate2D here =  newLocation.coordinate;

    [self calculateUserAngle:here];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
    compass.transform = CGAffineTransformMakeRotation(newHeading.magneticHeading * M_PI / 180);
    needle.transform = CGAffineTransformMakeRotation((degrees - newHeading.magneticHeading) * M_PI / 180);
}

-(void) calculateUserAngle:(CLLocationCoordinate2D)user {
    locLat = [[targetLocationDictionary objectForKey:@"latitude"] floatValue];
    locLon = [[targetLocationDictionary objectForKey:@"longitude"] floatValue];

    NSLog(@"%f ; %f", locLat, locLon);

    float pLat;
    float pLon;

    if(locLat > user.latitude && locLon > user.longitude) {
        // north east

        pLat = user.latitude;
        pLon = locLon;

        degrees = 0;
    }
    else if(locLat > user.latitude && locLon < user.longitude) {
        // south east

        pLat = locLat;
        pLon = user.longitude;

        degrees = 45;
    }
    else if(locLat < user.latitude && locLon < user.longitude) {
        // south west

        pLat = locLat;
        pLon = user.latitude;

        degrees = 180;
    }
    else if(locLat < user.latitude && locLon > user.longitude) {
        // north west

        pLat = locLat;
        pLon = user.longitude;

        degrees = 225;
    }

    // Vector QP (from user to point)
    float vQPlat = pLat - user.latitude;
    float vQPlon = pLon - user.longitude;

    // Vector QL (from user to location)
    float vQLlat = locLat - user.latitude;
    float vQLlon = locLon - user.longitude;

    // degrees between QP and QL
    float cosDegrees = (vQPlat * vQLlat + vQPlon * vQLlon) / sqrt((vQPlat*vQPlat + vQPlon*vQPlon) * (vQLlat*vQLlat + vQLlon*vQLlon));
    degrees = degrees + acos(cosDegrees);
}
Ondrej Rafaj
  • 4,342
  • 8
  • 42
  • 65
simonbs
  • 7,932
  • 13
  • 69
  • 115
  • this is great .. i'll try it to my project and i hope it works fine .. you saved my day :) – Mohamed Emad Hegab May 06 '11 at 18:24
  • can you send me the complete class for this :) – Mohamed Emad Hegab May 07 '11 at 12:16
  • yes, this information is really helpful.Can you answer my question related to this? - http://stackoverflow.com/questions/7173925/how-to-rotate-a-direction-arrow-using-clheading – Dhaval Panchal Aug 25 '11 at 05:00
  • @lesk do not use code to calculateUserAngle, instead use the code of question. in Question result= degrees and work fine for me – AsifHabib Jun 25 '13 at 14:33
  • I think it is also worth bearing in mind that the UIImage that contains the arrow has to be a vertical arrow i.e. when you add the arrow to the xib the arrow image is pointing straight up to the top of the screen – Guy Mar 05 '16 at 11:38