Authored by Justin R. Miller

fixes #12: remove boundingMask & make minZoom automatic per frame

@@ -49,13 +49,6 @@ @@ -49,13 +49,6 @@
49 @class RMQuadTree; 49 @class RMQuadTree;
50 @class RMUserLocation; 50 @class RMUserLocation;
51 51
52 -// constants for boundingMask  
53 -enum : NSUInteger {  
54 - RMMapNoMinBound = 0, // Map can be zoomed out past view limits  
55 - RMMapMinHeightBound = 1, // Minimum map height when zooming out restricted to view height  
56 - RMMapMinWidthBound = 2 // Minimum map width when zooming out restricted to view width (default)  
57 -};  
58 -  
59 // constants for the scrollview deceleration mode 52 // constants for the scrollview deceleration mode
60 typedef enum : NSUInteger { 53 typedef enum : NSUInteger {
61 RMMapDecelerationNormal = 0, 54 RMMapDecelerationNormal = 0,
@@ -129,8 +122,6 @@ typedef enum : NSUInteger { @@ -129,8 +122,6 @@ typedef enum : NSUInteger {
129 /** Take missing tiles from lower-numbered zoom levels, up to a given number of zoom levels. This can be used in order to increase perceived tile load performance or to allow zooming in beyond levels supported natively by a given tile source. Defaults to 1. */ 122 /** Take missing tiles from lower-numbered zoom levels, up to a given number of zoom levels. This can be used in order to increase perceived tile load performance or to allow zooming in beyond levels supported natively by a given tile source. Defaults to 1. */
130 @property (nonatomic, assign) NSUInteger missingTilesDepth; 123 @property (nonatomic, assign) NSUInteger missingTilesDepth;
131 124
132 -@property (nonatomic, assign) NSUInteger boundingMask;  
133 -  
134 /** A custom, static view to use behind the map tiles. The default behavior is to use grid imagery that moves with map panning like MapKit. */ 125 /** A custom, static view to use behind the map tiles. The default behavior is to use grid imagery that moves with map panning like MapKit. */
135 @property (nonatomic, retain) UIView *backgroundView; 126 @property (nonatomic, retain) UIView *backgroundView;
136 127
@@ -85,8 +85,6 @@ @@ -85,8 +85,6 @@
85 - (void)correctPositionOfAllAnnotationsIncludingInvisibles:(BOOL)correctAllLayers animated:(BOOL)animated; 85 - (void)correctPositionOfAllAnnotationsIncludingInvisibles:(BOOL)correctAllLayers animated:(BOOL)animated;
86 - (void)correctOrderingOfAllAnnotations; 86 - (void)correctOrderingOfAllAnnotations;
87 87
88 -- (void)correctMinZoomScaleForBoundingMask;  
89 -  
90 - (void)updateHeadingForDeviceOrientation; 88 - (void)updateHeadingForDeviceOrientation;
91 89
92 @end 90 @end
@@ -192,11 +190,12 @@ @@ -192,11 +190,12 @@
192 190
193 RMAnnotation *_currentAnnotation; 191 RMAnnotation *_currentAnnotation;
194 SMCalloutView *_currentCallout; 192 SMCalloutView *_currentCallout;
  193 +
  194 + BOOL _rotateAtMinZoom;
195 } 195 }
196 196
197 @synthesize decelerationMode = _decelerationMode; 197 @synthesize decelerationMode = _decelerationMode;
198 198
199 -@synthesize boundingMask = _boundingMask;  
200 @synthesize zoomingInPivotsAroundCenter = _zoomingInPivotsAroundCenter; 199 @synthesize zoomingInPivotsAroundCenter = _zoomingInPivotsAroundCenter;
201 @synthesize minZoom = _minZoom, maxZoom = _maxZoom; 200 @synthesize minZoom = _minZoom, maxZoom = _maxZoom;
202 @synthesize screenScale = _screenScale; 201 @synthesize screenScale = _screenScale;
@@ -246,7 +245,6 @@ @@ -246,7 +245,6 @@
246 245
247 _screenScale = [UIScreen mainScreen].scale; 246 _screenScale = [UIScreen mainScreen].scale;
248 247
249 - _boundingMask = RMMapMinWidthBound;  
250 _adjustTilesForRetinaDisplay = NO; 248 _adjustTilesForRetinaDisplay = NO;
251 _missingTilesDepth = 1; 249 _missingTilesDepth = 1;
252 _debugTiles = NO; 250 _debugTiles = NO;
@@ -288,7 +286,6 @@ @@ -288,7 +286,6 @@
288 [self setCenterCoordinate:initialCenterCoordinate animated:NO]; 286 [self setCenterCoordinate:initialCenterCoordinate animated:NO];
289 287
290 [self setDecelerationMode:RMMapDecelerationFast]; 288 [self setDecelerationMode:RMMapDecelerationFast];
291 - [self setBoundingMask:RMMapMinHeightBound];  
292 289
293 self.showLogoBug = YES; 290 self.showLogoBug = YES;
294 291
@@ -393,7 +390,8 @@ @@ -393,7 +390,8 @@
393 [self setCenterProjectedPoint:centerPoint animated:NO]; 390 [self setCenterProjectedPoint:centerPoint animated:NO];
394 391
395 [self correctPositionOfAllAnnotations]; 392 [self correctPositionOfAllAnnotations];
396 - [self correctMinZoomScaleForBoundingMask]; 393 +
  394 + self.minZoom = 0; // force new minZoom calculation
397 } 395 }
398 } 396 }
399 397
@@ -466,10 +464,17 @@ @@ -466,10 +464,17 @@
466 // 464 //
467 if (self.userTrackingMode == RMUserTrackingModeFollowWithHeading) 465 if (self.userTrackingMode == RMUserTrackingModeFollowWithHeading)
468 [self locationManager:_locationManager didUpdateHeading:_locationManager.heading]; 466 [self locationManager:_locationManager didUpdateHeading:_locationManager.heading];
  467 +
  468 + // fix UIScrollView artifacts from rotation at minZoomScale
  469 + //
  470 + _rotateAtMinZoom = fabs(self.zoom - self.minZoom) < 0.1;
