14

In my app I have a latitude-longitude of 1 fix location. Now user with iPhone device can move anywhere and even he rotate his device, the arrow (some uiimageview) should point to that fix location so User will get direction to that location every time. I tried as follows.

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {

CLLocationCoordinate2D here =  newLocation.coordinate;
[self calculateUserAngle:here];
}


-(void) calculateUserAngle:(CLLocationCoordinate2D)user {
    NSLog(@"my destination is %f ; %f", fixlocLat, fixlocLon);
    degrees =  degrees + atan2(sin(fixlocLon-user.longitude)*cos(fixlocLat),cos(user.latitude)*sin(fixlocLat) - sin(user.latitude)*cos(fixlocLat)*cos(fixlocLon-user.longitude));
//get angle between user location and fix location
}

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

but the code above will always rotate arrow to approx. 2-3 degree north for every location.

My question looks similar to this Stack Overflow question.but using code from this link I am getting wrong direction. please help me on this.

and also if any idea using accelerometer or gyrodata will be helpful for me.

thanx in advance.

Community
  • 1
  • 1
Dhaval Panchal
  • 2,529
  • 1
  • 25
  • 36
  • How far away are the locations? if very far away, maybe [great circle distance](http://en.wikipedia.org/wiki/Great-circle_distance) calculations are required. – RabinDev Aug 24 '11 at 12:03
  • how we can use great circle distance calculation in xcode(objective c) and distance can be far as approx. 1000km. – Dhaval Panchal Aug 24 '11 at 12:13
  • distance is variable, it will be updated as user moves but range is around 0 to 1000km. – Dhaval Panchal Aug 24 '11 at 12:46
  • I am **not** sure that this is indeed the problem you are encountering, the great circle thing is only one idea for why the angle is not accurate. Apart from this thing, i see you did not implement the device direction. Use "if ([CLLocationManager headingAvailable]) [locationsManager startUpdatingHeading];" to get heading (azimuth) updates from the compass, and implement the delegate method didUpdateHeading, which gives you the latest heading of the device. Use the heading.trueHeading or magnetic to get the direction the device is facing, and use the resulting angle in your calculateUserAngle. – RabinDev Aug 24 '11 at 13:39
  • all is there in the question and startUpdatingHeading,headingAvailable is already written in viewdidload – Dhaval Panchal Aug 24 '11 at 14:11
  • 1
    see a possible answer here: http://stackoverflow.com/questions/7298193/iphone-compass-showing-to-a-specific-location/8066925#8066925 – Flori Nov 09 '11 at 15:07

4 Answers4

14

This works for me: to rotate a Direction Arrow to particular location

-(void)viewDidLoad
{
    [super viewDidLoad]; 

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

}

-(void) calculateUserAngle:(CLLocationCoordinate2D)current {
    double x = 0, y = 0 , deg = 0,delLon = 0;

    delLon = fixLon - current.longitude;    
    y = sin(delLon) * cos(fixLat);
    x = cos(current.latitude) * sin(fixLat) - sin(current.latitude) * cos(fixLat) * cos(delLon);    
    deg = RADIANS_TO_DEGREES(atan2(y, x));    

    if(deg<0){      
        deg = -deg;
    } else {
        deg = 360 - deg;
    }    
    degrees = deg;
}


-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {

     CLLocationCoordinate2D here =  newLocation.coordinate;
     [self calculateUserAngle:here];
}


- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading 
{
    arrow.transform = CGAffineTransformMakeRotation((degrees-newHeading.trueHeading) * M_PI / 180);
}
auco
  • 9,329
  • 4
  • 47
  • 54
Kiran Patel
  • 944
  • 10
  • 16
  • I implemented this recently, but have been having an occasional problem when the arrow points directly in the opposite direction it should be. It's like the arrow is mirrored. Do you have any thoughts of why that would happen? – Chris Feb 04 '15 at 22:25
  • 1
    What is "fixLon" and "fixLat"? not find any definition or declaration in your code – Chirag Shah Apr 06 '16 at 11:35
  • 2
    @chiragshah Those are the coordinates of the Fixed Location you want the arrow to point towards – SteBra Apr 11 '16 at 15:19
10

this code may help you

-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
    // Use the true heading if it is valid. 
    CLLocationDirection direction = newHeading.magneticHeading;
    CGFloat radians = -direction / 180.0 * M_PI;

    self.strAccuracy = [NSString stringWithFormat:@"%.1fmi",newHeading.headingAccuracy];    
    [lblAccuracy setText:self.strAccuracy];

    //Rotate Bearing View
    [self rotateBearingView:bearingView radians:radians];

    //For Rotate Niddle
    CGFloat angle = RadiansToDegrees(radians);  
    [self setLatLonForDistanceAndAngle];
    [self rotateArrowView:arrowView degrees:(angle + fltAngle)];
}

