Authored by Thomas Rasch

o Implemented the missingTilesDepth feature (issue #73), which tries to take mis…

…sing tiles from lower zoom levels
... ... @@ -27,7 +27,6 @@
#import "RMAbstractWebMapSource.h"
#import "RMTileCache.h"
#import "RMTileImage.h"
@implementation RMAbstractWebMapSource
... ... @@ -148,9 +147,6 @@
[[NSNotificationCenter defaultCenter] postNotificationName:RMTileRetrieved object:[NSNumber numberWithUnsignedLongLong:RMTileKey(tile)]];
});
if (!image)
return [RMTileImage errorTile];
return image;
}
... ...
... ... @@ -10,6 +10,7 @@
#import "RMMapView.h"
#import "RMTileSource.h"
#import "RMTileImage.h"
@interface RMMapOverlayView ()
... ... @@ -132,6 +133,50 @@
UIGraphicsPushContext(context);
UIImage *tileImage = [[mapView tileSource] imageForTile:RMTileMake(x, y, zoom) inCache:[mapView tileCache]];
if ( ! tileImage)
{
if (mapView.missingTilesDepth == 0)
{
tileImage = [RMTileImage errorTile];
}
else
{
NSUInteger currentTileDepth = 1, currentZoom = zoom - currentTileDepth;
// tries to return lower zoom level tiles if a tile cannot be found
while ( !tileImage && currentZoom >= mapView.tileSource.minZoom && currentTileDepth <= mapView.missingTilesDepth)
{
float nextX = x / powf(2.0, (float)currentTileDepth),
nextY = y / powf(2.0, (float)currentTileDepth);
float nextTileX = floor(nextX),
nextTileY = floor(nextY);
tileImage = [[mapView tileSource] imageForTile:RMTileMake((int)nextTileX, (int)nextTileY, currentZoom) inCache:[mapView tileCache]];
if (tileImage)
{
// crop
float cropSize = 1.0 / powf(2.0, (float)currentTileDepth);
CGRect cropBounds = CGRectMake(tileImage.size.width * (nextX - nextTileX),
tileImage.size.height * (nextY - nextTileY),
tileImage.size.width * cropSize,
tileImage.size.height * cropSize);
CGImageRef imageRef = CGImageCreateWithImageInRect([tileImage CGImage], cropBounds);
tileImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
break;
}
currentTileDepth++;
currentZoom = zoom - currentTileDepth;
}
}
}
[tileImage drawInRect:rect];
UIGraphicsPopContext();
... ...
... ... @@ -69,12 +69,9 @@ typedef enum {
{
id <RMMapViewDelegate> delegate;
/// projection objects to convert from latitude/longitude to meters,
/// from projected meters to tile coordinates
RMProjection *projection;
RMFractalTileProjection *mercatorToTileProjection;
/// subview for the background image displayed while tiles are loading. Set its contents by providing your own "loading.png".
UIView *backgroundView;
RMMapScrollView *mapScrollView;
RMMapTiledLayerView *tiledLayerView;
... ... @@ -92,10 +89,10 @@ typedef enum {
id <RMTileSource> tileSource;
RMTileCache *tileCache; // Generic tile cache
/// minimum and maximum zoom number allowed for the view. #minZoom and #maxZoom must be within the limits of #tileSource but can be stricter; they are clamped to tilesource limits if needed.
float minZoom, maxZoom, zoom;
float screenScale;
NSUInteger missingTilesDepth;
NSUInteger boundingMask;
}
... ... @@ -117,27 +114,34 @@ typedef enum {
@property (nonatomic, readonly) double scaledMetersPerPixel;
@property (nonatomic, readonly) double scaleDenominator; /// The denominator in a cartographic scale like 1/24000, 1/50000, 1/2000000.
@property (nonatomic, readonly) float screenScale;
@property (nonatomic, assign) NSUInteger boundingMask;
@property (nonatomic, assign) BOOL adjustTilesForRetinaDisplay;
@property (nonatomic, readonly) float adjustedZoomForRetinaDisplay; // takes adjustTilesForRetinaDisplay and screen scale into account
@property (nonatomic, assign) float zoom; /// zoom level is clamped to range (minZoom, maxZoom)
/// minimum and maximum zoom number allowed for the view. #minZoom and #maxZoom must be within the limits of #tileSource but can be stricter; they are clamped to tilesource limits (minZoom, maxZoom) if needed.
@property (nonatomic, assign) float zoom;
@property (nonatomic, assign) float minZoom;
@property (nonatomic, assign) float maxZoom;
/// take missing tiles from lower zoom levels, up to #missingTilesDepth zoom levels (defaults to 0, which disables this feature)
@property (nonatomic, assign) NSUInteger missingTilesDepth;
@property (nonatomic, assign) NSUInteger boundingMask;
@property (nonatomic, retain) RMQuadTree *quadTree;
@property (nonatomic, assign) BOOL enableClustering;
@property (nonatomic, assign) BOOL positionClusterMarkersAtTheGravityCenter;
@property (nonatomic, assign) CGSize clusterMarkerSize;
@property (nonatomic, assign) CGSize clusterAreaSize;
/// projection objects to convert from latitude/longitude to meters, from projected meters to tile coordinates
@property (nonatomic, readonly) RMProjection *projection;
@property (nonatomic, readonly) id <RMMercatorToTileProjection> mercatorToTileProjection;
@property (nonatomic, retain) id <RMTileSource> tileSource;
@property (nonatomic, retain) RMTileCache *tileCache;
/// subview for the background image displayed while tiles are loading.
@property (nonatomic, retain) UIView *backgroundView;
#pragma mark -
... ...
... ... @@ -117,6 +117,7 @@
@synthesize quadTree;
@synthesize enableClustering, positionClusterMarkersAtTheGravityCenter, clusterMarkerSize, clusterAreaSize;
@synthesize adjustTilesForRetinaDisplay;
@synthesize missingTilesDepth;
#pragma mark -
#pragma mark Initialization
... ... @@ -144,6 +145,7 @@
boundingMask = RMMapMinWidthBound;
adjustTilesForRetinaDisplay = NO;
missingTilesDepth = 0;
annotations = [NSMutableSet new];
visibleAnnotations = [NSMutableSet new];
... ...