diff --git a/.gitmodules b/.gitmodules
index 332f239..ec02bc2 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -6,4 +6,4 @@
 	url = https://github.com/groue/GRMustache.git
 [submodule "MapView/Map/SMCalloutView"]
 	path = MapView/Map/SMCalloutView
-	url = https://github.com/mapbox/SMCalloutView.git
+	url = https://github.com/nfarina/calloutview.git
diff --git a/MapView/Map/Mapbox.h b/MapView/Map/Mapbox.h
index 5a24389..53572b1 100644
--- a/MapView/Map/Mapbox.h
+++ b/MapView/Map/Mapbox.h
@@ -36,6 +36,7 @@
 #import "RMConfiguration.h"
 #import "RMCoordinateGridSource.h"
 #import "RMDatabaseCache.h"
+#import "RMGreatCircleAnnotation.h"
 #import "RMInteractiveSource.h"
 #import "RMMBTilesSource.h"
 #import "RMMapboxSource.h"
diff --git a/MapView/Map/RMAnnotation.h b/MapView/Map/RMAnnotation.h
index 2d55782..b522924 100644
--- a/MapView/Map/RMAnnotation.h
+++ b/MapView/Map/RMAnnotation.h
@@ -157,6 +157,6 @@
 #pragma mark -
 
 // Used internally
-@property (nonatomic, strong) RMMapView *mapView;
+@property (nonatomic, weak) RMMapView *mapView;
 
 @end
diff --git a/MapView/Map/RMAttributionViewController.m b/MapView/Map/RMAttributionViewController.m
index 9c07e32..d184fc6 100644
--- a/MapView/Map/RMAttributionViewController.m
+++ b/MapView/Map/RMAttributionViewController.m
@@ -133,7 +133,11 @@
     {
         // add SDK info
         //
-        [attribution insertString:[NSString stringWithFormat:@"%@ uses the Mapbox iOS SDK © 2013 Mapbox, Inc.<br/><a href='http://mapbox.com/mapbox-ios-sdk'>More</a><br/><br/>", [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleDisplayName"]]  atIndex:0];
+        NSString *appName = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleDisplayName"];
+        NSDateFormatter *dateFormatter = [NSDateFormatter new];
+        [dateFormatter setDateFormat:@"yyyy"];
+        NSString *currentYear = [dateFormatter stringFromDate:[NSDate date]];
+        [attribution insertString:[NSString stringWithFormat:@"%@ uses the Mapbox iOS SDK © %@ Mapbox, Inc.<br/><a href='http://mapbox.com/mapbox-ios-sdk'>More</a><br/><br/>", appName, currentYear] atIndex:0];
 
         // add tinted logo
         //
diff --git a/MapView/Map/RMCompositeSource.h b/MapView/Map/RMCompositeSource.h
index 6397d77..1664916 100644
--- a/MapView/Map/RMCompositeSource.h
+++ b/MapView/Map/RMCompositeSource.h
@@ -28,10 +28,23 @@
 
 #import "RMAbstractMercatorTileSource.h"
 
+/** RMCompositeSource combines two or more other tile sources, compositing them into a single image per tile and caching that composited result to the tile cache.
+*
+*   RMCompositeSource can have better performance for instances of fully opaque tiles that are layered above other tiles in the tile source stacking order. It will determine if a tile is opaque and stop iteration of tile sources below it early as a result, since they would be obscured anyway. */
 @interface RMCompositeSource : RMAbstractMercatorTileSource
 
+/** @name Creating Tile Sources */
+
+/** Initialize a compositing tile source.
+*
+*   @param tileSources An array of tile sources to be composited.
+*   @param tileCacheKey A tile cache key for storage of composited result tiles. 
+*   @return An initialized compositing tile source. */
 - (id)initWithTileSources:(NSArray *)tileSources tileCacheKey:(NSString *)tileCacheKey;
 
+/** @name Querying Tile Source Information */
+
+/** An array of tile sources being composited. */
 @property (nonatomic, weak, readonly) NSArray *tileSources;
 
 @end
diff --git a/MapView/Map/RMDatabaseCache.m b/MapView/Map/RMDatabaseCache.m
index 196eb32..2f263f7 100644
--- a/MapView/Map/RMDatabaseCache.m
+++ b/MapView/Map/RMDatabaseCache.m
@@ -227,10 +227,11 @@
              {
                  BOOL result = [db executeUpdate:@"DELETE FROM ZCACHE WHERE last_used < ?", [NSDate dateWithTimeIntervalSinceNow:-_expiryPeriod]];
 
-                 if (result == NO)
-                     RMLog(@"Error expiring cache");
+                 if (result)
+                     result = [db executeUpdate:@"VACUUM"];
 
-                 [[db executeQuery:@"VACUUM"] close];
+                 if ( ! result)
+                     RMLog(@"Error expiring cache");
              }];
 
             [_writeQueueLock unlock];
@@ -333,10 +334,11 @@
      {
          BOOL result = [db executeUpdate:@"DELETE FROM ZCACHE WHERE tile_hash IN (SELECT tile_hash FROM ZCACHE ORDER BY last_used LIMIT ?)", [NSNumber numberWithUnsignedLongLong:count]];
 
-         if (result == NO)
-             RMLog(@"Error purging cache");
+         if (result)
+             result = [db executeUpdate:@"VACUUM"];
 
-         [[db executeQuery:@"VACUUM"] close];
+         if ( ! result)
+             RMLog(@"Error purging cache");
      }];
 
     [_writeQueueLock unlock];
@@ -355,10 +357,11 @@
          {
              BOOL result = [db executeUpdate:@"DELETE FROM ZCACHE"];
 
-             if (result == NO)
-                 RMLog(@"Error purging cache");
+             if (result)
+                 result = [db executeUpdate:@"VACUUM"];
 
-             [[db executeQuery:@"VACUUM"] close];
+             if ( ! result)
+                 RMLog(@"Error purging cache");
          }];
 
         [_writeQueueLock unlock];
@@ -378,7 +381,10 @@
          {
              BOOL result = [db executeUpdate:@"DELETE FROM ZCACHE WHERE cache_key = ?", cacheKey];
 
-             if (result == NO)
+             if (result)
+                 result = [db executeUpdate:@"VACUUM"];
+
+             if ( ! result)
                  RMLog(@"Error purging cache");
          }];
 
