Grouped Annotation Style

Grouped styles are used for most point and polygon data sources for a weather map. A grouped style categorizes model objects and their annotations based on varying criteria as defined by a series of model evaluator blocks. Then, a separate AWFMapItemStyle subclass is setup for each category in the group.

These grouped styles are already set up for you by default. However, you can override the styles for one or all categories in a group.

For example, the default style for earthquake point data on a weather map is a grouped style consisting of separate styles for each magnitude category. So first, the individual styles are created for each earthquake magnitude category (identified by AWFEarthquakeAnnotationType):

ObjectiveC
Swift
NSMutableDictionary *mutableStyles = [NSMutableDictionary dictionary];

// AWFEarthquakeAnnotationStyle conforms to AWFGroupedStyle and contains an `identifiers` method that returns an array
// of category identifiers for the group that we use to iterate over.
[[AWFEarthquakeAnnotationStyle identifiers] enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
    CGFloat radius = 8.0;
    UIColor *fillColor = [UIColor colorWithRed:0.436 green:0.702 blue:0.079 alpha:1.000];
    NSString *label = nil;
    
    if ([key isEqualToString:AWFEarthquakeAnnotationTypeCatastrophic]) {
        radius = 23.0f;
        fillColor = [UIColor colorWithRed:0.962 green:0.000 blue:1.000 alpha:1.000];
        label = AWFLocalizedString(@"Catastrophic", nil);
    } else if ([key isEqualToString:AWFEarthquakeAnnotationTypeGreat]) {
        radius = 18.0f;
        fillColor = [UIColor colorWithRed:0.725 green:0.002 blue:0.522 alpha:1.000];
        label = AWFLocalizedString(@"Great", nil);
    } else if ([key isEqualToString:AWFEarthquakeAnnotationTypeMajor]) {
        radius = 15.0f;
        fillColor = [UIColor colorWithRed:0.809 green:0.000 blue:0.323 alpha:1.000];
        label = AWFLocalizedString(@"Major", nil);
    } else if ([key isEqualToString:AWFEarthquakeAnnotationTypeStrong]) {
        radius = 13.0f;
        fillColor = [UIColor colorWithRed:0.914 green:0.000 blue:0.020 alpha:1.000];
        label = AWFLocalizedString(@"Strong", nil);
    } else if ([key isEqualToString:AWFEarthquakeAnnotationTypeModerate]) {
        radius = 11.0f;
        fillColor = [UIColor colorWithRed:1.000 green:0.365 blue:0.000 alpha:1.000];
        label = AWFLocalizedString(@"Moderate", nil);
    } else if ([key isEqualToString:AWFEarthquakeAnnotationTypeLight]) {
        radius = 10.0f;
        fillColor = [UIColor colorWithRed:0.806 green:0.561 blue:0.000 alpha:1.000];
        label = AWFLocalizedString(@"Light", nil);
    } else if ([key isEqualToString:AWFEarthquakeAnnotationTypeMinor]) {
        radius = 9.0f;
        fillColor = [UIColor colorWithRed:0.876 green:0.798 blue:0.000 alpha:1.000];
        label = AWFLocalizedString(@"Minor", nil);
    } else {
        label = AWFLocalizedString(@"Mini", nil);
    }
    
    AWFEarthquakeAnnotationStyle *style = [[self class] styleWithRadius:radius fillColor:fillColor strokeColor:[UIColor whiteColor] strokeWidth:2.0];
    style.identifier = key;
    style.label = label;
    
    mutableStyles[key] = style;
}];
var styles = [String: AWFAnnotationStyle]()

// AWFEarthquakeAnnotationStyle conforms to AWFGroupedStyle and contains an `identifiers` method that returns an array
// of category identifiers for the group that we use to iterate over.
AWFEarthquakeAnnotationStyle.identifiers().forEach { (identifier) in
    var radius: CGFloat = 8.0
    var fillColor = UIColor(red: 0.436, green: 0.702, blue: 0.079, alpha: 1.0)
    var label: String?
    
    switch identifier {
    case AWFEarthquakeAnnotationType.catastrophic.rawValue:
        radius = 23.0
        fillColor = UIColor(red: 0.962, green: 0, blue: 1.0, alpha: 1.0)
        label = "Catastrophic"
    case AWFEarthquakeAnnotationType.great.rawValue:
        radius = 18.0
        fillColor = UIColor(red: 0.725, green: 0.002, blue: 0.522, alpha: 1.0)
        label = "Great"
    case AWFEarthquakeAnnotationType.major.rawValue:
        radius = 15.0
        fillColor = UIColor(red: 0.809, green: 0, blue: 0.323, alpha: 1.0)
        label = "Major"
    case AWFEarthquakeAnnotationType.strong.rawValue:
        radius = 13.0
        fillColor = UIColor(red: 0.914, green: 0, blue: 0.020, alpha: 1.0)
        label = "Strong"
    case AWFEarthquakeAnnotationType.moderate.rawValue:
        radius = 11.0
        fillColor = UIColor(red: 1.0, green: 0.365, blue: 0, alpha: 1.0)
        label = "Moderate"
    case AWFEarthquakeAnnotationType.light.rawValue:
        radius = 10.0
        fillColor = UIColor(red: 0.806, green: 0.561, blue: 0, alpha: 1.0)
        label = "Light"
    case AWFEarthquakeAnnotationType.minor.rawValue:
        radius = 9.0
        fillColor = UIColor(red: 0.876, green: 0.798, blue: 0, alpha: 1.0)
        label = "Minor"
    default:
        label = "Mini"
    }
    
    let style = AWFEarthquakeAnnotationStyle(radius: radius, fill: fillColor, stroke: .white, strokeWidth: 2.0)
    style.identifier = identifier
    style.label = label
    
    styles[identifier] = style
}

Then we need to create a grouped style instance that manages these styles, passing in the style and model object types to the class declaration:

ObjectiveC
Swift
AWFGroupedStyle<AWFAnnotationStyle *, AWFEarthquake *> *style = [[AWFGroupedStyle alloc] initWithStyles:mutableStyles];
let style = AWFGroupedStyle<AWFAnnotationStyle, AWFEarthquake>(styles: styles)

And in order to assign the proper style to each annotation for the data set, we need to setup a series of model evaluator blocks that will return a Boolean value based on whether or not the model belongs to the category:

ObjectiveC
Swift
NSDictionary *evaluators = @{
    AWFEarthquakeAnnotationTypeMini: ^(AWFEarthquake *quake){
        return (quake.magnitude < 3.0);
    },
    AWFEarthquakeAnnotationTypeMinor: ^(AWFEarthquake *quake){
        return (quake.magnitude >= 3.0 && quake.magnitude < 4.0);
    },
    AWFEarthquakeAnnotationTypeLight: ^(AWFEarthquake *quake){
        return (quake.magnitude >= 4.0 && quake.magnitude < 5.0);
    },
    AWFEarthquakeAnnotationTypeModerate: ^(AWFEarthquake *quake){
        return (quake.magnitude >= 5.0 && quake.magnitude < 6.0);
    },
    AWFEarthquakeAnnotationTypeStrong: ^(AWFEarthquake *quake){
        return (quake.magnitude >= 6.0 && quake.magnitude < 7.0);
    },
    AWFEarthquakeAnnotationTypeMajor: ^(AWFEarthquake *quake){
        return (quake.magnitude >= 7.0 && quake.magnitude < 8.0);
    },
    AWFEarthquakeAnnotationTypeGreat: ^(AWFEarthquake *quake){
        return (quake.magnitude >= 8.0 && quake.magnitude < 9.0);
    },
    AWFEarthquakeAnnotationTypeCatastrophic: ^(AWFEarthquake *quake){
        return (quake.magnitude >= 9.0);
    }};
    
[style setModelEvaluatorBlocks:evaluators];
let evaluators: [String: (AWFEarthquake) -> Bool] = [
    AWFEarthquakeAnnotationType.mini.rawValue: { (quake) in
        return quake.magnitude < 3.0
    },
    AWFEarthquakeAnnotationType.minor.rawValue: { (quake) in
        return quake.magnitude >= 3.0 && quake.magnitude < 4.0
    },
    AWFEarthquakeAnnotationType.light.rawValue: { (quake) in
        return quake.magnitude >= 4.0 && quake.magnitude < 5.0
    },
    AWFEarthquakeAnnotationType.moderate.rawValue: { (quake) in
        return quake.magnitude >= 5.0 && quake.magnitude < 6.0
    },
    AWFEarthquakeAnnotationType.strong.rawValue: { (quake) in
        return quake.magnitude >= 6.0 && quake.magnitude < 7.0
    },
    AWFEarthquakeAnnotationType.major.rawValue: { (quake) in
        return quake.magnitude >= 7.0 && quake.magnitude < 8.0
    },
    AWFEarthquakeAnnotationType.great.rawValue: { (quake) in
        return quake.magnitude >= 8.0 && quake.magnitude < 9.0
    },
    AWFEarthquakeAnnotationType.catastrophic.rawValue: { (quake) in
        return quake.magnitude >= 9.0
    }
]
    
style.setModelEvaluatorBlocks(evaluators)

Now that your grouped style is setup, you just need to associate it with the earthquake point layer type on your weather map:

ObjectiveC
Swift
[self.weatherMap.style setStyle:style forLayerCode:AWFPointLayerEarthquakes];
weatherMap.style.setStyle(style, forLayerType: .earthquakes)