Method of IOS CoreLocation to realize system self contained positioning

  • 2021-10-27 09:22:23
  • OfStack

Core Location is a framework that provides device location in iOS SDK. You can use three techniques to get the location: GPS, cellular, or WiFi. Among these technologies, GPS is the most accurate. If GPS hardware is available, Core Location will give priority to it. If the device does not have GPS hardware (such as WiFi iPad) or fails to get the current location using GPS, Core Location will settle for next best, choosing cellular or WiFi.

Most of the functionality of the Core Location is provided by the Location Manager (CLLocationManager), which can be used to specify the frequency and precision of location updates, as well as to start and stop receiving these updates.

To use Location Manager, you must first add the framework Core Location to your project, and then import its interface file:


#import <CoreLocation/CoreLocation.h>

And initialize the location manager, specify the update agent, and some update settings, and then update


CLLocationManager *locManager = [[CLLocationManager alloc] init];

locManager.delegate = self;

[locManager startUpdatingLocation]; 

The Location Manager Delegate (CLLocationManagerDelegate) has two location-related methods:


- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations

{ 

 CLLocation *curLocation = [locations lastObject];
  
  if(curLocation.horizontalAccuracy > 0)
  {
    NSLog(@" Current location: %.0f,%.0f +/- %.0f meters",curLocation.coordinate.longitude,
       curLocation.coordinate.latitude,
       curLocation.horizontalAccuracy);
  }
  
  if(curLocation.verticalAccuracy > 0)
  {
    NSLog(@" Current altitude: %.0f +/- %.0f meters",curLocation.altitude,curLocation.verticalAccuracy);
  }

}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{ // This method is called when positioning fails. And because it will be relocated after failure, the update must be stopped at the end  

 if(error.code == kCLErrorLocationUnknown)
  {
    NSLog(@"Currently unable to retrieve location.");
  }
  else if(error.code == kCLErrorNetwork)
  {
    NSLog(@"Network used to retrieve location is unavailable.");
  }
  else if(error.code == kCLErrorDenied)
  {
    NSLog(@"Permission to retrieve location is denied.");
    [manager stopUpdatingLocation];
  }


} 


The first method handles the positioning successfully, and the manager parameter indicates the location manager instance; locations is an array, which is a collection of position changes, and it is stored in the order of time changes. If you want to get the current location of the device, you only need to access the last element of the array. Each object type in the collection is CLLocation, which contains the following properties:

coordinate-coordinates. A structure encapsulating longitude and latitude.

altitude-Altitude. Positive numbers indicate above sea level, while negative numbers indicate below sea level.

horizontalAccuracy-Accuracy of position (radius). Position accuracy is represented by a circle, and the actual position may be anywhere in this circle. This circle is determined by both coordinate (coordinates) and horizontalAccuracy (radius). The larger the value of horizontalAccuracy, the larger the circle is defined, and therefore the lower the position accuracy. If the value of horizontalAccuracy is negative, the value of coordinate is invalid.

verticalAccuracy-Accuracy of altitude. A positive value indicates that the error of altitude is the corresponding number of meters; Negative indicates that the value of altitude (altitude) is invalid.

speed-Speed. This property is calculated by comparing the current position to the previous 1 position, and comparing the time difference and distance between them. Given the frequency of Core Location updates, the value of the speed attribute is not very precise unless the movement speed changes very little.

When the application starts tracking the user's location, it displays a prompt box on the screen whether to allow positioning. If the user disables the location service, iOS does not prevent the application from running, but the location manager generates an error.

The second method handles this positioning failure, and the parameters of this method indicate the cause of the failure. If the user disables application positioning, the error parameter will be kCLErrorDenied; If the position of Core Location cannot be confirmed after effort, the error parameter will be kCLErrorLocationUnknown;; If there is no source to get the location, the error parameter will be kCLErrorNetwork.

Typically, Core Location will continue to try to determine the location after an error occurs, but it will not do so if the user prohibits positioning; In this case, the location manager should be stopped using method stopUpdatingLocation.