diff --git a/MapView/Map/RMGreatCircleAnnotation.h b/MapView/Map/RMGreatCircleAnnotation.h
new file mode 100644
index 0000000..9941aa0
--- /dev/null
+++ b/MapView/Map/RMGreatCircleAnnotation.h
@@ -0,0 +1,38 @@
+//
+//  RMGreatCircleAnnotation.m
+//  MapView
+//
+// Copyright (c) 2008-2013, Route-Me Contributors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+//   list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+//   this list of conditions and the following disclaimer in the documentation
+//   and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#import "RMShapeAnnotation.h"
+
+@interface RMGreatCircleAnnotation : RMShapeAnnotation
+
+- (id)initWithMapView:(RMMapView *)aMapView coordinate1:(CLLocationCoordinate2D)coordinate1 coordinate2:(CLLocationCoordinate2D)coordinate2;
+
+@property (nonatomic, readonly, assign) CLLocationCoordinate2D coordinate1;
+@property (nonatomic, readonly, assign) CLLocationCoordinate2D coordinate2;
+
+@end
diff --git a/MapView/Map/RMGreatCircleAnnotation.m b/MapView/Map/RMGreatCircleAnnotation.m
new file mode 100644
index 0000000..54d417a
--- /dev/null
+++ b/MapView/Map/RMGreatCircleAnnotation.m
@@ -0,0 +1,117 @@
+//
+//  RMGreatCircleAnnotation.m
+//  MapView
+//
+// Copyright (c) 2008-2013, Route-Me Contributors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+//   list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+//   this list of conditions and the following disclaimer in the documentation
+//   and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#import "RMGreatCircleAnnotation.h"
+
+#import "RMShape.h"
+
+@implementation RMGreatCircleAnnotation
+
+- (id)initWithMapView:(RMMapView *)aMapView coordinate1:(CLLocationCoordinate2D)coordinate1 coordinate2:(CLLocationCoordinate2D)coordinate2
+{
+    if (!(self = [super initWithMapView:aMapView points:@[ [[CLLocation alloc] initWithLatitude:coordinate1.latitude longitude:coordinate1.longitude], [[CLLocation alloc] initWithLatitude:coordinate2.latitude longitude:coordinate2.longitude]]]))
+        return nil;
+
+    _coordinate1 = coordinate1;
+    _coordinate2 = coordinate2;
+
+    return self;
+}
+
+- (RMMapLayer *)layer
+{
+    if ( ! [super layer])
+    {
+        RMShape *shape = [[RMShape alloc] initWithView:self.mapView];
+
+        [shape performBatchOperations:^(RMShape *aShape)
+        {
+            // based on implementation at http://stackoverflow.com/questions/6104517/drawing-great-circle-overlay-lines-on-an-mkmapview
+            //
+            double lat1 = self.coordinate1.latitude;
+            double lon1 = self.coordinate1.longitude;
+            double lat2 = self.coordinate2.latitude;
+            double lon2 = self.coordinate2.longitude;
+            lat1 = lat1 * (M_PI/180);
+            lon1 = lon1 * (M_PI/180);
+            lat2 = lat2 * (M_PI/180);
+            lon2 = lon2 * (M_PI/180);
+            double d = 2 * asin( sqrt(pow(( sin( (lat1-lat2)/2) ), 2) + cos(lat1) * cos(lat2) * pow(( sin( (lon1-lon2)/2) ), 2)));
+            int numsegs = 100;
+            NSMutableArray *coords = [NSMutableArray arrayWithCapacity:numsegs];
+            double f = 0.0;
+            for(int i=1; i<=numsegs; i++)
+            {
+                f += 1.0 / (float)numsegs;
+                double A=sin((1-f)*d)/sin(d);
+                double B=sin(f*d)/sin(d);
+                double x = A*cos(lat1) * cos(lon1) +  B * cos(lat2) * cos(lon2);
+                double y = A*cos(lat1) * sin(lon1) +  B * cos(lat2) * sin(lon2);
+                double z = A*sin(lat1)           +  B*sin(lat2);
+                double latr=atan2(z, sqrt(pow(x, 2) + pow(y, 2) ));
+                double lonr=atan2(y, x);
+                double lat = latr * (180/M_PI);
+                double lon = lonr * (180/M_PI);
+                [coords addObject:[[CLLocation alloc] initWithLatitude:lat longitude:lon]];
+            }
+            CLLocationCoordinate2D prevCoord;
+            NSMutableArray *coords2 = [NSMutableArray array];
+            for(int i=0; i<numsegs; i++)
+            {
+                CLLocationCoordinate2D coord = ((CLLocation *)coords[i]).coordinate;
+                if(prevCoord.longitude < -170 && prevCoord.longitude > -180  && prevCoord.longitude < 0
+                   && coord.longitude > 170 && coord.longitude < 180 && coord.longitude > 0)
+                {
+                    [coords2 addObjectsFromArray:[coords subarrayWithRange:NSMakeRange(i, [coords count] - i)]];
+                    [coords removeObjectsInRange:NSMakeRange(i, [coords count] - i)];
+                    break;
+                }
+                prevCoord = coord;
+            }
+
+            [aShape moveToCoordinate:((CLLocation *)coords[0]).coordinate];
+
+            for (int i = 1; i < [coords count]; i++)
+                [aShape addLineToCoordinate:((CLLocation *)coords[i]).coordinate];
+
+            if ([coords2 count])
+            {
+                [aShape moveToCoordinate:((CLLocation *)coords2[0]).coordinate];
+
+                for (int j = 1; j < [coords2 count]; j++)
+                    [aShape addLineToCoordinate:((CLLocation *)coords2[j]).coordinate];
+            }
+        }];
+
+        super.layer = shape;
+    }
+    
+    return [super layer];
+}
+
+@end
diff --git a/MapView/Map/RMMapLayer.h b/MapView/Map/RMMapLayer.h
index 8007eaa..dcd3e8c 100644
--- a/MapView/Map/RMMapLayer.h
+++ b/MapView/Map/RMMapLayer.h
@@ -77,7 +77,7 @@
 *
 *   If the value of this property is `NO`, the value of the title string is ignored and the annotation remains enabled by default. You can still disable the annotation explicitly using the enabled property. 
 *
