Hertzelle

Passionate Coder. Pinball Wizard. Friend.

Twitter | LinkedIn | Vigilante Studios

Apple's KMLParser example to take in a standard KML mapping field, which usually contains things like overlays, boundaries of regions and annotations for a map. MapKit works wonderfully for handling these objects, overlays and annotations after they are parsed from the KML file. Parsing of the KML and building reference to the overlays/annotations on our MKMapView  :

[self.generalMap setDelegate:self];
// Locate and Parse our KML File
NSString *path = [[NSBundle mainBundle] pathForResource:@“regions“ ofType:@“kml“];
NSURL *url = [NSURL fileURLWithPath:path];
self.kmlParser = [[KMLParser alloc] initWithURL:url];
[self.kmlParser parseKML];

// Add all of the MKOverlay objects onto the MKMapView
NSArray *overlays = [self.kmlParser overlays];
[self.generalMap addOverlays:overlays];

// Add all of the MKAnnotation objects onto the MKMapView
NSArray *annotations = [self.kmlParser points];
[self.generalMap addAnnotations:annotations];

// Loop through the list of annotations and overlay objects and add them to our MapRect object.
MKMapRect mapRect = MKMapRectNull;
for (id  overlay in overlays) {
    if (MKMapRectIsNull(mapRect)) {
        mapRect = [overlay boundingMapRect];
    } else {
        mapRect = MKMapRectUnion(mapRect, [overlay boundingMapRect]);
    }
}

for (id  annotation in annotations) {
    MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
    MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0);
    if (MKMapRectIsNull(mapRect)) {
        mapRect = pointRect;
    } else {
        mapRect = MKMapRectUnion(mapRect, pointRect);
    }
}

// Position the map centering around our visible MapRect
self.generalMap.visibleMapRect = mapRect;
Necessary code for rendering the overlay on your mapView, remember to set your IBOutlet delegate to self, as shown above, so these methods are called:

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id )overlay
{
    return [self.kmlParser viewForOverlay:overlay];
}

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation
{
    return [self.kmlParser viewForAnnotation:annotation];
}

- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay
{
    //This sets some of our colors and our store color around each region.
    if (![overlay isKindOfClass:[MKPolygon class]]) {
        return nil;
    }
    MKPolygon *polygon = (MKPolygon *)overlay;
    MKPolygonRenderer *renderer = [[MKPolygonRenderer alloc] initWithPolygon:polygon];
    renderer.fillColor = [[self generateRandomUIColor] colorWithAlphaComponent:0.2];
    renderer.strokeColor = [[UIColor grayColor] colorWithAlphaComponent:0.9];
    return renderer;
}
Finally, one more little gem, testing to see of our CLLocation, that is returned from our CLLocationManager falls within a region that we've mapped onto our MapKit view:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    CLLocation *location = [locations lastObject];
    CLLocationCoordinate2D coordinates = location.coordinate;
    MKMapPoint mapPointToTest = MKMapPointForCoordinate(coordinates);
    
    BOOL foundRegion = NO;
    
    //Loop through MKPolygon overlays...
    for (id overlay in self.generalMap.overlays)
    {
        if ([overlay isKindOfClass:[MKPolygon class]])
        {
            MKPolygon *polygon = (MKPolygon *)overlay;
            
            
            CGMutablePathRef pathref = CGPathCreateMutable();
            
            MKMapPoint *polygonPoints = polygon.points;
            
            for (int p=0; p < polygon.pointCount; p++)
            {
                MKMapPoint mp = polygonPoints[p];
                if (p == 0)
                    CGPathMoveToPoint(pathref, NULL, mp.x, mp.y);
                else
                    CGPathAddLineToPoint(pathref, NULL, mp.x, mp.y);
            }
            
            CGPoint mapPointAsCGP = CGPointMake(mapPointToTest.x, mapPointToTest.y);
            
            BOOL pointIsInPolygon = CGPathContainsPoint(pathref, NULL, mapPointAsCGP, FALSE);
            
            if ( pointIsInPolygon ) {
                [self.currentRegionLabel setTitle:polygon.title forState:UIControlStateNormal];
                [self.currentRegionLabel setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
                foundRegion = YES;
            }
            
            CGPathRelease(pathref);
        }
    }
    
    if ( !foundDistrict ) {
        [self.currentRegionLabel setTitle:@"Out of Boundary" forState:UIControlStateNormal];
        [self.currentRegionLabel setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    }
}
Of course you'll need to setup your didChangeAuthorizationStatus delegate methods and check for authorization for current version of iOS, but now you can map your regions to a MapKit view and check whether or not the current users location falls within on of the regions. It's surprisingly accurate and very awesome!">

Parsing KML and Mapping Regions in Objective-C

This brief example uses Apple's KMLParser example to take in a standard KML mapping field, which usually contains things like overlays, boundaries of regions and annotations for a map. MapKit works wonderfully for handling these objects, overlays and annotations after they are parsed from the KML file. Parsing of the KML and building reference to the overlays/annotations on our MKMapView  :


[self.generalMap setDelegate:self];
// Locate and Parse our KML File
NSString *path = [[NSBundle mainBundle] pathForResource:@“regions“ ofType:@“kml“];
NSURL *url = [NSURL fileURLWithPath:path];
self.kmlParser = [[KMLParser alloc] initWithURL:url];
[self.kmlParser parseKML];

// Add all of the MKOverlay objects onto the MKMapView
NSArray *overlays = [self.kmlParser overlays];
[self.generalMap addOverlays:overlays];

// Add all of the MKAnnotation objects onto the MKMapView
NSArray *annotations = [self.kmlParser points];
[self.generalMap addAnnotations:annotations];

// Loop through the list of annotations and overlay objects and add them to our MapRect object.
MKMapRect mapRect = MKMapRectNull;
for (id  overlay in overlays) {
    if (MKMapRectIsNull(mapRect)) {
        mapRect = [overlay boundingMapRect];
    } else {
        mapRect = MKMapRectUnion(mapRect, [overlay boundingMapRect]);
    }
}

for (id  annotation in annotations) {
    MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
    MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0);
    if (MKMapRectIsNull(mapRect)) {
        mapRect = pointRect;
    } else {
        mapRect = MKMapRectUnion(mapRect, pointRect);
    }
}

