How to rotate a Direction Arrow to particular location
Asked Answered
K

4

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.

Karns answered 24/8, 2011 at 10:25 Comment(6)
How far away are the locations? if very far away, maybe great circle distance calculations are required.Lavadalavage
how we can use great circle distance calculation in xcode(objective c) and distance can be far as approx. 1000km.Karns
distance is variable, it will be updated as user moves but range is around 0 to 1000km.Karns
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.Lavadalavage
all is there in the question and startUpdatingHeading,headingAvailable is already written in viewdidloadKarns
see a possible answer here: #7298693Ragan
N
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;
}
Nganngc answered 3/10, 2011 at 11:13 Comment(8)
thanx for the answer and just 1 query ; what about this method - [self rotateBearingView:bearingView radians:radians];what is in this method?Karns
i have just rotated one image of speedometer kind in the direction of product locationNganngc
where do you use getAngleFromLatLon ??Eyas
I have four different locations, it does not go into the north east conditionSherlynsherm
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.Solus
You need to mention everything clearly in your answer.Give detailed answer.Solus
NIKHIL your answer is very useful for everyone if you answer is very clear(without error).Solus
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 #43589365 . can u please help me .Sentiment
N
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);
}
Nuncio answered 12/10, 2012 at 6:44 Comment(3)
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?Flite
What is "fixLon" and "fixLat"? not find any definition or declaration in your codeKyungkyushu
@chiragshah Those are the coordinates of the Fixed Location you want the arrow to point towardsBohlen
N
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;
}
Nganngc answered 3/10, 2011 at 11:13 Comment(8)
thanx for the answer and just 1 query ; what about this method - [self rotateBearingView:bearingView radians:radians];what is in this method?Karns
i have just rotated one image of speedometer kind in the direction of product locationNganngc
where do you use getAngleFromLatLon ??Eyas
I have four different locations, it does not go into the north east conditionSherlynsherm
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.Solus
You need to mention everything clearly in your answer.Give detailed answer.Solus
NIKHIL your answer is very useful for everyone if you answer is very clear(without error).Solus
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 #43589365 . can u please help me .Sentiment
B
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);
}
Brittaneybrittani answered 25/9, 2014 at 10:36 Comment(4)
Maybe add a description of what it does and where the OP went wrong?Drusi
try this one it will be help for you.Brittaneybrittani
This (with a little bit of a update for iOS 8) worked great for me! Thanks.Oratorio
ShigaSuresh can you explain please what is GeoAngle here?Solus
A
-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);
}
Afforest answered 14/7, 2016 at 19:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.