-*   Note that callouts are not supported on cluster annotation layers. These annotations can be interacted with, but do not remain consistent visually during map pan and zoom events; thus, callout behavior would be inconsistent. */
+*   Note that callouts are not supported on non-marker or cluster annotation layers. These annotations can be interacted with, but do not remain consistent visually during map pan and zoom events; thus, callout behavior would be inconsistent. */
 @property (nonatomic, assign) BOOL canShowCallout;
 
 /** The offset (in pixels) at which to place the callout bubble.
diff --git a/MapView/Map/RMMapLayer.m b/MapView/Map/RMMapLayer.m
index 4bb41be..cad6e76 100644
--- a/MapView/Map/RMMapLayer.m
+++ b/MapView/Map/RMMapLayer.m
@@ -29,6 +29,7 @@
 #import "RMPixel.h"
 #import "RMAnnotation.h"
 #import "RMMapView.h"
+#import "RMMarker.h"
 
 @interface RMMapView (PrivateMethods)
 
@@ -74,8 +75,13 @@
 
 - (void)setCanShowCallout:(BOOL)canShowCallout
 {
-    if ( ! self.annotation.isClusterAnnotation)
-        _canShowCallout = canShowCallout;
+    if (canShowCallout)
+    {
+        NSAssert([self isKindOfClass:[RMMarker class]],  @"Callouts are not supported on non-marker annotation layers");
+        NSAssert( ! self.annotation.isClusterAnnotation, @"Callouts are not supported on cluster annotation layers");
+    }
+
+    _canShowCallout = canShowCallout;
 }
 
 - (void)setPosition:(CGPoint)position animated:(BOOL)animated
diff --git a/MapView/Map/RMMapTiledLayerView.m b/MapView/Map/RMMapTiledLayerView.m
index f898f31..b45aa89 100644
--- a/MapView/Map/RMMapTiledLayerView.m
+++ b/MapView/Map/RMMapTiledLayerView.m
@@ -41,7 +41,7 @@
 
 @implementation RMMapTiledLayerView
 {
-    RMMapView *_mapView;
+    __weak RMMapView *_mapView;
     id <RMTileSource> _tileSource;
 }
 
@@ -176,7 +176,7 @@
                     {
                         // ensure only one request for a URL at a time
                         //
-                        @synchronized ([(RMAbstractWebMapSource *)_tileSource URLForTile:RMTileMake(x, y, zoom)])
+                        @synchronized ([(RMAbstractWebMapSource *)_tileSource URLsForTile:RMTileMake(x, y, zoom)])
                         {
                             // this will return quicker if cached since above attempt, else block on fetch
                             //
diff --git a/MapView/Map/RMMapView.m b/MapView/Map/RMMapView.m
index dc9040b..a9942f6 100644
--- a/MapView/Map/RMMapView.m
+++ b/MapView/Map/RMMapView.m
@@ -131,8 +131,6 @@
 
 @implementation RMMapView
 {
-    id <RMMapViewDelegate> _delegate;
-
     BOOL _delegateHasBeforeMapMove;
     BOOL _delegateHasAfterMapMove;
     BOOL _delegateHasBeforeMapZoom;
@@ -197,7 +195,7 @@
 
     RMUserTrackingBarButtonItem *_userTrackingBarButtonItem;
 
-    UIViewController *_viewControllerPresentingAttribution;
+    __weak UIViewController *_viewControllerPresentingAttribution;
     UIButton *_attributionButton;
     UIPopoverController *_attributionPopover;
 
@@ -655,11 +653,6 @@
 #pragma mark -
 #pragma mark Delegate
 
-- (id <RMMapViewDelegate>)delegate
-{
-	return _delegate;
-}
-
 - (void)setDelegate:(id <RMMapViewDelegate>)aDelegate
 {
     if (_delegate == aDelegate)
@@ -710,12 +703,16 @@
     {
         BOOL flag = wasUserEvent;
 
+        __weak RMMapView *weakSelf = self;
+        BOOL hasBeforeMapMove = _delegateHasBeforeMapMove;
+        BOOL hasAfterMapMove  = _delegateHasAfterMapMove;
+
         if ([_moveDelegateQueue operationCount] == 0)
         {
             dispatch_async(dispatch_get_main_queue(), ^(void)
             {
-                if (_delegateHasBeforeMapMove)
-                    [_delegate beforeMapMove:self byUser:flag];
+                if (hasBeforeMapMove)
+                    [_delegate beforeMapMove:weakSelf byUser:flag];
             });
         }
 
@@ -727,8 +724,8 @@
             {
                 dispatch_async(dispatch_get_main_queue(), ^(void)
                 {
-                    if (_delegateHasAfterMapMove)
-                        [_delegate afterMapMove:self byUser:flag];
+                    if (hasAfterMapMove)
+                        [_delegate afterMapMove:weakSelf byUser:flag];
                 });
             }];
         }
@@ -749,12 +746,16 @@
     {
         BOOL flag = wasUserEvent;
 
+        __weak RMMapView *weakSelf = self;
+        BOOL hasBeforeMapZoom = _delegateHasBeforeMapZoom;
+        BOOL hasAfterMapZoom  = _delegateHasAfterMapZoom;
+
         if ([_zoomDelegateQueue operationCount] == 0)
         {
             dispatch_async(dispatch_get_main_queue(), ^(void)
             {
-                if (_delegateHasBeforeMapZoom)
-                    [_delegate beforeMapZoom:self byUser:flag];
+                if (hasBeforeMapZoom)
+                    [_delegate beforeMapZoom:weakSelf byUser:flag];
             });
         }
 
@@ -766,8 +767,8 @@
             {
                 dispatch_async(dispatch_get_main_queue(), ^(void)
                 {
-                    if (_delegateHasAfterMapZoom)
-                        [_delegate afterMapZoom:self byUser:flag];
+                    if (hasAfterMapZoom)
+                        [_delegate afterMapZoom:weakSelf byUser:flag];
                 });
             }];
         }
@@ -1070,17 +1071,27 @@
 
 - (void)setZoom:(float)newZoom atCoordinate:(CLLocationCoordinate2D)newCenter animated:(BOOL)animated
 {
-    [UIView animateWithDuration:(animated ? 0.3 : 0.0)
-                          delay:0.0
-                        options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
-                     animations:^(void)
-                     {
-                         [self setZoom:newZoom];
-                         [self setCenterCoordinate:newCenter animated:NO];
+    if (animated)
+    {
+        [UIView animateWithDuration:0.3
+                              delay:0.0
+                            options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
+                         animations:^(void)
+                         {
+                             [self setZoom:newZoom];
+                             [self setCenterCoordinate:newCenter animated:NO];
 
-                         self.userTrackingMode = RMUserTrackingModeNone;
-                     }
-                     completion:nil];
+                             self.userTrackingMode = RMUserTrackingModeNone;
+                         }
+                         completion:nil];
+    }
+    else
+    {
+        [self setZoom:newZoom];
+        [self setCenterCoordinate:newCenter animated:NO];
+
+        self.userTrackingMode = RMUserTrackingModeNone;
+    }
 }
 
 - (void)zoomByFactor:(float)zoomFactor near:(CGPoint)pivot animated:(BOOL)animated
@@ -1595,21 +1606,6 @@
     {
         [self correctPositionOfAllAnnotationsIncludingInvisibles:NO animated:(_mapScrollViewIsZooming && !_mapScrollView.zooming)];
 
-        if (_currentAnnotation && ! [_currentAnnotation isKindOfClass:[RMMarker class]])
-        {
-            // adjust shape annotation callouts for frame changes during zoom
-            //
-            _currentCallout.delegate = nil;
-
-            [_currentCallout presentCalloutFromRect:_currentAnnotation.layer.bounds
-                                            inLayer:_currentAnnotation.layer
-                                 constrainedToLayer:self.layer
-                           permittedArrowDirections:SMCalloutArrowDirectionDown
-                                           animated:NO];
-
-            _currentCallout.delegate = self;
-        }
-
         _lastZoom = _zoom;
     }
 
@@ -1833,13 +1829,13 @@
             _draggedAnnotation = nil;
         }
     }
-    else if ([hit isKindOfClass:[RMMapLayer class]] && _delegateHasLongPressOnAnnotation)
+    else if (recognizer.state == UIGestureRecognizerStateBegan && [hit isKindOfClass:[RMMapLayer class]] && _delegateHasLongPressOnAnnotation)
     {
         // pass annotation long-press to delegate
         //
         [_delegate longPressOnAnnotation:[((RMMapLayer *)hit) annotation] onMap:self];
     }
-    else if (_delegateHasLongPressOnMap)
+    else if (recognizer.state == UIGestureRecognizerStateBegan && _delegateHasLongPressOnMap)
     {
         // pass map long-press to delegate
         //
@@ -1894,16 +1890,15 @@
     }
     else if (anAnnotation.isEnabled && ! [anAnnotation isEqual:_currentAnnotation])
     {
+        self.userTrackingMode = RMUserTrackingModeNone;
+
         [self deselectAnnotation:_currentAnnotation animated:NO];
 
         _currentAnnotation = anAnnotation;
 
         if (anAnnotation.layer.canShowCallout && anAnnotation.title)
         {
-            _currentCallout = [SMCalloutView new];
-
-            if (RMPreVersion7)
-                _currentCallout.backgroundView = [SMCalloutBackgroundView systemBackgroundView];
+            _currentCallout = [SMCalloutView platformCalloutView];
 
             if (RMPostVersion7)
                 _currentCallout.tintColor = self.tintColor;
@@ -1931,10 +1926,11 @@
 
             _currentCallout.delegate = self;
 
+            _currentCallout.permittedArrowDirection = SMCalloutArrowDirectionDown;
+
             [_currentCallout presentCalloutFromRect:anAnnotation.layer.bounds
                                             inLayer:anAnnotation.layer
                                  constrainedToLayer:self.layer
-                           permittedArrowDirections:SMCalloutArrowDirectionDown
                                            animated:animated];
         }
 
@@ -2628,10 +2624,12 @@
         _logoBug.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin;
         _logoBug.translatesAutoresizingMaskIntoConstraints = NO;
         [self addSubview:_logoBug];
+        [self updateConstraints];
     }
     else if ( ! showLogoBug && _logoBug)
     {
         [_logoBug removeFromSuperview];
+        _logoBug = nil;
     }
 
     _showLogoBug = showLogoBug;
@@ -3126,6 +3124,9 @@
 
 - (void)addAnnotation:(RMAnnotation *)annotation
 {
+    if ( ! annotation)
+        return;
+
     @synchronized (_annotations)
     {
         if ([_annotations containsObject:annotation])
@@ -3158,6 +3159,9 @@
 
 - (void)addAnnotations:(NSArray *)newAnnotations
 {
+    if ( ! newAnnotations || ! [newAnnotations count])
+        return;
+
     @synchronized (_annotations)
     {
         [_annotations addObjectsFromArray:newAnnotations];
@@ -3602,7 +3606,9 @@
     if (_delegateHasDidUpdateUserLocation)
         [_delegate mapView:self didUpdateUserLocation:self.userLocation];
 
-    if (newHeading.trueHeading != 0 && self.userTrackingMode == RMUserTrackingModeFollowWithHeading)
+    CLLocationDirection headingDirection = (newHeading.trueHeading > 0 ? newHeading.trueHeading : newHeading.magneticHeading);
+
+    if (headingDirection != 0 && self.userTrackingMode == RMUserTrackingModeFollowWithHeading)
     {
         if (_userHeadingTrackingView.alpha < 1.0)
             [UIView animateWithDuration:0.5 animations:^(void) { _userHeadingTrackingView.alpha = 1.0; }];
@@ -3616,7 +3622,7 @@
                             options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
                          animations:^(void)
                          {
-                             CGFloat angle = (M_PI / -180) * newHeading.trueHeading;
+                             CGFloat angle = (M_PI / -180) * headingDirection;
 
                              _mapTransform = CGAffineTransformMakeRotation(angle);
                              _annotationTransform = CATransform3DMakeAffineTransform(CGAffineTransformMakeRotation(-angle));
@@ -3781,10 +3787,12 @@
                                               _attributionButton.bounds.size.width,
                                               _attributionButton.bounds.size.height);
         [self addSubview:_attributionButton];
+        [self updateConstraints];
     }
     else if ( ! _viewControllerPresentingAttribution && _attributionButton)
     {
         [_attributionButton removeFromSuperview];
+        _attributionButton = nil;
     }
 }
 
diff --git a/MapView/Map/RMMapboxSource.h b/MapView/Map/RMMapboxSource.h
index 806f62b..dbd578a 100644
--- a/MapView/Map/RMMapboxSource.h
+++ b/MapView/Map/RMMapboxSource.h
@@ -92,7 +92,7 @@ typedef enum : NSUInteger {
 *   @return An initialized Mapbox tile source. */
 - (id)initWithTileJSON:(NSString *)tileJSON;
 
