Lecture #11: Core Location and MapKit

Please note, this blog entry is from a previous course. You might want to check out the current one.

Lecture eleven is named “11. Core Location and MapKit (November 1, 2011)” and can be found at iTunes. Its slides are available at Stanford.

The theoretical part starts with an introduction to Core Location and MapKit.

CoreLocation is a framework managing location and heading and has no user interface. Its basic object is CLLocation and contains properties like coordinate, altitude, horizontal/verticalAccuracy, timestamp, speed, course.

coordinate consists of two doubles with the latitude and longitude in degrees:

@property (readonly) CLLocationCoordinate2D coordinate;
typedef {
    CLLocationDegrees latitude;
    CLLocationDegrees longitude;
} CLLocationCoordinate2D;

The altitude is defined in meters, where negative values mean below sea level.

@property (readonly) CLLocationDistance altitude;

The accuracy of these values is provided via horizontal/verticalAccuracy with values like kCLLocationAccuracyBestForNavigation/Best/NearestTenMeters/HundredMeters/Kilometer/ThreeKilometers or a negative value if the provided position is invalid. The accuracy depends on the actual technology used like GPS, WiFi, or cellular tower triangulation.

The current instantaneous (not average) speed is provided in meters per second:

@property (readonly) CLLocationSpeed speed;

Course provides the current heading in clockwise counted degrees with 0 equals north:

@property (readonly) CLLocationDirection course;

Timestamp provides the actual time of a delivered location as they are not provided on a consistent time bases:

@property (readonly) NSDate *timestamp;

To get a location

  • check if current hardware supports the needed location,
  • create a CLLocationManager instance and set the delegate to receive updates,
  • configure the manager according to the needed location, and
  • start the manager monitoring for location changes.

To check the currently available hardware

+ (BOOL)locationServicesEnabled; 

provides if the user has enabled location monitoring.

+ (BOOL)headingAvailable;

tells if the hardware can provide heading information e.g. like a compass.

+ (BOOL)significantLocationChangeMonitoringAvailable;

tells if the device has cellular capabilities.

+ (BOOL)regionMonitoringAvailable;

tells if region monitoring is supported by the device and

+ (BOOL)regionMonitoringEnabled;

tells if the user allows region monitoring.

When an application tries to use location monitoring the first time, the user will be asked for permission.

@property (copy) NSString *purpose;

can provide a string which describes the application’s purpose in using the location services.

- (void)startUpdatingLocation;
- (void)stopUpdatingLocation;

will start or stop location monitoring.

@property CLLocationAccuracy desiredAccuracy;
@property CLLocationDistance distanceFilter;

define when the delegate should be triggered. Where desiredAccuracy should be defined as low as possible distanceFilter should be set as high as possible to minimize notifications which are sent to the delegate as:

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

Similar functions are available for heading monitoring:

@property CLLocationDegrees headingFilter;
@property CLHeadingOrientation headingOrientation;

- (void)startUpdatingHeading;
- (void)stopUpdatingHeading;

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

Where CLHeading provides:

@property (readonly) CLLocationDirection magneticHeading;
@property (readonly) CLLocationDirection trueHeading;
@property (readonly) CLLocationDirection headingAccuracy;
@property (readonly) NSDate *timestamp;

An user interface for heading calibration is put up automatically by iOS, but can be prevented or dismissed:

- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager;
- (void)dismissHeadingCalibrationDisplay;

Significant location change monitoring

- (void)startMonitoringSignificantLocationChanges;
- (void)stopMonitoringSignificantLocationChanges;

works even when the application is not running. When triggered the application will launch and the delegate will receive a message application:didFinishLaunchingWithOptions: with and additional dictionary containing an UIApplicationLaunchOptionsLocationKey.

Region-based monitoring works also if the application is not running:

- (void)startMonitoringForRegion:(CLRegion *) desiredAccuracy:(CLLocationAccuracy); 
- (void)stopMonitoringForRegion:(CLRegion *);

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region;
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region;
   - (void)locationManager:(CLLocationManager *)manager
monitoringDidFailForRegion:(CLRegion *)region
                 withError:(NSError *)error;

MapKit is a framework to display maps with annotations and additional callouts. It is created with alloc/init in code or dragged out in storyboard. Its annotation are stored in an array:

@property (readonly) NSArray *annotations;

Its objects follow the MKAnnotation protocol:

@protocol MKAnnotation <NSObject>
@property (readonly) CLLocationCoordinate2D coordinate;
@property (readonly) NSString *title;
@property (readonly) NSString *subtitle;

typedef {
    CLLocationDegrees latitude;
    CLLocationDegrees longitude;
} CLLocationCoordinate2D;

Because annotation are read-only they have to be added or removed explicitly:

- (void)addAnnotation:(id <MKAnnotation>)annotation;
- (void)addAnnotations:(NSArray *)annotations;
- (void)removeAnnotation:(id <MKAnnotation>)annotation;
- (void)removeAnnotations:(NSArray *)annotations;

By default an annotation looks like an pin, but can be drawn customized as MKAnnotationViews. If their canShowCallout is set to YES, an additional view opens they are touched. This view contains a title and subtitle by default, and can be enhanced with left and rightCalloutAccessoryViews. In addition the delegate receives a notification:

- (void)mapView:(MKMapView *)sender didSelectAnnotationView:(MKAnnotationView *)aView;

Annotation views are created similar to table cell views

- (MKAnnotationView *)mapView:(MKMapView *)sender
            viewForAnnotation:(id <MKAnnotation>)annotation
    MKAnnotationView *aView = [sender dequeueReusableAnnotationViewWithIdentifier:IDENT]; 
    if (!aView) {
        aView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation
    aView.annotation = annotation;
    return aView;

and provide various properties:

@property id <MKAnnotation> annotation;
@property UIImage *image; // to replace the pin
@property UIView *leftCalloutAccessoryView; // e.g. UIImageView
@property UIView *rightCalloutAccessoryView; // e.g. UIButton 
@property BOOL enabled; 
@property CGPoint centerOffset; // relative to the image 
@property BOOL draggable;

If a callout accessory is set to a UIControl additionally the following MKMapViewDelegate method will get called:

              - (void)mapView:(MKMapView *)sender
               annotationView:(MKAnnotationView *)aView
calloutAccessoryControlTapped:(UIControl *)control;

The map view can be displayed as standard, satellite or hybrid map. Its user interaction can be restricted:

@property BOOL zoomEnabled;
@property BOOL scrollEnabled;

The portion of the map which should be displayed can be set/changed via a region:

@property MKCoordinateRegion region;
typedef struct {
    CLLocationCoordinate2D center;
    MKCoordinateSpan span;
} MKCoordinateRegion;
typedef struct {
    CLLocationDegrees latitudeDelta;
    CLLocationDegrees longitudeDelta;
- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated;

or its center point:

@property CLLocationCoordinate2D centerCoordinate;
- (void)setCenterCoordinate:(CLLocationCoordinate2D)center animated:(BOOL)animated;

Like any other view a map kit view provides notifications about its loading status:

- (void)mapViewWillStartLoadingMap:(MKMapView *)sender;
- (void)mapViewDidFinishLoadingMap:(MKMapView *)sender;
- (void)mapViewDidFailLoadingMap:(MKMapView *)sender withError:(NSError *)error;

Finally overlays which work similar to annotations allow to draw on the map:

- (void)addOverlay:(id <MKOverlay>)overlay;    // also addOverlays:(NSArray *)
- (void)removeOverlay:(id <MKOverlay>)overlay; // also removeOverlays:(NSArray *)

@property (readonly) MKMapRect boundingMapRect;
- (BOOL)intersectsMapRect:(MKMapRect)mapRect;

- (MKOverlayView *)mapView:(MKMapView *)sender
            viewForOverlay:(id <MKOverlay>)overlay;

- (void)drawMapRect:(MKMapRect)mapRect

- (MKMapPoint)mapPointForPoint:(CGPoint)point;
- (MKMapRect)mapRectForRect:(CGRect)rect;
- (CGPoint)pointForMapPoint:(MKMapPoint)mapPoint;
- (CGRect)rectForMapRect:(MKMapRect)mapRect;

The demo of this lecture adds a MapView to the Shutterbug project using annotations for the location of the fetched photos as well as preview images in the annotation views. Its code is available directly at Stanford or at github.


Leave a Reply

Your email address will not be published.