The position accuracy can be specified according to the actual situation. For example, there is no need to require an Core Location precision of 10 meters for applications that only need to determine which country the user is in. To specify precision, set the location manager's desiredAccuracy before starting the location update. There are 6 enumeration values representing different precision:


extern const CLLocationAccuracy kCLLocationAccuracyBestForNavigation;

extern const CLLocationAccuracy kCLLocationAccuracyBest;

extern const CLLocationAccuracy kCLLocationAccuracyNearestTenMeters;

extern const CLLocationAccuracy kCLLocationAccuracyHundredMeters;

extern const CLLocationAccuracy kCLLocationAccuracyKilometer;

extern const CLLocationAccuracy kCLLocationAccuracyThreeKilometers; 

After an update is initiated for the Location Manager, the update is continuously passed to the Location Manager delegate until the update is stopped. You cannot directly control the frequency of these updates, but you can use the location manager property distanceFilter for indirect control. Set the property distanceFilter before starting the update, which specifies how many meters the device (horizontal or vertical) moves before sending another update to the delegate. The following code starts the Location Manager with settings suitable for tracking long-distance travelers:


CLLocationManager *locManager = [[CLLocationManager alloc] init];

locManager.delegate = self;

locManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;// The positioning accuracy is within 100 meters 

locManager.distanceFilter = 200;// Move horizontally or vertically 200 Meters call proxy update location 

[locManager startUpdatingLocation];// Start location update  

P. s. The higher the precision required for positioning, the smaller the value of the property distanceFilter, and the greater the power consumption of the application.

The Location Manager has an headingAvailable attribute that indicates whether the device is equipped with a magnetic compass. If the attribute is YES, you can use Core Location to obtain heading (heading) information. Receiving heading updates is very similar to receiving location updates. To begin receiving heading updates, specify the location manager delegate, set the property headingFilter to specify at what frequency (measured in degrees of heading change) updates are to be received, and invoke the method startUpdatingHeading on the location manager:

The Location Manager Delegation Protocol defines a method for receiving heading updates. The protocol has two methods related to heading:


- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager
{
  return YES;
}

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
  
}

The first method specifies whether the location manager displays alignment prompts to the user. This prompt will automatically rotate the device 360 degrees. Because the compass is always self-aligned, this tip is only helpful if the compass reading fluctuates violently. When set to YES, prompts may distract the user or affect the user's current action.

The parameter newHeading of the second method is an CLHeading object. The CLHeading provides heading readings through one set of attributes: magneticHeading and trueHeading. These values are in degrees and are of type CLLocationDirection, that is, double-precision floating-point numbers. This means:

If the heading is 0.0, the heading is north;

If the heading is 90.0, the heading is east;

If the heading is 180.0, the heading is south;

If heading is 270.0, the heading is west.

The CLHeading object also contains attributes headingAccuracy (precision), timestamp (measurement time of readings), and description (a description that is more suitable for writing to a log than displaying to the user). The following demonstrates how to handle heading updates using this method:


- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
  if(newHeading.headingAccuracy >=0)
  {
    NSString *headingDesc = [NSString stringWithFormat:@"%.0f degrees (true), %.0f degrees (magnetic)",newHeading.trueHeading,newHeading.magneticHeading];
    
    NSLog(@"%@",headingDesc);
  }
}

trueHeading and magneticHeading represent real heading and magnetic heading, respectively. If the location service is turned off, GPS and wifi can only get magneticHeading (magnetic field heading). trueHeading (True Heading) can only be obtained if the location service is turned on.

The following code demonstrates the distance between the current position and the location and the correct course when there is a location with determined latitude and longitude:


#import "ViewController.h"

#define kDestLongitude 113.12 // Precision 
#define kDestLatitude 22.23 // Latitude 
#define kRad2Deg 57.2957795 // 180/ π 
#define kDeg2Rad 0.0174532925 //  π /180

@interface ViewController ()
@property (strong, nonatomic) IBOutlet UILabel *lblMessage;
@property (strong, nonatomic) IBOutlet UIImageView *imgView;
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) CLLocation *recentLocation;