-/** For TileJSON 2.1.0+ layers, initialize a tile source and automatically find and add annotations from [simplestyle](http://mapbox.com/developers/simplestyle/) data.
+/** For TileJSON 2.1.0+ layers, initialize a tile source and automatically find and add point annotations from [simplestyle](http://mapbox.com/developers/simplestyle/) data.
 *
 *   This method requires a network connection in order to download the TileJSON used to define the tile source.
 *
@@ -101,7 +101,7 @@ typedef enum : NSUInteger {
 *   @return An initialized Mapbox tile source. */
 - (id)initWithMapID:(NSString *)mapID enablingDataOnMapView:(RMMapView *)mapView;
 
-/** For TileJSON 2.1.0+ layers, initialize a tile source and automatically find and add annotations from [simplestyle](http://mapbox.com/developers/simplestyle/) data, optionally enabling SSL.
+/** For TileJSON 2.1.0+ layers, initialize a tile source and automatically find and add point annotations from [simplestyle](http://mapbox.com/developers/simplestyle/) data, optionally enabling SSL.
 *
 *   This method requires a network connection in order to download the TileJSON used to define the tile source.
 *
@@ -111,13 +111,13 @@ typedef enum : NSUInteger {
 *   @return An initialized Mapbox tile source. */
 - (id)initWithMapID:(NSString *)mapID enablingDataOnMapView:(RMMapView *)mapView enablingSSL:(BOOL)enableSSL;
 
-/** For TileJSON 2.1.0+ layers, initialize a tile source and automatically find and add annotations from [simplestyle](http://mapbox.com/developers/simplestyle/) data.
+/** For TileJSON 2.1.0+ layers, initialize a tile source and automatically find and add point annotations from [simplestyle](http://mapbox.com/developers/simplestyle/) data.
 *   @param tileJSON A string containing TileJSON.
 *   @param mapView A map view on which to display the annotations. 
 *   @return An initialized Mapbox tile source. */
 - (id)initWithTileJSON:(NSString *)tileJSON enablingDataOnMapView:(RMMapView *)mapView;
 