-(void)rotateArrowView:(UIView *)view degrees:(CGFloat)degrees
{
    CGAffineTransform transform = CGAffineTransformMakeRotation(DegreesToRadians(degrees));
    view.transform = transform;
}

-(void)setLatLonForDistanceAndAngle
{
    dblLat1 = DegreesToRadians(appDelegate.dblLatitude);
    dblLon1 = DegreesToRadians(appDelegate.dblLongitude);

    dblLat2 = DegreesToRadians(objClsProductSearch.dblLatitude);
    dblLon2 = DegreesToRadians(objClsProductSearch.dblLongitude);

    fltLat = dblLat2 - dblLat1;
    fltLon = dblLon2 - dblLon1;
}

-(float)getAngleFromLatLon
{
    //Calculate angle between two points taken from http://www.movable-type.co.uk/scripts    /latlong.html
    double y = sin(fltLon) * cos(dblLat2);
    double x = cos(dblLat1) * sin(dblLat2) - sin(dblLat1) * cos(dblLat2) * cos(dblLon2);
    CGFloat angle = RadiansToDegrees(atan2(y, x));
    return angle;
}
wixardy
  • 35
  • 1
  • 5
NIKHIL
  • 2,719
  • 1
  • 26
  • 50
  • thanx for the answer and just 1 query ; what about this method - [self rotateBearingView:bearingView radians:radians];what is in this method? – Dhaval Panchal Oct 04 '11 at 07:33
  • i have just rotated one image of speedometer kind in the direction of product location – NIKHIL Oct 04 '11 at 07:42
  • 4
    where do you use getAngleFromLatLon ?? – FrankTan Aug 05 '12 at 11:58
  • I have four different locations, it does not go into the north east condition – AsifHabib Jun 25 '13 at 10:43
  • Here what is fltAngle,dblLat1,dblLon1,dblLat2,dblLon2,fltLat,fltLon and how do you declare this? also you created getAngleFromLatLon method and you have not called in your code. – user3182143 Dec 22 '16 at 13:42
  • You need to mention everything clearly in your answer.Give detailed answer. – user3182143 Dec 22 '16 at 13:43
  • NIKHIL your answer is very useful for everyone if you answer is very clear(without error). – user3182143 Dec 22 '16 at 14:58
  • I'm using course object of location to show the current location(Car). Now i want to my car to keep pointing to north always like http://stackoverflow.com/questions/43588865/ios-google-maps-keep-current-location-courseangle-always-north-like-google-nav . can u please help me . – siva krishna Apr 24 '17 at 13:14
4
-(void)viewDidLoad
{
  locationManager = [[CLLocationManager alloc] init];
  locationManager.delegate = self;
  locationManager.desiredAccuracy = kCLLocationAccuracyBest;
  locationManager.distanceFilter = kCLDistanceFilterNone;
  [locationManager startUpdatingLocation];
  [locationManager startUpdatingHeading];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
  GeoAngle = [self setLatLonForDistanceAndAngle:newLocation];
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
   CLLocation *location=[locations lastObject];
   GeoAngle = [self setLatLonForDistanceAndAngle:location];
}
-(float)setLatLonForDistanceAndAngle:(CLLocation *)userlocation
{
   float lat1 = DegreesToRadians(userlocation.coordinate.latitude);
   float lon1 = DegreesToRadians(userlocation.coordinate.longitude);

   float lat2 = DegreesToRadians(Destination lattitude);
   float lon2 = DegreesToRadians(Destination Longitude);

   float dLon = lon2 - lon1;

   float y = sin(dLon) * cos(lat2);
   float x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
   float radiansBearing = atan2(y, x);
   if(radiansBearing < 0.0)
   {
      radiansBearing += 2*M_PI;
   }
   return radiansBearing;
}

-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
   float direction = -newHeading.trueHeading;
   arrow.transform=CGAffineTransformMakeRotation((direction* M_PI / 180)+ GeoAngle);
}
user3182143
  • 9,459
  • 3
  • 32
  • 39
ShigaSuresh
  • 1,598
  • 17
  • 19
-1

Try this example. Its in C# Xamarin tho :) "double heading" is the CLHeading.MagneticHeading from the eventobject in CLLocationManager.UpdatedHeading.

void UpdateCompass(Location origin, Location target, double heading)
{
    var angle1 = GetAngleBetweenPoints(origin, target);
    var angle2 = GetAngleFromHeading(heading);
    var radian = Math.PI * (angle1 + angle2) / 180.0;
    CompassArrow.Transform = CGAffineTransform.MakeRotation((nfloat)radian);
}

double GetAngleBetweenPoints(Location origin, Location target)
{
    var n = 270 - (Math.Atan2(origin.Latitude - target.Latitude, origin.Longitude - target.Longitude)) * 180 / Math.PI;
    return n % 360;
}

double GetAngleFromHeading(double heading)
{
    var radians = -heading / 180.0 * Math.PI;
    return radians * (180.0 / Math.PI);
}
Henke
  • 302
  • 3
  • 8