Setting Up a Weather Map

While the AWFWeatherMapViewController class handles all of the setup necessary to get a fully-functional weather map up and running without any work, there may be times when you want more control over your own weather map.

This example includes the various phases of setting up a weather map in much of the same way as AWFWeatherMapViewController. Depending on your own implementation, you may choose to only use parts of this setup.

The primary steps include:

  1. Ensure you’ve setup the SDK with your Aeris account access keys.
  2. Configure and create your AWFWeatherMap instance and assign its delegate.
  3. If desired, setup the AWFTimelineView (or alternative custom view) which will be used to show animation progress and allow the user to control playback of the animation. You must also set the timeline view’s delegate to receive time change events from the view.
  4. Create a toggleAnimation method that will handle starting or stopping the animation based on the animation’s current state. Set the timeline view’s playback control action to call this method when tapped.
  5. Setup the appropriate AWFTimelineViewDelegate methods to respond to time changes from the timeline view and update the weather map accordingly.
  6. Setup the appropriate AWFWeatherMapDelegate methods to respond to animation state changes from the weather map and update the timeline view accordingly.
ObjectiveC
Swift
#import <UIKit/UIKit.h>
#import <AerisMapKit/AerisMapKit.h>

@interface MapViewController : UIViewController

@property (nonatomic, readonly) AWFWeatherMap *weatherMap;
@property (nonatomic, readonly) AWFTimelineView *timelineView;

@end
#import "MapViewController.h"

@interface MapViewController () <AWFWeatherMapDelegate, AWFTimelineViewDelegate>
@property (nonatomic, strong) AWFWeatherMap *weatherMap;
@property (nonatomic, strong) AWFTimelineView *timelineView;
@end