-/** For TileJSON 2.1.0+ layers, initialize a tile source and automatically find and add annotations from [simplestyle](http://mapbox.com/developers/simplestyle/) data.
+/** For TileJSON 2.1.0+ layers, initialize a tile source and automatically find and add point annotations from [simplestyle](http://mapbox.com/developers/simplestyle/) data.
 *
 *   Passing a remote URL requires a network connection. If offline functionality is desired, you should cache the TileJSON locally at a prior date, then pass a file path URL to this method.
 *
diff --git a/MapView/Map/RMMapboxSource.m b/MapView/Map/RMMapboxSource.m
index 375c7ff..a798678 100644
--- a/MapView/Map/RMMapboxSource.m
+++ b/MapView/Map/RMMapboxSource.m
@@ -85,11 +85,14 @@
 
         _tileJSON = tileJSON;
 
-        _uniqueTilecacheKey = [NSString stringWithFormat:@"Mapbox-%@%@", [_infoDictionary objectForKey:@"id"], ([_infoDictionary objectForKey:@"version"] ? [@"-" stringByAppendingString:[_infoDictionary objectForKey:@"version"]] : @"")];
+        if ([_infoDictionary[@"id"] hasPrefix:@"examples."])
+            RMLog(@"Using watermarked example map ID %@. Please go to http://mapbox.com and create your own map style.", _infoDictionary[@"id"]);
+
+        _uniqueTilecacheKey = [NSString stringWithFormat:@"Mapbox-%@%@", _infoDictionary[@"id"], (_infoDictionary[@"version"] ? [@"-" stringByAppendingString:_infoDictionary[@"version"]] : @"")];
 
         id dataObject = nil;
         
-        if (mapView && (dataObject = [_infoDictionary objectForKey:@"data"]) && dataObject)
+        if (mapView && (dataObject = _infoDictionary[@"data"]) && dataObject)
         {
             dispatch_async(_dataQueue, ^(void)
             {
@@ -111,28 +114,31 @@
                         
                         if ((jsonObject = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]) && [jsonObject isKindOfClass:[NSDictionary class]])
                         {
-                            for (NSDictionary *feature in [jsonObject objectForKey:@"features"])
+                            for (NSDictionary *feature in jsonObject[@"features"])
                             {
-                                NSDictionary *properties = [feature objectForKey:@"properties"];
-                                
-                                CLLocationCoordinate2D coordinate = {
-                                    .longitude = [[[[feature objectForKey:@"geometry"] objectForKey:@"coordinates"] objectAtIndex:0] floatValue],
-                                    .latitude  = [[[[feature objectForKey:@"geometry"] objectForKey:@"coordinates"] objectAtIndex:1] floatValue]
-                                };
-
-                                RMAnnotation *annotation = nil;
-
-                                if ([mapView.delegate respondsToSelector:@selector(mapView:layerForAnnotation:)])
-                                    annotation = [RMAnnotation annotationWithMapView:mapView coordinate:coordinate andTitle:[properties objectForKey:@"title"]];
-                                else
-                                    annotation = [RMPointAnnotation annotationWithMapView:mapView coordinate:coordinate andTitle:[properties objectForKey:@"title"]];
-                                
-                                annotation.userInfo = properties;
-                                
-                                dispatch_async(dispatch_get_main_queue(), ^(void)
+                                if ([feature[@"geometry"][@"type"] isEqualToString:@"Point"])
                                 {
-                                    [mapView addAnnotation:annotation];
-                                });
+                                    NSDictionary *properties = feature[@"properties"];
+
+                                    CLLocationCoordinate2D coordinate = {
+                                        .longitude = [feature[@"geometry"][@"coordinates"][0] floatValue],
+                                        .latitude  = [feature[@"geometry"][@"coordinates"][1] floatValue]
+                                    };
+
+                                    RMAnnotation *annotation = nil;
+
+                                    if ([mapView.delegate respondsToSelector:@selector(mapView:layerForAnnotation:)])
+                                        annotation = [RMAnnotation annotationWithMapView:mapView coordinate:coordinate andTitle:properties[@"title"]];
+                                    else
+                                        annotation = [RMPointAnnotation annotationWithMapView:mapView coordinate:coordinate andTitle:properties[@"title"]];
+
+                                    annotation.userInfo = properties;
+
+                                    dispatch_async(dispatch_get_main_queue(), ^(void)
+                                    {
+                                        [mapView addAnnotation:annotation];
+                                    });
+                                }
                             }
                         }
                     }
@@ -187,9 +193,9 @@
 
 - (NSURL *)tileJSONURL
 {
-    BOOL useSSL = [[[self.infoDictionary objectForKey:@"tiles"] objectAtIndex:0] hasPrefix:@"https"];
+    BOOL useSSL = [self.infoDictionary[@"tiles"][0] hasPrefix:@"https"];
 
-    return [NSURL URLWithString:[NSString stringWithFormat:@"http%@://api.tiles.mapbox.com/v3/%@.json%@", (useSSL ? @"s" : @""), [self.infoDictionary objectForKey:@"id"], (useSSL ? @"?secure" : @"")]];
+    return [NSURL URLWithString:[NSString stringWithFormat:@"http%@://api.tiles.mapbox.com/v3/%@.json%@", (useSSL ? @"s" : @""), self.infoDictionary[@"id"], (useSSL ? @"?secure" : @"")]];
 }
 
 - (NSURL *)URLForTile:(RMTile)tile
@@ -198,16 +204,16 @@
     NSInteger x    = tile.x;
     NSInteger y    = tile.y;
 
-    if ([self.infoDictionary objectForKey:@"scheme"] && [[self.infoDictionary objectForKey:@"scheme"] isEqual:@"tms"])
+    if (self.infoDictionary[@"scheme"] && [self.infoDictionary[@"scheme"] isEqual:@"tms"])
         y = pow(2, zoom) - tile.y - 1;
 
     NSString *tileURLString = nil;
 
-    if ([self.infoDictionary objectForKey:@"tiles"])
-        tileURLString = [[self.infoDictionary objectForKey:@"tiles"] objectAtIndex:0];
+    if (self.infoDictionary[@"tiles"])
+        tileURLString = self.infoDictionary[@"tiles"][0];
 
     else
-        tileURLString = [self.infoDictionary objectForKey:@"tileURL"];
+        tileURLString = self.infoDictionary[@"tileURL"];
 
     tileURLString = [tileURLString stringByReplacingOccurrencesOfString:@"{z}" withString:[[NSNumber numberWithInteger:zoom] stringValue]];
     tileURLString = [tileURLString stringByReplacingOccurrencesOfString:@"{x}" withString:[[NSNumber numberWithInteger:x]    stringValue]];
@@ -276,17 +282,17 @@
 
 - (float)minZoom
 {
-    return [[self.infoDictionary objectForKey:@"minzoom"] floatValue];
+    return [self.infoDictionary[@"minzoom"] floatValue];
 }
 
 - (float)maxZoom
 {
-    return [[self.infoDictionary objectForKey:@"maxzoom"] floatValue];
+    return [self.infoDictionary[@"maxzoom"] floatValue];
 }
 
 - (RMSphericalTrapezium)latitudeLongitudeBoundingBox
 {
-    id bounds = [self.infoDictionary objectForKey:@"bounds"];
+    id bounds = self.infoDictionary[@"bounds"];
 
     NSArray *parts = nil;
 
@@ -329,15 +335,15 @@
 
 - (NSString *)legend
 {
-    return [self.infoDictionary objectForKey:@"legend"];
+    return self.infoDictionary[@"legend"];
 }
 
 - (CLLocationCoordinate2D)centerCoordinate
 {
-    if ([self.infoDictionary objectForKey:@"center"])
+    if (self.infoDictionary[@"center"])
     {
-        return CLLocationCoordinate2DMake([[[self.infoDictionary objectForKey:@"center"] objectAtIndex:1] doubleValue], 
-                                          [[[self.infoDictionary objectForKey:@"center"] objectAtIndex:0] doubleValue]);
+        return CLLocationCoordinate2DMake([self.infoDictionary[@"center"][1] doubleValue],
+                                          [self.infoDictionary[@"center"][0] doubleValue]);
     }
     
     return CLLocationCoordinate2DMake(0, 0);
@@ -345,9 +351,9 @@
 
 - (float)centerZoom
 {
-    if ([self.infoDictionary objectForKey:@"center"])
+    if (self.infoDictionary[@"center"])
     {
-        return [[[self.infoDictionary objectForKey:@"center"] objectAtIndex:2] floatValue];
+        return [self.infoDictionary[@"center"][2] floatValue];
     }
     
     return roundf(([self maxZoom] + [self minZoom]) / 2);
@@ -360,17 +366,17 @@
 
 - (NSString *)shortName
 {
-	return [self.infoDictionary objectForKey:@"name"];
+	return self.infoDictionary[@"name"];
 }
 
 - (NSString *)longDescription
 {
-	return [self.infoDictionary objectForKey:@"description"];
+	return self.infoDictionary[@"description"];
 }
 
 - (NSString *)shortAttribution
 {
-	return [self.infoDictionary objectForKey:@"attribution"];
+	return self.infoDictionary[@"attribution"];
 }
 
 - (NSString *)longAttribution
diff --git a/MapView/Map/RMQuadTree.m b/MapView/Map/RMQuadTree.m
index 181014e..f7e828b 100644
--- a/MapView/Map/RMQuadTree.m
+++ b/MapView/Map/RMQuadTree.m
@@ -70,7 +70,7 @@
     NSMutableArray *_annotations;
     RMQuadTreeNode *_parentNode, *_northWest, *_northEast, *_southWest, *_southEast;
     RMQuadTreeNodeType _nodeType;
-    RMMapView *_mapView;
+    __weak RMMapView *_mapView;
 
     RMAnnotation *_cachedClusterAnnotation;
     NSArray *_cachedClusterEnclosedAnnotations;
@@ -596,7 +596,7 @@
 @implementation RMQuadTree
 {
     RMQuadTreeNode *_rootNode;
-    RMMapView *_mapView;
+    __weak RMMapView *_mapView;
 }
 
 - (id)initWithMapView:(RMMapView *)aMapView
diff --git a/MapView/Map/RMShape.h b/MapView/Map/RMShape.h
index 62f8ae6..a525768 100644
--- a/MapView/Map/RMShape.h
+++ b/MapView/Map/RMShape.h
@@ -84,6 +84,9 @@
 /** The bounding box of the shape in the current viewport. */
 @property (nonatomic, readonly) CGRect pathBoundingBox;
 
+/** An additional pixel area around the shape that is applied to touch hit testing events. Defaults to none. */
+@property (nonatomic, assign) CGFloat additionalTouchPadding;
+
 /** @name Drawing Shapes */
 
 /** Move the drawing pen to a projected point. 
diff --git a/MapView/Map/RMShape.m b/MapView/Map/RMShape.m
index 59ca748..a9ded69 100644
--- a/MapView/Map/RMShape.m
+++ b/MapView/Map/RMShape.m
@@ -435,10 +435,24 @@
     {
         // if shape is not filled with a color, do a simple "point on path" test
         //
-        UIGraphicsBeginImageContext(self.bounds.size);
-        CGContextAddPath(UIGraphicsGetCurrentContext(), shapeLayer.path);
-        containsPoint = CGContextPathContainsPoint(UIGraphicsGetCurrentContext(), thePoint, kCGPathStroke);
-        UIGraphicsEndImageContext();
+        if (self.additionalTouchPadding)
+        {
+            CGPathRef tapTargetPath = CGPathCreateCopyByStrokingPath(shapeLayer.path, nil, fmaxf(self.additionalTouchPadding, shapeLayer.lineWidth), 0, 0, 0);
+
+            if (tapTargetPath)
+            {
+                containsPoint = [[UIBezierPath bezierPathWithCGPath:tapTargetPath] containsPoint:thePoint];
+
+                CGPathRelease(tapTargetPath);
+            }
+        }
+        else
+        {
+            UIGraphicsBeginImageContext(self.bounds.size);
+            CGContextAddPath(UIGraphicsGetCurrentContext(), shapeLayer.path);
+            containsPoint = CGContextPathContainsPoint(UIGraphicsGetCurrentContext(), thePoint, kCGPathStroke);
+            UIGraphicsEndImageContext();
+        }
     }
     else
     {
diff --git a/MapView/Map/RMStaticMapView.m b/MapView/Map/RMStaticMapView.m
index 9aa717c..c9f4592 100644
--- a/MapView/Map/RMStaticMapView.m
+++ b/MapView/Map/RMStaticMapView.m
@@ -89,7 +89,7 @@
 
 - (void)performInitializationWithMapID:(NSString *)mapID centerCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(CGFloat)zoomLevel completionHandler:(void (^)(UIImage *))handler
 {
-    RMMapboxSource *tileSource = [[RMMapboxSource alloc] initWithMapID:kMapboxPlaceholderMapID enablingDataOnMapView:self];
+    RMMapboxSource *tileSource = [[RMMapboxSource alloc] initWithMapID:(mapID ? mapID : kMapboxPlaceholderMapID) enablingDataOnMapView:self];
 
     self.tileSource = tileSource;
 
diff --git a/MapView/Map/RMTileCache.h b/MapView/Map/RMTileCache.h
index 9579a40..7d10d1f 100644
--- a/MapView/Map/RMTileCache.h
+++ b/MapView/Map/RMTileCache.h
@@ -81,14 +81,14 @@ typedef enum : short {
 *   @param tileCache The tile cache. 
 *   @param tileCount The total number of tiles required for coverage of the desired geographic area. 
 *   @param tileSource The tile source providing the tiles. */
-- (void)tileCache:(RMTileCache *)tileCache didBeginBackgroundCacheWithCount:(int)tileCount forTileSource:(id <RMTileSource>)tileSource;
+- (void)tileCache:(RMTileCache *)tileCache didBeginBackgroundCacheWithCount:(NSUInteger)tileCount forTileSource:(id <RMTileSource>)tileSource;
 
 /** Sent upon caching of each tile in a background cache operation.
 *   @param tileCache The tile cache. 
 *   @param tile A structure representing the tile in question. 
 *   @param tileIndex The index of the tile in question, beginning with `1` and ending with totalTileCount. 
 *   @param totalTileCount The total number of of tiles required for coverage of the desired geographic area. */
-- (void)tileCache:(RMTileCache *)tileCache didBackgroundCacheTile:(RMTile)tile withIndex:(int)tileIndex ofTotalTileCount:(int)totalTileCount;
+- (void)tileCache:(RMTileCache *)tileCache didBackgroundCacheTile:(RMTile)tile withIndex:(NSUInteger)tileIndex ofTotalTileCount:(NSUInteger)totalTileCount;
 
 /** Sent when all tiles have completed downloading and caching. 
 *   @param tileCache The tile cache. */
@@ -156,11 +156,19 @@ typedef enum : short {
 *   @param northEast The northeast corner of the geographic area to cache. 
 *   @param minZoom The minimum zoom level to cache. 
 *   @param maxZoom The maximum zoom level to cache. */
-- (void)beginBackgroundCacheForTileSource:(id <RMTileSource>)tileSource southWest:(CLLocationCoordinate2D)southWest northEast:(CLLocationCoordinate2D)northEast minZoom:(float)minZoom maxZoom:(float)maxZoom;
+- (void)beginBackgroundCacheForTileSource:(id <RMTileSource>)tileSource southWest:(CLLocationCoordinate2D)southWest northEast:(CLLocationCoordinate2D)northEast minZoom:(NSUInteger)minZoom maxZoom:(NSUInteger)maxZoom;
 
 /** Cancel any background caching. 
 *
 *   This method returns immediately so as to not block the calling thread. If you wish to be notified of the actual cancellation completion, implement the tileCacheDidCancelBackgroundCache: delegate method. */
 - (void)cancelBackgroundCache;
 
+/** A count of the number of tiles that would be downloaded in a background tile cache download operation.
+*   @param southWest The southwest corner of the geographic area to cache.
+*   @param northEast The northeast corner of the geographic area to cache.
+*   @param minZoom The minimum zoom level to cache.
+*   @param maxZoom The maximum zoom level to cache.
+*   @return The number of tiles representing the coverage area. */
+- (NSUInteger)tileCountForSouthWest:(CLLocationCoordinate2D)southWest northEast:(CLLocationCoordinate2D)northEast minZoom:(NSUInteger)minZoom maxZoom:(NSUInteger)maxZoom;
+
 @end
diff --git a/MapView/Map/RMTileCache.m b/MapView/Map/RMTileCache.m
index 5c47773..be35c8d 100644
--- a/MapView/Map/RMTileCache.m
+++ b/MapView/Map/RMTileCache.m
@@ -252,32 +252,25 @@
     return (_activeTileSource || _backgroundFetchQueue);
 }
 