// Position the map centering around our visible MapRect
self.generalMap.visibleMapRect = mapRect;
Necessary code for rendering the overlay on your mapView, remember to set your IBOutlet delegate to self, as shown above, so these methods are called:

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id )overlay
{
    return [self.kmlParser viewForOverlay:overlay];
}

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation
{
    return [self.kmlParser viewForAnnotation:annotation];
}

- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay
{
    //This sets some of our colors and our store color around each region.
    if (![overlay isKindOfClass:[MKPolygon class]]) {
        return nil;
    }
    MKPolygon *polygon = (MKPolygon *)overlay;
    MKPolygonRenderer *renderer = [[MKPolygonRenderer alloc] initWithPolygon:polygon];
    renderer.fillColor = [[self generateRandomUIColor] colorWithAlphaComponent:0.2];
    renderer.strokeColor = [[UIColor grayColor] colorWithAlphaComponent:0.9];
    return renderer;
}
Finally, one more little gem, testing to see of our CLLocation, that is returned from our CLLocationManager falls within a region that we've mapped onto our MapKit view:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    CLLocation *location = [locations lastObject];
    CLLocationCoordinate2D coordinates = location.coordinate;
    MKMapPoint mapPointToTest = MKMapPointForCoordinate(coordinates);
    
    BOOL foundRegion = NO;
    
    //Loop through MKPolygon overlays...
    for (id overlay in self.generalMap.overlays)
    {
        if ([overlay isKindOfClass:[MKPolygon class]])
        {
            MKPolygon *polygon = (MKPolygon *)overlay;
            
            
            CGMutablePathRef pathref = CGPathCreateMutable();
            
            MKMapPoint *polygonPoints = polygon.points;
            
            for (int p=0; p < polygon.pointCount; p++)
            {
                MKMapPoint mp = polygonPoints[p];
                if (p == 0)
                    CGPathMoveToPoint(pathref, NULL, mp.x, mp.y);
                else
                    CGPathAddLineToPoint(pathref, NULL, mp.x, mp.y);
            }
            
            CGPoint mapPointAsCGP = CGPointMake(mapPointToTest.x, mapPointToTest.y);
            
            BOOL pointIsInPolygon = CGPathContainsPoint(pathref, NULL, mapPointAsCGP, FALSE);
            
            if ( pointIsInPolygon ) {
                [self.currentRegionLabel setTitle:polygon.title forState:UIControlStateNormal];
                [self.currentRegionLabel setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
                foundRegion = YES;
            }
            
            CGPathRelease(pathref);
        }
    }
    
    if ( !foundDistrict ) {
        [self.currentRegionLabel setTitle:@"Out of Boundary" forState:UIControlStateNormal];
        [self.currentRegionLabel setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    }
}
Of course you'll need to setup your didChangeAuthorizationStatus delegate methods and check for authorization for current version of iOS, but now you can map your regions to a MapKit view and check whether or not the current users location falls within on of the regions. It's surprisingly accurate and very awesome!

Education is Important. Sharing Knowledge is Critical.

For a long time now, I've wanted to share various bits of information that I've ran into as a professional software developer. This is the beginning of that adventure. Topics will mainly cover iOS, Swift and Objective-C. For now, here is a little Sound Singleton for playing a quick sound in Objective-C. This is a good class for playing music or playing back a simple sound.


  Sound.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@interface Sounds : NSObject

@property (nonatomic) int numberOfLoops;

+ (id)sharedManager;
- (void)playSoundFromString:(NSString *)soundName;
- (void)stopSound;

@end
Sound.m
#import “Sounds.h“
@interface Sounds ()

@property (strong, nonatomic) AVAudioPlayer *audioPlayer;

@end

@implementation Sounds

+ (id)sharedManager {
    static Sounds *soundsManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        soundsManager = [[self alloc] init];
    });
    return soundsManager;
}

- (id)init {
    self = [super init];
    self.numberOfLoops = 0;
    return self;
}

- (void)playSoundFromString:(NSString *)soundName {
    
    NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] 
                                          pathForResource:soundName 
                                                   ofType:@“mp3“]];
    self.audioPlayer = [[AVAudioPlayer alloc] 
                         initWithContentsOfURL:url 
                                         error:nil];
    self.audioPlayer.volume = 1.0f;
    self.audioPlayer.numberOfLoops = self.numberOfLoops;
    [self.audioPlayer play];
}

-(void)stopSound {
    [self.audioPlayer stop];
}

@end