@implementation MapViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // setup the weather map config
    AWFWeatherMapConfig *config = [AWFWeatherMapConfig config];
    // make any changes to your configuration before creating the weather map...
    
    // create the weather map instance
    AWFWeatherMap *weatherMap = [[AWFWeatherMap alloc] initWithMapType:AWFWeatherMapTypeApple config:config];
    weatherMap.delegate = self;
    [self.view addSubview:weatherMap.weatherMapView];
    self.weatherMap = weatherMap;
    
    // setup the timeline view that will be used to control the weather map's animation
    AWFTimelineView *timelineView = [[AWFTimelineView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 50.0)];
    timelineView.delegate = self;
    timelineView.startDate = weatherMap.timeline.fromTime;
    timelineView.endDate = weatherMap.timeline.toTime;
    timelineView.currentTime = weatherMap.timeline.fromTime;
    [self.view addSubview:timelineView];
    self.timelineView = timelineView;
    
    // layout the map view container
    weatherMap.weatherMapView.translatesAutoresizingMaskIntoConstraints = NO;
    [NSLayoutConstraint activateConstraints:@[[weatherMap.weatherMapView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
                                                [weatherMap.weatherMapView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
                                                [weatherMap.weatherMapView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
                                                [weatherMap.weatherMapView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]]];
    
    // layout the timeline view
    CGFloat timelineHeight = 50.0;
    timelineView.translatesAutoresizingMaskIntoConstraints = NO;
    timelineView.preservesSuperviewLayoutMargins = YES;
    [NSLayoutConstraint activateConstraints:@[[timelineView.topAnchor constraintEqualToAnchor:self.view.layoutMarginsGuide.bottomAnchor constant:-timelineHeight],
                                                [timelineView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
                                                [timelineView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
                                                [timelineView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]]];
    
    // add action handler for timeline's playback control
    [self.timelineView.playButton addTarget:self action:@selector(toggleAnimation:) forControlEvents:UIControlEventTouchUpInside];
    
    // start the weather map at the current time
    [self.weatherMap goToTime:[NSDate date]];
}

#pragma mark - Control Handlers

- (void)toggleAnimation:(id)target {
    UIButton *btn = (UIButton *)target;
    if (self.weatherMap.isAnimating || self.weatherMap.isLoadingAnimation) {
        btn.selected = NO;
        [self.weatherMap stopAnimating];
    } else {
        btn.selected = YES;
        [self.weatherMap startAnimating];
    }
}

#pragma mark - AWFTimelineViewDelegate

// Observe change events on the timelineView so the weather map can be updated accordingly, such as when the user pans
// the timeline or taps the "Now" control to go to the current time/date.

- (void)timelineView:(AWFTimelineView *)timelineView didPanToDate:(NSDate *)date {
    if (self.weatherMap.config.timelineScrubbingEnabled) {
        [self.weatherMap pauseAnimation];
        [self.weatherMap goToTime:date];
    }
}

- (void)timelineView:(AWFTimelineView *)timelineView didSelectDate:(NSDate *)date {
    [self.weatherMap stopAnimating];
    [self.weatherMap goToTime:date];
}

#pragma mark - AWFWeatherMapDelegate

// Updates to the timelineView are handled by handling several AWFWeatherMapDelegate methods that notify our controller about changes to the weather map's
// timeline, such as start/end date changes and when the map begins or stops animating.

- (void)weatherMap:(AWFWeatherMap *)weatherMap didUpdateTimelineRangeFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate {
    self.timelineView.startDate = fromDate;
    self.timelineView.endDate = toDate;
    self.timelineView.currentTime = self.weatherMap.timeline.currentTime;
}

- (void)weatherMapDidStartAnimating:(AWFWeatherMap *)weatherMap {
    self.timelineView.playButton.selected = YES;
}

- (void)weatherMapDidStopAnimating:(AWFWeatherMap *)weatherMap {
    self.timelineView.playButton.selected = NO;
}

- (void)weatherMapDidResetAnimation:(AWFWeatherMap *)weatherMap {
    [self.timelineView setProgress:0.0 animated:YES];
}

- (void)weatherMap:(AWFWeatherMap *)weatherMap animationDidUpdateToDate:(NSDate *)date {
    self.timelineView.currentTime = date;
}

- (void)weatherMapDidStartLoadingAnimationData:(AWFWeatherMap *)weatherMap {
    self.timelineView.playButton.selected = YES;
    [self.timelineView showLoading:YES];
}

- (void)weatherMapDidFinishLoadingAnimationData:(AWFWeatherMap *)weatherMap {
    [self.timelineView setProgress:1.0 animated:YES];
    [self.timelineView showLoading:NO];
}

- (void)weatherMapDidCancelLoadingAnimationData:(AWFWeatherMap *)weatherMap {
    self.timelineView.playButton.selected = NO;
    [self.timelineView setProgress:0.0 animated:YES];
    [self.timelineView showLoading:NO];
}

- (void)weatherMap:(AWFWeatherMap *)weatherMap didUpdateAnimationDataLoadingProgress:(NSInteger)totalLoaded total:(NSInteger)total {
    [self.timelineView setProgress:((CGFloat)totalLoaded / (CGFloat)total) animated:YES];
}

@end
        
class MapViewController: UIViewController {
    var weatherMap: AWFWeatherMap!
    var timelineView: AWFTimelineView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // setup the weather map config
        let config = AWFWeatherMapConfig()
        // make any changes to your configuration before creating the weather map instance...
        
        // create the weather map instance
        weatherMap = AWFWeatherMap(mapType: .apple, config: config)
        weatherMap.delegate = self
        view.addSubview(weatherMap.weatherMapView)
        
        timelineView = AWFTimelineView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 50.0))
        timelineView.delegate = self
        timelineView.startDate = weatherMap.timeline.fromTime
        timelineView.endDate = weatherMap.timeline.toTime
        timelineView.currentTime = weatherMap.timeline.fromTime
        view.addSubview(timelineView)
        
        // layout the map view container
        weatherMap.weatherMapView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([weatherMap.weatherMapView.topAnchor.constraint(equalTo: view.topAnchor),
                                        weatherMap.weatherMapView.leftAnchor.constraint(equalTo: view.leftAnchor),
                                        weatherMap.weatherMapView.rightAnchor.constraint(equalTo: view.rightAnchor),
                                        weatherMap.weatherMapView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])
        
        // layout the timeline view
        let timelineHeight: CGFloat = 50.0
        timelineView.translatesAutoresizingMaskIntoConstraints = false
        timelineView.preservesSuperviewLayoutMargins = true
        NSLayoutConstraint.activate([timelineView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: -timelineHeight),
                                        timelineView.leftAnchor.constraint(equalTo: view.leftAnchor),
                                        timelineView.rightAnchor.constraint(equalTo: view.rightAnchor),
                                        timelineView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])
        
        // add an action handler for the timeline view's playback control
        timelineView.playButton.addTarget(self, action: #selector(toggleAnimation(sender:)), for: .touchUpInside)
        
        // start the weather map at the current time
        weatherMap.go(toTime: Date())
    }
    
    // MARK: Control Handlers
    
    @objc func toggleAnimation(sender: Any) {
        if let btn = sender as? UIButton {
            if weatherMap.isAnimating || weatherMap.isLoadingAnimation {
                btn.isSelected = false
                weatherMap.stopAnimating()
            } else {
                btn.isSelected = true
                weatherMap.startAnimating()
            }
        }
    }
}

// Observe change events on the timelineView so the weather map can be updated accordingly, such as when the user pans
// the timeline or taps the "Now" control to go to the current time/date.
extension MapViewController: AWFTimelineViewDelegate {
    
    func timelineView(_ timelineView: AWFTimelineView!, didPanTo date: Date!) {
        if weatherMap.config.timelineScrubbingEnabled {
            weatherMap.pauseAnimation()
            weatherMap.go(toTime: date)
        }
    }
    
    func timelineView(_ timelineView: AWFTimelineView!, didSelect date: Date!) {
        weatherMap.stopAnimating()
        weatherMap.go(toTime: date)
    }
}

// Updates to the timelineView are handled by handling several AWFWeatherMapDelegate methods that notify our controller about changes to the weather map's
// timeline, such as start/end date changes and when the map begins or stops animating.
extension MapViewController: AWFWeatherMapDelegate {
    
    func weatherMap(_ weatherMap: AWFWeatherMap, didUpdateTimelineRangeFrom fromDate: Date, to toDate: Date) {
        timelineView.startDate = fromDate
        timelineView.endDate = toDate
        timelineView.currentTime = weatherMap.timeline.currentTime
    }
    
    func weatherMapDidStartAnimating(_ weatherMap: AWFWeatherMap) {
        timelineView.playButton.isSelected = true
    }
    
    func weatherMapDidStopAnimating(_ weatherMap: AWFWeatherMap) {
        timelineView.playButton.isSelected = false
    }
    
    func weatherMapDidResetAnimation(_ weatherMap: AWFWeatherMap) {
        timelineView.setProgress(0, animated: true)
    }
    
    func weatherMap(_ weatherMap: AWFWeatherMap, animationDidUpdateTo date: Date) {
        timelineView.currentTime = date
    }
    
    func weatherMapDidStartLoadingAnimationData(_ weatherMap: AWFWeatherMap) {
        timelineView.playButton.isSelected = true
        timelineView.showLoading(true)
    }
    
    func weatherMapDidFinishLoadingAnimationData(_ weatherMap: AWFWeatherMap) {
        timelineView.setProgress(1.0, animated: true)
        timelineView.showLoading(false)
    }
    
    func weatherMapDidCancelLoadingAnimationData(_ weatherMap: AWFWeatherMap) {
        timelineView.playButton.isSelected = false
        timelineView.setProgress(0, animated: true)
        timelineView.showLoading(false)
    }
    
    func weatherMap(_ weatherMap: AWFWeatherMap, didUpdateAnimationDataLoadingProgress totalLoaded: Int, total: Int) {
        timelineView.setProgress(CGFloat(totalLoaded) / CGFloat(total), animated: true)
    }
}