-- (void)beginBackgroundCacheForTileSource:(id <RMTileSource>)tileSource southWest:(CLLocationCoordinate2D)southWest northEast:(CLLocationCoordinate2D)northEast minZoom:(float)minZoom maxZoom:(float)maxZoom
+- (NSUInteger)tileCountForSouthWest:(CLLocationCoordinate2D)southWest northEast:(CLLocationCoordinate2D)northEast minZoom:(NSUInteger)minZoom maxZoom:(NSUInteger)maxZoom
 {
-    if (self.isBackgroundCaching)
-        return;
+    NSUInteger minCacheZoom = minZoom;
+    NSUInteger maxCacheZoom = maxZoom;
 
-    _activeTileSource = tileSource;
-    
-    _backgroundFetchQueue = [[NSOperationQueue alloc] init];
-    [_backgroundFetchQueue setMaxConcurrentOperationCount:6];
-    
-    int   minCacheZoom = (int)minZoom;
-    int   maxCacheZoom = (int)maxZoom;
-    float minCacheLat  = southWest.latitude;
-    float maxCacheLat  = northEast.latitude;
-    float minCacheLon  = southWest.longitude;
-    float maxCacheLon  = northEast.longitude;
+    CLLocationDegrees minCacheLat = southWest.latitude;
+    CLLocationDegrees maxCacheLat = northEast.latitude;
+    CLLocationDegrees minCacheLon = southWest.longitude;
+    CLLocationDegrees maxCacheLon = northEast.longitude;
 
     NSAssert(minCacheZoom <= maxCacheZoom, @"Minimum zoom should be less than or equal to maximum zoom");
     NSAssert(maxCacheLat  >  minCacheLat,  @"Northernmost bounds should exceed southernmost bounds");
     NSAssert(maxCacheLon  >  minCacheLon,  @"Easternmost bounds should exceed westernmost bounds");
 
-    int n, xMin, yMax, xMax, yMin;
+    NSUInteger n, xMin, yMax, xMax, yMin;
 
-    int totalTiles = 0;
+    NSUInteger totalTiles = 0;
 
-    for (int zoom = minCacheZoom; zoom <= maxCacheZoom; zoom++)
+    for (NSUInteger zoom = minCacheZoom; zoom <= maxCacheZoom; zoom++)
     {
         n = pow(2.0, zoom);
         xMin = floor(((minCacheLon + 180.0) / 360.0) * n);
@@ -288,12 +281,37 @@
         totalTiles += (xMax + 1 - xMin) * (yMax + 1 - yMin);
     }
 
+    return totalTiles;
+}
+
+- (void)beginBackgroundCacheForTileSource:(id <RMTileSource>)tileSource southWest:(CLLocationCoordinate2D)southWest northEast:(CLLocationCoordinate2D)northEast minZoom:(NSUInteger)minZoom maxZoom:(NSUInteger)maxZoom
+{
+    if (self.isBackgroundCaching)
+        return;
+
+    _activeTileSource = tileSource;
+
+    _backgroundFetchQueue = [[NSOperationQueue alloc] init];
+    [_backgroundFetchQueue setMaxConcurrentOperationCount:6];
+
+    NSUInteger totalTiles = [self tileCountForSouthWest:southWest northEast:northEast minZoom:minZoom maxZoom:maxZoom];
+
+    NSUInteger minCacheZoom = minZoom;
+    NSUInteger maxCacheZoom = maxZoom;
+
+    CLLocationDegrees minCacheLat = southWest.latitude;
+    CLLocationDegrees maxCacheLat = northEast.latitude;
+    CLLocationDegrees minCacheLon = southWest.longitude;
+    CLLocationDegrees maxCacheLon = northEast.longitude;
+
     if ([_backgroundCacheDelegate respondsToSelector:@selector(tileCache:didBeginBackgroundCacheWithCount:forTileSource:)])
         [_backgroundCacheDelegate tileCache:self didBeginBackgroundCacheWithCount:totalTiles forTileSource:_activeTileSource];
 
-    __block int progTile = 0;
+    NSUInteger n, xMin, yMax, xMax, yMin;
+
+    __block NSUInteger progTile = 0;
 
-    for (int zoom = minCacheZoom; zoom <= maxCacheZoom; zoom++)
+    for (NSUInteger zoom = minCacheZoom; zoom <= maxCacheZoom; zoom++)
     {
         n = pow(2.0, zoom);
         xMin = floor(((minCacheLon + 180.0) / 360.0) * n);
@@ -301,9 +319,9 @@
         xMax = floor(((maxCacheLon + 180.0) / 360.0) * n);
         yMin = floor((1.0 - (logf(tanf(maxCacheLat * M_PI / 180.0) + 1.0 / cosf(maxCacheLat * M_PI / 180.0)) / M_PI)) / 2.0 * n);
 
-        for (int x = xMin; x <= xMax; x++)
+        for (NSUInteger x = xMin; x <= xMax; x++)
         {
-            for (int y = yMin; y <= yMax; y++)
+            for (NSUInteger y = yMin; y <= yMax; y++)
             {
                 RMTileCacheDownloadOperation *operation = [[RMTileCacheDownloadOperation alloc] initWithTile:RMTileMake(x, y, zoom)
                                                                                                 forTileSource:_activeTileSource
diff --git a/MapView/Map/SMCalloutView b/MapView/Map/SMCalloutView
index 05d2211..58ce460 160000
--- a/MapView/Map/SMCalloutView
+++ b/MapView/Map/SMCalloutView
@@ -1 +1 @@
-Subproject commit 05d22115c5702408ecf0c7f2810321dde6a5a26e
+Subproject commit 58ce4607e7967e4d22fec6ef6d202433c44dd3a0
diff --git a/MapView/MapView.xcodeproj/project.pbxproj b/MapView/MapView.xcodeproj/project.pbxproj
index c4871c4..7cba0d4 100755
--- a/MapView/MapView.xcodeproj/project.pbxproj
+++ b/MapView/MapView.xcodeproj/project.pbxproj
@@ -114,8 +114,12 @@
 		DD41960116250ED40049E6BA /* RMTileCacheDownloadOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DD4195FF16250ED40049E6BA /* RMTileCacheDownloadOperation.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		DD41960216250ED40049E6BA /* RMTileCacheDownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DD41960016250ED40049E6BA /* RMTileCacheDownloadOperation.m */; };
 		DD4BE198161CE296003EF677 /* Mapbox.h in Headers */ = {isa = PBXBuildFile; fileRef = DD4BE197161CE296003EF677 /* Mapbox.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		DD5F5F9518C7C03C0085DE98 /* SMClassicCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DD5F5F9318C7C03C0085DE98 /* SMClassicCalloutView.h */; };
+		DD5F5F9618C7C03C0085DE98 /* SMClassicCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DD5F5F9418C7C03C0085DE98 /* SMClassicCalloutView.m */; };
 		DD5FA1EB15E2B020004EB6C5 /* RMLoadingTileView.h in Headers */ = {isa = PBXBuildFile; fileRef = DD5FA1E915E2B020004EB6C5 /* RMLoadingTileView.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		DD5FA1EC15E2B020004EB6C5 /* RMLoadingTileView.m in Sources */ = {isa = PBXBuildFile; fileRef = DD5FA1EA15E2B020004EB6C5 /* RMLoadingTileView.m */; };
+		DD63175F17D1506D008CA79B /* RMGreatCircleAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = DD63175D17D1506D008CA79B /* RMGreatCircleAnnotation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		DD63176017D1506D008CA79B /* RMGreatCircleAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = DD63175E17D1506D008CA79B /* RMGreatCircleAnnotation.m */; };
 		DD63176317D15EB5008CA79B /* RMCircleAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = DD63176117D15EB5008CA79B /* RMCircleAnnotation.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		DD63176417D15EB5008CA79B /* RMCircleAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = DD63176217D15EB5008CA79B /* RMCircleAnnotation.m */; };
 		DD63DD531811AD0B00726E03 /* libGRMustache6-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DD63DD521811AD0B00726E03 /* libGRMustache6-iOS.a */; };
@@ -293,8 +297,12 @@
 		DD4195FF16250ED40049E6BA /* RMTileCacheDownloadOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMTileCacheDownloadOperation.h; sourceTree = "<group>"; };
 		DD41960016250ED40049E6BA /* RMTileCacheDownloadOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMTileCacheDownloadOperation.m; sourceTree = "<group>"; };
 		DD4BE197161CE296003EF677 /* Mapbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mapbox.h; sourceTree = "<group>"; };
+		DD5F5F9318C7C03C0085DE98 /* SMClassicCalloutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SMClassicCalloutView.h; path = SMCalloutView/SMClassicCalloutView.h; sourceTree = "<group>"; };
+		DD5F5F9418C7C03C0085DE98 /* SMClassicCalloutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SMClassicCalloutView.m; path = SMCalloutView/SMClassicCalloutView.m; sourceTree = "<group>"; };
 		DD5FA1E915E2B020004EB6C5 /* RMLoadingTileView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMLoadingTileView.h; sourceTree = "<group>"; };
 		DD5FA1EA15E2B020004EB6C5 /* RMLoadingTileView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMLoadingTileView.m; sourceTree = "<group>"; };
+		DD63175D17D1506D008CA79B /* RMGreatCircleAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMGreatCircleAnnotation.h; sourceTree = "<group>"; };
+		DD63175E17D1506D008CA79B /* RMGreatCircleAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMGreatCircleAnnotation.m; sourceTree = "<group>"; };
 		DD63176117D15EB5008CA79B /* RMCircleAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMCircleAnnotation.h; sourceTree = "<group>"; };
 		DD63176217D15EB5008CA79B /* RMCircleAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMCircleAnnotation.m; sourceTree = "<group>"; };
 		DD63DD521811AD0B00726E03 /* libGRMustache6-iOS.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libGRMustache6-iOS.a"; path = "GRMustache/lib/libGRMustache6-iOS.a"; sourceTree = "<group>"; };
@@ -543,6 +551,8 @@
 				DDE357F716522CD8001DB842 /* RMPolygonAnnotation.m */,
 				DD63176117D15EB5008CA79B /* RMCircleAnnotation.h */,
 				DD63176217D15EB5008CA79B /* RMCircleAnnotation.m */,
+				DD63175D17D1506D008CA79B /* RMGreatCircleAnnotation.h */,
+				DD63175E17D1506D008CA79B /* RMGreatCircleAnnotation.m */,
 				16FAB66213E03D55002F4E1C /* RMQuadTree.h */,
 				16FAB66313E03D55002F4E1C /* RMQuadTree.m */,
 				B86F26AC0E87442C007A3773 /* RMMapLayer.h */,
@@ -605,6 +615,8 @@
 			children = (
 				DD1E3C6C161F954F004FC649 /* SMCalloutView.h */,
 				DD1E3C6D161F954F004FC649 /* SMCalloutView.m */,
+				DD5F5F9318C7C03C0085DE98 /* SMClassicCalloutView.h */,
+				DD5F5F9418C7C03C0085DE98 /* SMClassicCalloutView.m */,
 			);
 			name = SMCallout;
 			path = Map;
@@ -755,6 +767,7 @@
 				DDE357F816522CD8001DB842 /* RMPolygonAnnotation.h in Headers */,
 				DD1985C1165C5F6400DF667F /* RMTileMillSource.h in Headers */,
 				DD63176317D15EB5008CA79B /* RMCircleAnnotation.h in Headers */,
+				DD63175F17D1506D008CA79B /* RMGreatCircleAnnotation.h in Headers */,
 				B8C974220E8A19B2007D16AD /* RMProjection.h in Headers */,
 				B8C974260E8A19B2007D16AD /* RMTile.h in Headers */,
 				B8C974270E8A19B2007D16AD /* RMPixel.h in Headers */,
@@ -763,6 +776,7 @@
 				23A0AAEB0EB90AA6003A4521 /* RMFoundation.h in Headers */,
 				96492C400FA8AD3400EBA6D2 /* RMGlobalConstants.h in Headers */,
 				B1EB26C610B5D8E6009F8658 /* RMNotifications.h in Headers */,
+				DD5F5F9518C7C03C0085DE98 /* SMClassicCalloutView.h in Headers */,
 				D1437B37122869E400888DAE /* RMDBMapSource.h in Headers */,
 				1606C9FE13D86BA400547581 /* RMOpenCycleMapSource.h in Headers */,
 				16FAB66413E03D55002F4E1C /* RMQuadTree.h in Headers */,
@@ -919,6 +933,7 @@
 				B8C9746C0E8A1A50007D16AD /* RMMapView.m in Sources */,
 				B8F3FC610EA2B382004D8F85 /* RMMapLayer.m in Sources */,
 				B8F3FC650EA2E792004D8F85 /* RMMarker.m in Sources */,
+				DD5F5F9618C7C03C0085DE98 /* SMClassicCalloutView.m in Sources */,
 				B8474BA50EB40094006A0BC1 /* RMDatabaseCache.m in Sources */,
 				126693040EB76C0B00E002D5 /* RMConfiguration.m in Sources */,
 				23A0AAE90EB90A99003A4521 /* RMFoundation.c in Sources */,
@@ -935,6 +950,7 @@
 				16128CF6148D295300C23C0E /* RMOpenSeaMapSource.m in Sources */,
 				DD2B374614CF8041008DE8CB /* FMDatabase.m in Sources */,
 				DD2B374814CF8041008DE8CB /* FMDatabaseAdditions.m in Sources */,
+				DD63176017D1506D008CA79B /* RMGreatCircleAnnotation.m in Sources */,
 				DD2B374A14CF8041008DE8CB /* FMResultSet.m in Sources */,
 				DD2B375014CF814F008DE8CB /* FMDatabasePool.m in Sources */,
 				DD2B375214CF814F008DE8CB /* FMDatabaseQueue.m in Sources */,
@@ -1063,10 +1079,6 @@
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 5.0;
 				ONLY_ACTIVE_ARCH = YES;
-				OTHER_CFLAGS = (
-					"-O",
-					"-Wuninitialized",
-				);
 				OTHER_LDFLAGS = "";
 				RUN_CLANG_STATIC_ANALYZER = NO;
 				SDKROOT = iphoneos;
diff --git a/Mapbox.podspec b/Mapbox.podspec
index 2a07ecd..678df45 100644
--- a/Mapbox.podspec
+++ b/Mapbox.podspec
@@ -7,7 +7,7 @@ Pod::Spec.new do |m|
   m.description = 'Open source alternative to MapKit supporting custom tile sources, offline use, and complete cache control.'
   m.homepage    = 'http://mapbox.com/mobile'
   m.license     = 'BSD'
-  m.author      = { 'Mapbox' => 'ios@mapbox.com' }
+  m.author      = { 'Mapbox' => 'mobile@mapbox.com' }
   m.screenshot  = 'https://raw.github.com/mapbox/mapbox-ios-sdk/packaging/screenshot.png'
 
   m.source = { :git => 'https://github.com/mapbox/mapbox-ios-sdk.git', :tag => m.version.to_s }
@@ -31,10 +31,12 @@ Pod::Spec.new do |m|
 
   m.xcconfig = { 'OTHER_LDFLAGS' => '-ObjC', 'LIBRARY_SEARCH_PATHS' => '"${PODS_ROOT}/Mapbox/Proj4"' }
 
-  m.preserve_paths = 'Proj4/libProj4.a', 'MapView/MapView.xcodeproj', 'MapView/Map/Resources'
+  m.preserve_paths = 'MapView/MapView.xcodeproj', 'MapView/Map/Resources'
+
+  m.vendored_libraries = 'Proj4/libProj4.a'
 
   m.dependency 'FMDB', '2.0'
   m.dependency 'GRMustache', '6.8.3'
-  m.dependency 'SMCalloutView', '1.1'
+  m.dependency 'SMCalloutView', '2.0'
 
 end