-(double)headingToLocation:(CLLocationCoordinate2D)desired current:(CLLocationCoordinate2D)current;
@end

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad];

  self.locationManager = [[CLLocationManager alloc] init];
  self.locationManager.delegate = self;
  self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
  self.locationManager.distanceFilter = 1609; //1 Miles 1609 Rice 
  [self.locationManager startUpdatingLocation];
  
  if([CLLocationManager headingAvailable])
  {
    self.locationManager.headingFilter = 10; //10 ° 
    [self.locationManager startUpdatingHeading];
  }
}

/*
 * According to Movable Type Scripts
 * http://mathforum.org/library/drmath/view/55417.html
 *
 * Javascript:
 *
 * var y = Math.sin(dLon) * Math.cos(lat2);
 * var x = Math.cos(lat1)*Math.sin(lat2) -
 * Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
 * var brng = Math.atan2(y, x).toDeg();
 */
-(double)headingToLocation:(CLLocationCoordinate2D)desired current:(CLLocationCoordinate2D)current
{
  double lat1 = current.latitude*kDeg2Rad;
  double lat2 = desired.latitude*kDeg2Rad;
  double lon1 = current.longitude;
  double lon2 = desired.longitude;
  double dlon = (lon2-lon1)*kDeg2Rad;
  
  double y = sin(dlon)*cos(lat2);
  double x = cos(lat1)*sin(lat2) - sin(lat1)*cos(lat2)*cos(dlon);
  
  double heading=atan2(y,x);
  heading=heading*kRad2Deg;
  heading=heading+360.0;
  heading=fmod(heading,360.0);
  return heading;
}

// Handling course  
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
  if(self.recentLocation!=nil && newHeading.headingAccuracy>=0)
  {
    CLLocation *destLocation = [[CLLocation alloc] initWithLatitude:kDestLatitude longitude:kDestLongitude];
    
    double course = [self headingToLocation:destLocation.coordinate current:self.recentLocation.coordinate];
    
    double delta = newHeading.trueHeading - course;
    
    if (abs(delta) <= 10)
    {
      self.imgView.image = [UIImage imageNamed:@"up_arrow.png"];
    }
    else
    {
      if (delta > 180)
      {
        self.imgView.image = [UIImage imageNamed:@"right_arrow.png"];
      }
      else if (delta > 0)
      {
        self.imgView.image = [UIImage imageNamed:@"left_arrow.png"];
      }
      else if (delta > -180)
      {
        self.imgView.image = [UIImage imageNamed:@"right_arrow.png"];
      }
      else
      {
        self.imgView.image = [UIImage imageNamed:@"left_arrow.png"];
      }
    }
    self.imgView.hidden = NO;
  }
  else
  {
    self.imgView.hidden = YES;
  }
}

// Process positioning successful 
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
  CLLocation *curLocation = [locations lastObject];
  
  if(curLocation.horizontalAccuracy >= 0)
  {
    self.recentLocation = curLocation;
    
    CLLocation *destLocation = [[CLLocation alloc] initWithLatitude:kDestLatitude longitude:kDestLongitude];
    
    CLLocationDistance distance = [destLocation distanceFromLocation:curLocation];
    
    if(distance<500)
    {
      [self.locationManager stopUpdatingLocation];
      [self.locationManager stopUpdatingHeading];
      self.lblMessage.text = @" You have arrived at your destination !";
    }
    else
    {
      self.lblMessage.text = [NSString stringWithFormat:@" There is still a distance from the destination %f Rice ",distance];
    }
  }
}

// Failed to process positioning 
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
  if(error.code == kCLErrorLocationUnknown)
  {
    NSLog(@"Currently unable to retrieve location.");
  }
  else if(error.code == kCLErrorNetwork)
  {
    NSLog(@"Network used to retrieve location is unavailable.");
  }
  else if(error.code == kCLErrorDenied)
  {
    NSLog(@"Permission to retrieve location is denied.");
    [self.locationManager stopUpdatingLocation];
    self.locationManager = nil;
  }
}


- (void)didReceiveMemoryWarning
{
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

@end

Related articles: