Weather Maps

The AerisMap component of the SDK provides a fully-functional interactive weather map that you can easily drop into your custom project to support displaying any type of weather data, from radar and satellite overlays to storm reports and earthquakes. Additionally, a weather map is highly configurable and customizable, giving you even more control over how it functions.

 
 
 
 
 
 
 
 
 
 
 
 
 
 

Supported Map Types

The AerisMap component of the SDK supports three different mapping libraries:

  • Apple Maps (via MapKit)
  • Mapbox (via Mapbox tiles on top of Apple’s MapKit)
  • Google Maps (via Google Maps iOS SDK)

You must specify which mapping library to use when creating an instance of a weather map, which cannot be changed afterwards. The weather map will automatically handle setting up the actual map view to use along with the required delegates for adding and removing map objects.

The following are the support map types and their enumerated value used within the SDK:

Creating a Weather Map

The most basic method for creating a weather map is to instantiate AWFWeatherMap with the desired map type. If you don’t provide a map type, then the default of AWFWeatherMapTypeApple will be used:

AWFWeatherMap *weatherMap = [[AWFWeatherMap alloc] initWithMapType:AWFWeatherMapTypeApple];
let weatherMap = AWFWeatherMap(mapType: .apple)!

This will also use the default configuration for the weather map. If you want to use your own custom configuration, then you will also need to provide your configuration object. Review the Configuring a Weather Map section below for details regarding customizing a weather map’s options:

AWFWeatherMap *weatherMap = [[AWFWeatherMap alloc] initWithMapType:nil config:[MyWeatherMapConfig config]];
let weatherMap = AWFWeatherMap(mapType: .apple, config: MyWeatherMapConfig())!

Once you have a weather map instance, setup the internal view’s frame, accessible using the weatherMapView property, and add it as a subview to an existing view just as you would MKMapView:

weatherMap.weatherMapView.frame = self.view.bounds;
[self.view addSubview:weatherMap.weatherMapView];
weatherMap.weatherMapView.frame = view.bounds
view.addSubview(weatherMap.weatherMapView)

If your view controller needs to receive event notifications for various events fired by the weather map, such as when data layers are added and removed or animation events, make sure you have assigned the weather map’s delegate and that delegate conforms to the AWFWeatherMapDelegate protocol:

weatherMap.delegate = self;
weatherMap.delegate = self

Creating a Weather Map Using AWFWeatherMapViewController

The easiest method for getting a fully-functional weather map into your application is to create an instance of AWFWeatherMapViewController. This view controller sets up everything you need for an interactive weather map, including:

  • weather map instance
  • timeline/animation control view
  • legend view
  • options menu for toggling data layers on and off
  • handling of all of the various events from the weather map by implementing the AWFWeatherMapDelegate protocol

By default, AWFWeatherMapViewController will use the default map configuration and set its weather map to use AWFWeatherMapTypeApple. You can change these values by setting the properties weatherMapType and config after creating a controller instance:

// MyWeatherMapConfig is a subclass of AWFWeatherMapConfig
MyWeatherMapConfig *mapConfig = [MyWeatherMapConfig config];

AWFWeatherMapViewController *weatherMapController = [[AWFWeatherMapViewController alloc] initWithNibName:nil bundle:nil];
weatherMapController.weatherMapType = AWFWeatherMapTypeApple;
weatherMapController.config = mapConfig;
let mapConfig = MyWeatherMapConfig()
let weatherMapController = AWFWeatherMapViewController()
weatherMapController.weatherMapType = .apple
weatherMapController.config = mapConfig

Alternatively, you can subclass AWFWeatherMapViewController and override the above properties in initWithNibName:bundle::

MyWeatherMapViewController

@implementation MyWeatherMapViewController

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
	self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
	if (self) {
		// MyWeatherMapConfig is a subclass of AWFWeatherMapConfig
		MyWeatherMapConfig *mapConfig = [MyWeatherMapConfig config];

		self.weatherMapType = AWFWeatherMapTypeApple;
		self.config = mapConfig;
		
		self.autorefreshEnabled = YES;
	}
	
	return self;
}

@end
class MyWeatherMapViewController: AWFWeatherMapViewController {
	
	override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
		super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
		
		self.weatherMapType = .apple
		self.config = MyWeatherMapConfig()
		self.isAutorefreshEnabled = true
	}
	
	required init?(coder aDecoder: NSCoder) {
		fatalError("init(coder:) has not been implemented")
	}
}

For implementations that need more advanced customization, you should subclass AWFWeatherMapViewController and override any of the default options and/or methods. Review the API reference for AWFWeatherMapViewController for the complete list of public properties and methods available.

Configuring a Weather Map

A weather map’s configuration provides numerous ways to customize both how your weather map functions and how it looks. Each weather map instance contains a config property, which is an instance of AWFWeatherMapConfig and provides many of the options and style settings to the weather map.

 

To customize your weather map, you can simply override the default values on a weather map’s configuration object once you have created a weather map instance:

weatherMap.config.animationEnabled = NO;
weatherMap.config.refreshInterval = 15 * AWFMinuteInterval;
weatherMap.config.animationEnabled = false
weatherMap.config.refreshInterval = 15 * AWFMinuteInterval

If you need to use the same configuration across multiple weather map instances, you can also subclass AWFWeatherMapConfig and override the same properties within the object’s init method:

MyWeatherMapConfig

@implementation MyWeatherMapConfig

- (instancetype)init {
	self = [super init];
	if (self) {
		self.refreshInterval = 15 * AWFMinuteInterval;
		self.animationEnabled = NO;
	}
	
	return self;
}

@end
class MyWeatherMapConfig: AWFWeatherMapConfig {
	
	override init() {
		super.init()
		
		self.refreshInterval = 15 * AWFMinuteInterval
		self.animationEnabled = false
	}
}

And then you would provide your custom configuration instance when creating a new weather map:

MyWeatherMapConfig *config = [MyWeatherMapConfig config];
AWFWeatherMap *weatherMap = [[AWFWeatherMap alloc] initWithMapType:nil config:config];
let config = MyWeatherMapConfig()
let weatherMap = AWFWeatherMap(mapType: .apple, config: config)!

Note that once a weather map has been instantiated, its configuration object is readonly and cannot be changed. However, you can change properties on this configuration object at any time.

Refer to the AWFWeatherMapConfig API reference for the complete list of properties and methods available.

Styling Map Objects

Annotations and polygons can easily be customized simply by providing your own custom map item style and overriding the default on your map’s configuration. Refer to the Styling Map Objects guide for more information.

Legends


Legends can be displayed using the AWFWeatherMapLegendView class, which will handle rendering the individual legend components for the data layer types that have been added. In most cases, you will only want to add and remove legend items when data layers have been added and removed from the weather map. Therefore, you would need to implement the necessary delegate methods to be notified when data layers are added and removed:

#pragma mark - AWFWeatherMapDelegate

- (void)weatherMap:(AWFWeatherMap *)weatherMap didAddLayerType:(AWFLayerType)layerType {
	[self.legendView addLegendForLayerType:layerType];
}

- (void)weatherMap:(AWFWeatherMap *)weatherMap didRemoveLayerType:(AWFLayerType)layerType {
	[self.legendView removeLegendForLayerType:layerType];
}
extension MyWeatherMapViewController: AWFWeatherMapDelegate {
	
	public func weatherMap(_ weatherMap: AWFWeatherMap!, didAdd layerType: AWFLayerType) {
		self.legendView.addLegend(for: layerType)
	}
	
	public func weatherMap(_ weatherMap: AWFWeatherMap!, didRemove layerType: AWFLayerType) {
		self.legendView.removeLegend(for: layerType)
	}
}

After adding or removing a layer type’s legend to your legend view, you may want to call sizeToFit on the legend view to resize the view’s frame to fit the legends being displayed (specifically the height).

If you are using or subclassing AWFWeatherMapViewController then this legend view will automatically be handled for you. Legends will appear in a drop-down view from the top of your weather map for data layers that have a legend associated with it. Tapping this view or swiping down from the top of your weather map will slide down the legend view, whereas swiping up or tapping anywhere outside the legend view will slide it back up.

Displaying Map Options Using AWFMapOptionsViewController

The AerisMap component of the SDK includes a built-in view controller to manage the toggling of support map data layers as well as weather map options, such as which forecast model is used and the timeline start and end intervals.

If you are using AWFWeatherMapViewController, this options view controller will already be accessible via the Options button in the navigation controller’s navigation bar. Alternatively, you can create your own instance of AWFMapOptionsViewController and present as needed. Both methods allow you to customize the options displayed within the view.

AWFMapOptionsViewController uses a UITableView internally for managing and displaying the various map options in the view. For easy customization, each section displayed is handled by a AWFTableSection instance, which contains an array of AWFTableSectionRow instances for managing the title and value for each option in that section. Sections are already configured and added for you for the three primary map options: AWFMapOptionForecastModel, AWFMapOptionTimelineStart and AWFMapOptionTimelineEnd. Map data layer options are added as sections based on their type, such as tile layers, point data layers and polygon layers. Each of these data layer options are provided as instances of AWFTableLayerTypeRow, which is a subclass of AWFTableSectionRow that is specific to data layers.

You can override the default sections and options in your AWFMapOptionsViewController instance by creating your AWFTableSection instances populated with the required rows (as AWFTableSectionRow or AWFTableLayerTypeRow instances):

NSArray *severeTypes = @[@(AWFLayerTypeAdvisory), @(AWFLayerTypeWarning), @(AWFLayerTypeStormCell), @(AWFLayerTypeStormReport)];
AWFTableSection *severeSection = [controller sectionWithTitle:NSLocalizedString(@"Severe Weather", nil) layerTypes:severeTypes];

NSArray *currentTypes = @[@(AWFLayerTypeTemperatures), @(AWFLayerTypeDewPoint), @(AWFLayerTypeWinds), @(AWFLayerTypeHumidity)];
AWFTableSection *currentsSection = [controller sectionWithTitle:NSLocalizedString(@"Current Weather", nil) layerTypes:currentTypes];

// just use the default timeline start and end interval options
AWFTableSection *startSection = [controller sectionForMapOption:AWFMapOptionTimelineStart];
AWFTableSection *endSection = [controller sectionForMapOption:AWFMapOptionTimelineEnd];
let optionsController = AWFMapOptionsViewController()

let severeTypes: [AWFLayerType] = [.typeAdvisory, .typeWarning, .typeStormCell, .typeStormReport]
let severeSection = optionsController.section(withTitle: "Severe Weather", layerTypes: severeTypes)

let currentTypes: [AWFLayerType] = [.typeTemperature, .typeDewPoint, .typeWind, .typeHumidity]
let currentsSection = optionsController.section(withTitle: "Current Weather", layerTypes: currentTypes)

let startSection = optionsController.section(for: .timelineStart)
let endSection = optionsController.section(for: .timelineEnd)

If you wanted to provide a custom set of start or ending timeline intervals, you can setup an AWFTableSection with those options and title formatting:

AWFTableSection *startSection = [controller sectionWithTitle:NSLocalizedString(@"Timeline (Start)", nil)
				timeIntervals:@[@(0), @(-6), @(-12), @(-24)]
				rowFormatter:^NSString *(NSInteger interval)
				{
					if (interval == 0) {
						return NSLocalizedString(@"Now", nil);
					}
					return [NSString stringWithFormat:NSLocalizedString(@"Past %i Hours", nil), interval * -1];
				}];
let startSection = optionsController.section(withTitle: "Timeline (Start)", timeIntervals: [0, -6, -12, -24]) { (interval) -> String? in
	if interval == 0 {
		return "Now"
	}
	return "Past \(interval * -1) Hours"
}

Then set the sections property on your AWFMapOptionsViewController instance with an array containing the sections you’ve setup:

// you must pass the `AWFWeatherMap` instance this options controller should manage when instantiating your options controller
AWFMapOptionsViewController *optionsController = [[AWFMapOptionsViewController alloc] initWithWeatherMap:self.weatherMap]; 
optionsController.sections = @[severeSection, currentsSection, startSection, endSection];
let optionsController = AWFMapOptionsViewController(weatherMap: weatherMap)!
optionsController.sections = [severeSection, currentsSection, startSection, endSection]

If your options controller has already been loaded and rendered, you may need to force its table view to reload the data after you have changed the sections property:

[optionsController.tableView reloadData];
optionsController.tableView.reloadData()

Last modified: November 02, 2017