469 } 471 }
470 472
471 - (void)handleDidChangeOrientationNotification:(NSNotification *)notification 473 - (void)handleDidChangeOrientationNotification:(NSNotification *)notification
472 { 474 {
  475 + if (_rotateAtMinZoom)
  476 + [_mapScrollView setZoomScale:_mapScrollView.minimumZoomScale animated:YES];
  477 +
473 [self updateHeadingForDeviceOrientation]; 478 [self updateHeadingForDeviceOrientation];
474 } 479 }
475 480
@@ -833,31 +838,6 @@ @@ -833,31 +838,6 @@
833 #pragma mark - 838 #pragma mark -
834 #pragma mark Zoom 839 #pragma mark Zoom
835 840
836 -- (void)setBoundingMask:(NSUInteger)mask  
837 -{  
838 - _boundingMask = mask;  
839 -  
840 - [self correctMinZoomScaleForBoundingMask];  
841 -}  
842 -  
843 -- (void)correctMinZoomScaleForBoundingMask  
844 -{  
845 - if (self.boundingMask != RMMapNoMinBound)  
846 - {  
847 - if ([_tiledLayersSuperview.subviews count] == 0)  
848 - return;  
849 -  
850 - CGFloat newMinZoomScale = (self.boundingMask == RMMapMinWidthBound ? self.bounds.size.width : self.bounds.size.height) / ((CATiledLayer *)((RMMapTiledLayerView *)[_tiledLayersSuperview.subviews objectAtIndex:0]).layer).tileSize.width;  
851 -  
852 - if (_mapScrollView.minimumZoomScale > 0 && newMinZoomScale > _mapScrollView.minimumZoomScale)  
853 - {  
854 - RMLog(@"clamping min zoom of %f to %f due to %@", log2f(_mapScrollView.minimumZoomScale), log2f(newMinZoomScale), (self.boundingMask == RMMapMinWidthBound ? @"RMMapMinWidthBound" : @"RMMapMinHeightBound"));  
855 -  
856 - _mapScrollView.minimumZoomScale = newMinZoomScale;  
857 - }  
858 - }  
859 -}  
860 -  
861 - (RMProjectedRect)projectedBounds 841 - (RMProjectedRect)projectedBounds
862 { 842 {
863 CGPoint bottomLeft = CGPointMake(_mapScrollView.contentOffset.x, _mapScrollView.contentSize.height - (_mapScrollView.contentOffset.y + _mapScrollView.bounds.size.height)); 843 CGPoint bottomLeft = CGPointMake(_mapScrollView.contentOffset.x, _mapScrollView.contentSize.height - (_mapScrollView.contentOffset.y + _mapScrollView.bounds.size.height));
@@ -895,48 +875,6 @@ @@ -895,48 +875,6 @@
895 [_mapScrollView zoomToRect:zoomRect animated:animated]; 875 [_mapScrollView zoomToRect:zoomRect animated:animated];
896 } 876 }
897 877
898 -- (float)adjustedZoomForCurrentBoundingMask:(float)zoomFactor  
899 -{  
900 - if (_boundingMask == RMMapNoMinBound)  
901 - return zoomFactor;  
902 -  
903 - double newMetersPerPixel = _metersPerPixel / zoomFactor;  
904 -  
905 - RMProjectedRect mercatorBounds = [_projection planetBounds];  
906 -  
907 - // Check for MinWidthBound  
908 - if (_boundingMask & RMMapMinWidthBound)  
909 - {  
910 - double newMapContentsWidth = mercatorBounds.size.width / newMetersPerPixel;  
911 - double screenBoundsWidth = [self bounds].size.width;  
912 - double mapContentWidth;  
913 -  
914 - if (newMapContentsWidth < screenBoundsWidth)  
915 - {  
916 - // Calculate new zoom facter so that it does not shrink the map any further.  
917 - mapContentWidth = mercatorBounds.size.width / _metersPerPixel;  
918 - zoomFactor = screenBoundsWidth / mapContentWidth;  
919 - }  
920 - }  
921 -  
922 - // Check for MinHeightBound  
923 - if (_boundingMask & RMMapMinHeightBound)  
924 - {  
925 - double newMapContentsHeight = mercatorBounds.size.height / newMetersPerPixel;  
926 - double screenBoundsHeight = [self bounds].size.height;  
927 - double mapContentHeight;  
928 -  
929 - if (newMapContentsHeight < screenBoundsHeight)  
930 - {  
931 - // Calculate new zoom facter so that it does not shrink the map any further.  
932 - mapContentHeight = mercatorBounds.size.height / _metersPerPixel;  
933 - zoomFactor = screenBoundsHeight / mapContentHeight;  
934 - }  
935 - }  
936 -  
937 - return zoomFactor;  
938 -}  
939 -  
940 - (BOOL)shouldZoomToTargetZoom:(float)targetZoom withZoomFactor:(float)zoomFactor 878 - (BOOL)shouldZoomToTargetZoom:(float)targetZoom withZoomFactor:(float)zoomFactor
941 { 879 {
942 // bools for syntactical sugar to understand the logic in the if statement below 880 // bools for syntactical sugar to understand the logic in the if statement below
@@ -978,7 +916,6 @@ @@ -978,7 +916,6 @@
978 if (![self tileSourceBoundsContainScreenPoint:pivot]) 916 if (![self tileSourceBoundsContainScreenPoint:pivot])
979 return; 917 return;
980 918
981 - zoomFactor = [self adjustedZoomForCurrentBoundingMask:zoomFactor];  
982 float zoomDelta = log2f(zoomFactor); 919 float zoomDelta = log2f(zoomFactor);
983 float targetZoom = zoomDelta + [self zoom]; 920 float targetZoom = zoomDelta + [self zoom];
984 921
@@ -1331,6 +1268,12 @@ @@ -1331,6 +1268,12 @@
1331 1268
1332 _mapScrollViewIsZooming = NO; 1269 _mapScrollViewIsZooming = NO;
1333 1270
  1271 + // slight jiggle fixes problems with UIScrollView
  1272 + // briefly allowing zoom beyond min
  1273 + //
  1274 + [self moveBy:CGSizeMake(-1, -1)];
  1275 + [self moveBy:CGSizeMake( 1, 1)];
  1276 +
1334 [self correctPositionOfAllAnnotations]; 1277 [self correctPositionOfAllAnnotations];
1335 1278
1336 if (_loadingTileView) 1279 if (_loadingTileView)
@@ -2275,16 +2218,18 @@ @@ -2275,16 +2218,18 @@
2275 2218
2276 - (void)setMinZoom:(float)newMinZoom 2219 - (void)setMinZoom:(float)newMinZoom
2277 { 2220 {
2278 - if (newMinZoom < 0.0)  
2279 - newMinZoom = 0.0; 2221 + float boundingDimension = fmaxf(self.bounds.size.width, self.bounds.size.height);
  2222 + float tileSideLength = _tileSourcesContainer.tileSideLength;
  2223 + float clampedMinZoom = log2(boundingDimension / tileSideLength);
  2224 +
  2225 + if (newMinZoom < clampedMinZoom)
  2226 + newMinZoom = clampedMinZoom;
2280 2227
2281 _minZoom = newMinZoom; 2228 _minZoom = newMinZoom;
2282 2229
2283 // RMLog(@"New minZoom:%f", newMinZoom); 2230 // RMLog(@"New minZoom:%f", newMinZoom);
2284 2231
2285 _mapScrollView.minimumZoomScale = exp2f(newMinZoom); 2232 _mapScrollView.minimumZoomScale = exp2f(newMinZoom);
2286 -  
2287 - [self correctMinZoomScaleForBoundingMask];  
2288 } 2233 }
2289 2234
2290 - (float)tileSourcesMinZoom 2235 - (float)tileSourcesMinZoom