Authored by Justin R. Miller

merged upstream develop

... ... @@ -36,6 +36,7 @@
#import "RMCacheObject.h"
#import "RMCircle.h"
#import "RMConfiguration.h"
#import "RMCompositeSource.h"
#import "RMCoordinateGridSource.h"
#import "RMDBMapSource.h"
#import "RMDatabaseCache.h"
... ...
... ... @@ -35,7 +35,7 @@
RMFractalTileProjection *_tileProjection;
}
@synthesize minZoom = _minZoom, maxZoom = _maxZoom;
@synthesize minZoom = _minZoom, maxZoom = _maxZoom, cacheable = _cacheable, opaque = _opaque;
- (id)init
{
... ... @@ -48,6 +48,9 @@
self.minZoom = kDefaultMinTileZoom;
self.maxZoom = kDefaultMaxTileZoom;
self.cacheable = YES;
self.opaque = YES;
return self;
}
... ... @@ -69,6 +72,11 @@
userInfo:nil];
}
- (BOOL)tileSourceHasTile:(RMTile)tile
{
return YES;
}
- (void)cancelAllDownloads
{
}
... ...
... ... @@ -62,10 +62,19 @@
__block UIImage *image = nil;
tile = [[self mercatorToTileProjection] normaliseTile:tile];
image = [tileCache cachedImage:tile withCacheKey:[self uniqueTilecacheKey]];
if (image)
return image;
// Return NSNull here so that the RMMapTiledLayerView will try to
// fetch another tile if missingTilesDepth > 0
if ( ! [self tileSourceHasTile:tile])
return (UIImage *)[NSNull null];
if (self.isCacheable)
{
image = [tileCache cachedImage:tile withCacheKey:[self uniqueTilecacheKey]];
if (image)
return image;
}
dispatch_async(dispatch_get_main_queue(), ^(void)
{
... ... @@ -155,7 +164,7 @@
}
}
if (image)
if (image && self.isCacheable)
[tileCache addImage:image forTile:tile withCacheKey:[self uniqueTilecacheKey]];
[tileCache release];
... ...
... ... @@ -70,8 +70,11 @@
- (void)touch
{
[_timestamp autorelease];
_timestamp = [NSDate new];
@synchronized (self)
{
[_timestamp autorelease];
_timestamp = [NSDate new];
}
}
- (NSString *)description
... ...
//
// RMCompositeSource.h
// MapView
//
// Copyright (c) 2008-2012, 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 "RMAbstractMercatorTileSource.h"
@interface RMCompositeSource : RMAbstractMercatorTileSource
- (id)initWithTileSources:(NSArray *)tileSources tileCacheKey:(NSString *)tileCacheKey;
@property (nonatomic, readonly) NSArray *tileSources;
@end
... ...
//
// RMCompositeSource.m
// MapView
//
// Copyright (c) 2008-2012, 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 "RMCompositeSource.h"
#import "RMTileCache.h"
@implementation RMCompositeSource
{
NSArray *_tileSources;
NSString *_uniqueTilecacheKey;
}
- (id)initWithTileSources:(NSArray *)tileSources tileCacheKey:(NSString *)tileCacheKey
{
if (!(self = [super init]))
return nil;
NSAssert(tileSources != nil && [tileSources count], @"Empty host parameter not allowed");
_tileSources = [tileSources copy];
if (tileCacheKey)
{
_uniqueTilecacheKey = [tileCacheKey retain];
}
else
{
self.cacheable = NO;
_uniqueTilecacheKey = nil;
}
float tileSourcesMinZoom = FLT_MAX, tileSourcesMaxZoom = FLT_MIN;
BOOL tileSourcesAreOpaque = YES;
for (id <RMTileSource> currentTileSource in _tileSources)
{
tileSourcesMinZoom = MIN(tileSourcesMinZoom, currentTileSource.minZoom);
tileSourcesMaxZoom = MAX(tileSourcesMaxZoom, currentTileSource.maxZoom);
if ( ! currentTileSource.isOpaque)
tileSourcesAreOpaque = NO;
}
self.minZoom = tileSourcesMinZoom;
self.maxZoom = tileSourcesMaxZoom;
self.opaque = tileSourcesAreOpaque;
return self;
}
- (void)dealloc
{
[_uniqueTilecacheKey release]; _uniqueTilecacheKey = nil;
[_tileSources release]; _tileSources = nil;
[super dealloc];
}
- (NSArray *)tileSources
{
return [[_tileSources copy] autorelease];
}
- (NSString *)uniqueTilecacheKey
{
return _uniqueTilecacheKey;
}
- (NSString *)shortName
{
return @"Generic Map Source";
}
- (NSString *)longDescription
{
return @"Generic Map Source";
}
- (NSString *)shortAttribution
{
return @"n/a";
}
- (NSString *)longAttribution
{
return @"n/a";
}
- (UIImage *)imageForTile:(RMTile)tile inCache:(RMTileCache *)tileCache
{
UIImage *image = nil;
tile = [[self mercatorToTileProjection] normaliseTile:tile];
if (self.isCacheable)
{
image = [tileCache cachedImage:tile withCacheKey:[self uniqueTilecacheKey]];
if (image)
return image;
}
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[[NSNotificationCenter defaultCenter] postNotificationName:RMTileRequested object:[NSNumber numberWithUnsignedLongLong:RMTileKey(tile)]];
});
[tileCache retain];
NSMutableArray *tileImages = [NSMutableArray arrayWithCapacity:[_tileSources count]];
for (NSUInteger p = 0; p < [_tileSources count]; ++p)
[tileImages addObject:[NSNull null]];
for (NSInteger u = [_tileSources count]-1; u >=0 ; --u)
{
id <RMTileSource> tileSource = [_tileSources objectAtIndex:u];
if (tile.zoom < tileSource.minZoom || tile.zoom > tileSource.maxZoom || ![tileSource tileSourceHasTile:tile])
continue;
UIImage *tileImage = [tileSource imageForTile:tile inCache:tileCache];
if (tileImage)
{
[tileImages replaceObjectAtIndex:u withObject:tileImage];
if (tileSource.isOpaque)
break;
}
}
// composite the collected images together
//
for (UIImage *tileImage in tileImages)
{
if ( ! [tileImage isKindOfClass:[UIImage class]])
continue;
if (image != nil)
{
UIGraphicsBeginImageContext(image.size);
[image drawAtPoint:CGPointMake(0,0)];
[tileImage drawAtPoint:CGPointMake(0,0)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
else
{
image = tileImage;
}
}
if (image && self.isCacheable)
[tileCache addImage:image forTile:tile withCacheKey:[self uniqueTilecacheKey]];
[tileCache release];
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[[NSNotificationCenter defaultCenter] postNotificationName:RMTileRetrieved object:[NSNumber numberWithUnsignedLongLong:RMTileKey(tile)]];
});
return image;
}
@end
... ...
... ... @@ -81,7 +81,7 @@ static double coordinateGridSpacingDecimal[19] = {
@synthesize gridColor = _gridColor;
@synthesize gridLineWidth = _gridLineWidth;
@synthesize gridLabelInterval = _gridLabelInterval;
@synthesize gridMode = _labelingMode;
@synthesize gridMode = _gridMode;
@synthesize minorLabelColor = _minorLabelColor;
@synthesize minorLabelFont = _minorLabelFont;
@synthesize majorLabelColor = _majorLabelColor;
... ... @@ -93,14 +93,16 @@ static double coordinateGridSpacingDecimal[19] = {
return nil;
self.minZoom = 5;
self.maxZoom = 18;
self.maxZoom = 17;
self.opaque = NO;
self.gridColor = [UIColor colorWithWhite:0.1 alpha:0.6];
self.gridLineWidth = 2.0;
self.gridLabelInterval = 1;
self.gridMode = GridModeGeographicDecimal;
self.minorLabelColor = self.majorLabelColor = [UIColor colorWithWhite:0.1 alpha:0.6];
self.minorLabelColor = self.majorLabelColor = [UIColor colorWithWhite:0.1 alpha:0.7];
self.minorLabelFont = [UIFont boldSystemFontOfSize:14.0];
self.majorLabelFont = [UIFont boldSystemFontOfSize:11.0];
... ... @@ -314,7 +316,29 @@ static double coordinateGridSpacingDecimal[19] = {
- (NSString *)uniqueTilecacheKey
{
return @"RMCoordinateGrid";
NSString *tileCacheKey = nil;
switch (self.gridMode)
{
case GridModeGeographic: {
tileCacheKey = @"RMCoordinateGridGeographic";
break;
}
case GridModeGeographicDecimal:
{
tileCacheKey = @"RMCoordinateGridDecimal";
break;
}
case GridModeUTM: {
tileCacheKey = @"RMCoordinateGridUTM";
break;
}
}
if ( ! tileCacheKey)
tileCacheKey = @"RMCoordinateGrid";
return tileCacheKey;
}
- (NSString *)shortName
... ...
... ... @@ -203,10 +203,14 @@
__block UIImage *image = nil;
tile = [[self mercatorToTileProjection] normaliseTile:tile];
image = [tileCache cachedImage:tile withCacheKey:[self uniqueTilecacheKey]];
if (image)
return image;
if (self.isCacheable)
{
image = [tileCache cachedImage:tile withCacheKey:[self uniqueTilecacheKey]];
if (image)
return image;
}
// get the unique key for the tile
NSNumber *key = [NSNumber numberWithLongLong:RMTileKey(tile)];
... ... @@ -227,7 +231,7 @@
[result close];
}];
if (image)
if (image && self.isCacheable)
[tileCache addImage:image forTile:tile withCacheKey:[self uniqueTilecacheKey]];
return image;
... ...
... ... @@ -360,6 +360,27 @@
}];
}
- (void)removeAllCachedImagesForCacheKey:(NSString *)cacheKey
{
RMLog(@"removing tiles for key '%@' from the db cache", cacheKey);
[_writeQueue addOperationWithBlock:^{
[_writeQueueLock lock];
[_queue inDatabase:^(FMDatabase *db)
{
BOOL result = [db executeUpdate:@"DELETE FROM ZCACHE WHERE cache_key = ?", cacheKey];
if (result == NO)
RMLog(@"Error purging cache");
}];
[_writeQueueLock unlock];
_tileCount = [self countTiles];
}];
}
- (void)touchTile:(RMTile)tile withKey:(NSString *)cacheKey
{
[_writeQueue addOperationWithBlock:^{
... ...
... ... @@ -15,6 +15,8 @@
#import "RMMBTilesSource.h"
#import "RMDBMapSource.h"
#define IS_VALID_TILE_IMAGE(image) (image != nil && [image isKindOfClass:[UIImage class]])
@implementation RMMapTiledLayerView
{
RMMapView *_mapView;
... ... @@ -47,8 +49,8 @@
self.useSnapshotRenderer = NO;
CATiledLayer *tiledLayer = [self tiledLayer];
size_t levelsOf2xMagnification = _mapView.tileSourcesContainer.maxZoom;
if (_mapView.adjustTilesForRetinaDisplay) levelsOf2xMagnification += 1;
size_t levelsOf2xMagnification = _mapView.tileSourcesMaxZoom;
if (_mapView.adjustTilesForRetinaDisplay && _mapView.screenScale > 1.0) levelsOf2xMagnification += 1;
tiledLayer.levelsOfDetail = levelsOf2xMagnification;
tiledLayer.levelsOfDetailBias = levelsOf2xMagnification;
... ... @@ -100,7 +102,9 @@
for (int y=y1; y<=y2; ++y)
{
UIImage *tileImage = [_tileSource imageForTile:RMTileMake(x, y, zoom) inCache:[_mapView tileCache]];
[tileImage drawInRect:CGRectMake(x * rectSize, y * rectSize, rectSize, rectSize)];
if (IS_VALID_TILE_IMAGE(tileImage))
[tileImage drawInRect:CGRectMake(x * rectSize, y * rectSize, rectSize, rectSize)];
}
}
... ... @@ -112,6 +116,13 @@
int x = floor(rect.origin.x / rect.size.width),
y = floor(fabs(rect.origin.y / rect.size.height));
if (_mapView.adjustTilesForRetinaDisplay && _mapView.screenScale > 1.0)
{
zoom--;
x >>= 1;
y >>= 1;
}
// NSLog(@"Tile @ x:%d, y:%d, zoom:%d", x, y, zoom);
UIGraphicsPushContext(context);
... ... @@ -168,7 +179,7 @@
tileImage = [_tileSource imageForTile:RMTileMake((int)nextTileX, (int)nextTileY, currentZoom) inCache:[_mapView tileCache]];
if (tileImage)
if (IS_VALID_TILE_IMAGE(tileImage))
{
// crop
float cropSize = 1.0 / powf(2.0, (float)currentTileDepth);
... ... @@ -184,6 +195,10 @@
break;
}
else
{
tileImage = nil;
}
currentTileDepth++;
currentZoom = zoom - currentTileDepth;
... ... @@ -191,40 +206,63 @@
}
}
if (_mapView.debugTiles)
if (IS_VALID_TILE_IMAGE(tileImage))
{
UIGraphicsBeginImageContext(tileImage.size);
if (_mapView.adjustTilesForRetinaDisplay && _mapView.screenScale > 1.0)
{
// Crop the image
float xCrop = (floor(rect.origin.x / rect.size.width) / 2.0) - x;
float yCrop = (floor(rect.origin.y / rect.size.height) / 2.0) - y;
CGRect cropBounds = CGRectMake(tileImage.size.width * xCrop,
tileImage.size.height * yCrop,
tileImage.size.width * 0.5,
tileImage.size.height * 0.5);
CGImageRef imageRef = CGImageCreateWithImageInRect([tileImage CGImage], cropBounds);
tileImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
}
if (_mapView.debugTiles)
{
UIGraphicsBeginImageContext(tileImage.size);
CGContextRef debugContext = UIGraphicsGetCurrentContext();
CGContextRef debugContext = UIGraphicsGetCurrentContext();
CGRect debugRect = CGRectMake(0, 0, tileImage.size.width, tileImage.size.height);
CGRect debugRect = CGRectMake(0, 0, tileImage.size.width, tileImage.size.height);
[tileImage drawInRect:debugRect];
[tileImage drawInRect:debugRect];
UIFont *font = [UIFont systemFontOfSize:32.0];
UIFont *font = [UIFont systemFontOfSize:32.0];
CGContextSetStrokeColorWithColor(debugContext, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(debugContext, 2.0);
CGContextSetShadowWithColor(debugContext, CGSizeMake(0.0, 0.0), 5.0, [UIColor blackColor].CGColor);
CGContextSetStrokeColorWithColor(debugContext, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(debugContext, 2.0);
CGContextSetShadowWithColor(debugContext, CGSizeMake(0.0, 0.0), 5.0, [UIColor blackColor].CGColor);
CGContextStrokeRect(debugContext, debugRect);
CGContextStrokeRect(debugContext, debugRect);
CGContextSetFillColorWithColor(debugContext, [UIColor whiteColor].CGColor);
CGContextSetFillColorWithColor(debugContext, [UIColor whiteColor].CGColor);
NSString *debugString = [NSString stringWithFormat:@"Zoom %d", zoom];
CGSize debugSize1 = [debugString sizeWithFont:font];
[debugString drawInRect:CGRectMake(5.0, 5.0, debugSize1.width, debugSize1.height) withFont:font];
NSString *debugString = [NSString stringWithFormat:@"Zoom %d", zoom];
CGSize debugSize1 = [debugString sizeWithFont:font];
[debugString drawInRect:CGRectMake(5.0, 5.0, debugSize1.width, debugSize1.height) withFont:font];
debugString = [NSString stringWithFormat:@"(%d, %d)", x, y];
CGSize debugSize2 = [debugString sizeWithFont:font];
[debugString drawInRect:CGRectMake(5.0, 5.0 + debugSize1.height + 5.0, debugSize2.width, debugSize2.height) withFont:font];
debugString = [NSString stringWithFormat:@"(%d, %d)", x, y];
CGSize debugSize2 = [debugString sizeWithFont:font];
[debugString drawInRect:CGRectMake(5.0, 5.0 + debugSize1.height + 5.0, debugSize2.width, debugSize2.height) withFont:font];
tileImage = UIGraphicsGetImageFromCurrentImageContext();
tileImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
UIGraphicsEndImageContext();
}
[tileImage drawInRect:rect];
[tileImage drawInRect:rect];
}
else
{
// NSLog(@"Invalid image for {%d,%d} @ %d", x, y, zoom);
}
UIGraphicsPopContext();
}
... ...
... ... @@ -168,9 +168,9 @@ typedef enum : NSUInteger {
- (id)initWithFrame:(CGRect)frame
andTilesource:(id <RMTileSource>)newTilesource
centerCoordinate:(CLLocationCoordinate2D)initialCenterCoordinate
zoomLevel:(float)initialZoomLevel
maxZoomLevel:(float)maxZoomLevel
minZoomLevel:(float)minZoomLevel
zoomLevel:(float)initialTileSourceZoomLevel
maxZoomLevel:(float)initialTileSourceMaxZoomLevel
minZoomLevel:(float)initialTileSourceMinZoomLevel
backgroundImage:(UIImage *)backgroundImage;
- (void)setFrame:(CGRect)frame;
... ... @@ -214,6 +214,10 @@ typedef enum : NSUInteger {
/** The maximum zoom level of the map, clamped to the range supported by the tile source(s). */
@property (nonatomic, assign) float maxZoom;
@property (nonatomic, assign) float tileSourcesZoom;
@property (nonatomic, assign) float tileSourcesMinZoom;
@property (nonatomic, assign) float tileSourcesMaxZoom;
@property (nonatomic, assign) RMProjectedRect projectedBounds;
@property (nonatomic, readonly) RMProjectedPoint projectedOrigin;
@property (nonatomic, readonly) RMProjectedSize projectedViewSize;
... ...
... ... @@ -210,9 +210,9 @@
- (void)performInitializationWithTilesource:(id <RMTileSource>)newTilesource
centerCoordinate:(CLLocationCoordinate2D)initialCenterCoordinate
zoomLevel:(float)initialZoomLevel
maxZoomLevel:(float)maxZoomLevel
minZoomLevel:(float)minZoomLevel
zoomLevel:(float)initialTileSourceZoomLevel
maxZoomLevel:(float)initialTileSourceMaxZoomLevel
minZoomLevel:(float)initialTileSourceMinZoomLevel
backgroundImage:(UIImage *)backgroundImage
{
_constrainMovement = _enableBouncing = _zoomingInPivotsAroundCenter = NO;
... ... @@ -266,11 +266,11 @@
[self setBackgroundView:_loadingTileView];
}
if (minZoomLevel < newTilesource.minZoom) minZoomLevel = newTilesource.minZoom;
if (maxZoomLevel > newTilesource.maxZoom) maxZoomLevel = newTilesource.maxZoom;
[self setMinZoom:minZoomLevel];
[self setMaxZoom:maxZoomLevel];
[self setZoom:initialZoomLevel];
if (initialTileSourceMinZoomLevel < newTilesource.minZoom) initialTileSourceMinZoomLevel = newTilesource.minZoom;
if (initialTileSourceMaxZoomLevel > newTilesource.maxZoom) initialTileSourceMaxZoomLevel = newTilesource.maxZoom;
[self setTileSourcesMinZoom:initialTileSourceMinZoomLevel];
[self setTileSourcesMaxZoom:initialTileSourceMaxZoomLevel];
[self setTileSourcesZoom:initialTileSourceZoomLevel];
[self setTileSource:newTilesource];
[self setCenterCoordinate:initialCenterCoordinate animated:NO];
... ... @@ -995,16 +995,13 @@
self.userTrackingMode = RMUserTrackingModeNone;
// Calculate rounded zoom
float newZoom = fmin(ceilf([self zoom]) + 0.99, [self maxZoom]);
if (newZoom == self.zoom)
return;
float newZoom = fmin(ceilf([self zoom]) + 1.0, [self maxZoom]);
float factor = exp2f(newZoom - [self zoom]);
if (factor > 2.25)
{
newZoom = fmin(ceilf([self zoom]) - 0.01, [self maxZoom]);
newZoom = fmin(ceilf([self zoom]), [self maxZoom]);
factor = exp2f(newZoom - [self zoom]);
}
... ... @@ -1020,16 +1017,13 @@
- (void)zoomOutToNextNativeZoomAt:(CGPoint)pivot animated:(BOOL) animated
{
// Calculate rounded zoom
float newZoom = fmax(floorf([self zoom]) - 0.01, [self minZoom]);
if (newZoom == self.zoom)
return;
float newZoom = fmax(floorf([self zoom]), [self minZoom]);
float factor = exp2f(newZoom - [self zoom]);
if (factor > 0.75)
{
newZoom = fmax(floorf([self zoom]) - 1.01, [self minZoom]);
newZoom = fmax(floorf([self zoom]) - 1.0, [self minZoom]);
factor = exp2f(newZoom - [self zoom]);
}
... ... @@ -1160,10 +1154,7 @@
{
RMMapTiledLayerView *tiledLayerView = [[RMMapTiledLayerView alloc] initWithFrame:CGRectMake(0.0, 0.0, contentSize.width, contentSize.height) mapView:self forTileSource:tileSource];
if (self.adjustTilesForRetinaDisplay && _screenScale > 1.0)
((CATiledLayer *)tiledLayerView.layer).tileSize = CGSizeMake(tileSideLength * 2.0, tileSideLength * 2.0);
else
((CATiledLayer *)tiledLayerView.layer).tileSize = CGSizeMake(tileSideLength, tileSideLength);
((CATiledLayer *)tiledLayerView.layer).tileSize = CGSizeMake(tileSideLength, tileSideLength);
[_tiledLayersSuperview addSubview:tiledLayerView];
}
... ... @@ -1801,8 +1792,8 @@
else
_constrainingProjectedBounds = _projection.planetBounds;
[self setMinZoom:_tileSourcesContainer.minZoom];
[self setMaxZoom:_tileSourcesContainer.maxZoom];
[self setTileSourcesMinZoom:_tileSourcesContainer.minZoom];
[self setTileSourcesMaxZoom:_tileSourcesContainer.maxZoom];
[self setZoom:[self zoom]]; // setZoom clamps zoom level to min/max limits
// Recreate the map layer
... ... @@ -1841,8 +1832,8 @@
else
_constrainingProjectedBounds = _projection.planetBounds;
[self setMinZoom:_tileSourcesContainer.minZoom];
[self setMaxZoom:_tileSourcesContainer.maxZoom];
[self setTileSourcesMinZoom:_tileSourcesContainer.minZoom];
[self setTileSourcesMaxZoom:_tileSourcesContainer.maxZoom];
[self setZoom:[self zoom]]; // setZoom clamps zoom level to min/max limits
// Recreate the map layer
... ... @@ -1859,10 +1850,7 @@
RMMapTiledLayerView *tiledLayerView = [[RMMapTiledLayerView alloc] initWithFrame:CGRectMake(0.0, 0.0, contentSize.width, contentSize.height) mapView:self forTileSource:newTileSource];
if (self.adjustTilesForRetinaDisplay && _screenScale > 1.0)
((CATiledLayer *)tiledLayerView.layer).tileSize = CGSizeMake(tileSideLength * 2.0, tileSideLength * 2.0);
else
((CATiledLayer *)tiledLayerView.layer).tileSize = CGSizeMake(tileSideLength, tileSideLength);
((CATiledLayer *)tiledLayerView.layer).tileSize = CGSizeMake(tileSideLength, tileSideLength);
if (index >= [[_tileSourcesContainer tileSources] count])
[_tiledLayersSuperview addSubview:tiledLayerView];
... ... @@ -2064,6 +2052,9 @@
- (void)setMinZoom:(float)newMinZoom
{
if (newMinZoom < 0.0)
newMinZoom = 0.0;
_minZoom = newMinZoom;
// RMLog(@"New minZoom:%f", newMinZoom);
... ... @@ -2073,8 +2064,26 @@
[self correctMinZoomScaleForBoundingMask];
}
- (float)tileSourcesMinZoom
{
return self.tileSourcesContainer.minZoom;
}
- (void)setTileSourcesMinZoom:(float)tileSourcesMinZoom
{
tileSourcesMinZoom = ceilf(tileSourcesMinZoom) - 0.99;
if ( ! self.adjustTilesForRetinaDisplay && _screenScale > 1.0)
tileSourcesMinZoom -= 1.0;
[self setMinZoom:tileSourcesMinZoom];
}
- (void)setMaxZoom:(float)newMaxZoom
{
if (newMaxZoom < 0.0)
newMaxZoom = 0.0;
_maxZoom = newMaxZoom;
// RMLog(@"New maxZoom:%f", newMaxZoom);
... ... @@ -2082,6 +2091,21 @@
_mapScrollView.maximumZoomScale = exp2f(newMaxZoom);
}
- (float)tileSourcesMaxZoom
{
return self.tileSourcesContainer.maxZoom;
}
- (void)setTileSourcesMaxZoom:(float)tileSourcesMaxZoom
{
tileSourcesMaxZoom = floorf(tileSourcesMaxZoom);
if ( ! self.adjustTilesForRetinaDisplay && _screenScale > 1.0)
tileSourcesMaxZoom -= 1.0;
[self setMaxZoom:tileSourcesMaxZoom];
}
- (float)zoom
{
return _zoom;
... ... @@ -2093,11 +2117,31 @@
_zoom = (newZoom > _maxZoom) ? _maxZoom : newZoom;
_zoom = (_zoom < _minZoom) ? _minZoom : _zoom;
// RMLog(@"New zoom:%f", zoom);
// RMLog(@"New zoom:%f", _zoom);
_mapScrollView.zoomScale = exp2f(_zoom);
}
- (float)tileSourcesZoom
{
float zoom = ceilf(_zoom);
if ( ! self.adjustTilesForRetinaDisplay && _screenScale > 1.0)
zoom += 1.0;
return zoom;
}
- (void)setTileSourcesZoom:(float)tileSourcesZoom
{
tileSourcesZoom = floorf(tileSourcesZoom);
if ( ! self.adjustTilesForRetinaDisplay && _screenScale > 1.0)
tileSourcesZoom -= 1.0;
[self setZoom:tileSourcesZoom];
}
- (void)setEnableClustering:(BOOL)doEnableClustering
{
_enableClustering = doEnableClustering;
... ... @@ -2904,9 +2948,9 @@
_userHeadingTrackingView.contentMode = UIViewContentModeTop;
_userHeadingTrackingView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin;
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin;
_userHeadingTrackingView.alpha = 0.0;
... ... @@ -2918,9 +2962,9 @@
round([self bounds].size.height / 2));
_userLocationTrackingView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin;
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin;
[self insertSubview:_userLocationTrackingView aboveSubview:_userHeadingTrackingView];
... ...
... ... @@ -177,4 +177,20 @@
});
}
- (void)removeAllCachedImagesForCacheKey:(NSString *)cacheKey
{
dispatch_barrier_async(_memoryCacheQueue, ^{
NSMutableArray *keysToRemove = [NSMutableArray array];
[_memoryCache enumerateKeysAndObjectsUsingBlock:^(id key, RMCacheObject *cachedObject, BOOL *stop) {
if ([[cachedObject cacheKey] isEqualToString:cacheKey])
[keysToRemove addObject:key];
}];
[_memoryCache removeObjectsForKeys:keysToRemove];
});
}
@end
... ...
... ... @@ -37,6 +37,8 @@
self.minZoom = 1;
self.maxZoom = 18;
self.opaque = NO;
return self;
}
... ...
... ... @@ -80,7 +80,7 @@
}
// DEPRECATED. Use RMShape instead.
- (id)initWithView:(RMMapView *)aMapView __attribute__ ((deprecated));;
- (id)initWithView:(RMMapView *)aMapView __attribute__ ((deprecated));
@property (nonatomic, assign) CGPathDrawingMode drawingMode;
@property (nonatomic, assign) CGLineCap lineCap;
... ...
... ... @@ -66,6 +66,7 @@ typedef enum : short {
/** Removes all tile images from a cache. */
- (void)removeAllCachedImages;
- (void)removeAllCachedImagesForCacheKey:(NSString *)cacheKey;
@end
... ...
... ... @@ -217,6 +217,20 @@
});
}
- (void)removeAllCachedImagesForCacheKey:(NSString *)cacheKey
{
[_memoryCache removeAllCachedImagesForCacheKey:cacheKey];
dispatch_sync(_tileCacheQueue, ^{
for (id<RMTileCache> cache in _tileCaches)
{
[cache removeAllCachedImagesForCacheKey:cacheKey];
}
});
}
@end
#pragma mark -
... ...
... ... @@ -51,6 +51,12 @@
/** The maximum zoom level supported by the tile source. */
@property (nonatomic, assign) float maxZoom;
/** A Boolean value indicating whether the tiles from this source should be cached. */
@property (nonatomic, assign, getter=isCacheable) BOOL cacheable;
/** A Boolean value indicating whether the tiles from this source are opaque. Setting this correctly is important when using RMCompositeSource so that alpha transparency can be preserved when compositing tile images. */
@property (nonatomic, assign, getter=isOpaque) BOOL opaque;
@property (nonatomic, readonly) RMFractalTileProjection *mercatorToTileProjection;
@property (nonatomic, readonly) RMProjection *projection;
... ... @@ -93,6 +99,11 @@
* @return An image to display. */
- (UIImage *)imageForTile:(RMTile)tile inCache:(RMTileCache *)tileCache;
/** Check if the tile source can provide the requested tile.
* @param tile The map tile in question.
* @return A Boolean value indicating whether the tile source can provide the requested tile. */
- (BOOL)tileSourceHasTile:(RMTile)tile;
- (void)cancelAllDownloads;
- (void)didReceiveMemoryWarning;
... ...
... ... @@ -33,8 +33,6 @@
@interface RMTileSourcesContainer : NSObject
@property (nonatomic, readonly) NSArray *tileSources;
// These are the minimum and maximum zoom levels across all tile sources.
@property (nonatomic, assign) float minZoom;
@property (nonatomic, assign) float maxZoom;
... ... @@ -49,6 +47,12 @@
#pragma mark -
@property (nonatomic, readonly) NSArray *tileSources;
- (id <RMTileSource>)tileSourceForUniqueTilecacheKey:(NSString *)uniqueTilecacheKey;
#pragma mark -
- (BOOL)setTileSource:(id <RMTileSource>)tileSource;
- (BOOL)setTileSources:(NSArray *)tileSources;
... ...
... ... @@ -28,6 +28,8 @@
#import "RMTileSourcesContainer.h"
#import "RMCompositeSource.h"
@implementation RMTileSourcesContainer
{
NSMutableArray *_tileSources;
... ... @@ -87,6 +89,38 @@
return [tileSources autorelease];
}
- (id <RMTileSource>)tileSourceForUniqueTilecacheKey:(NSString *)uniqueTilecacheKey
{
if (!uniqueTilecacheKey)
return nil;
id result = nil;
[_tileSourcesLock lock];
NSMutableArray *tileSources = [NSMutableArray arrayWithArray:_tileSources];
while ([tileSources count])
{
id <RMTileSource> currentTileSource = [tileSources objectAtIndex:0];
[tileSources removeObjectAtIndex:0];
if ([currentTileSource isKindOfClass:[RMCompositeSource class]])
{
[tileSources addObjectsFromArray:[(RMCompositeSource *)currentTileSource tileSources]];
}
else if ([[currentTileSource uniqueTilecacheKey] isEqualToString:uniqueTilecacheKey])
{
result = [currentTileSource retain];
break;
}
}
[_tileSourcesLock unlock];
return [result autorelease];
}
- (BOOL)setTileSource:(id <RMTileSource>)tileSource
{
BOOL result;
... ...
... ... @@ -24,6 +24,8 @@
161E563B1594664E00B00BB6 /* RMOpenSeaMapLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 161E56391594664E00B00BB6 /* RMOpenSeaMapLayer.m */; };
1656665515A1DF7900EF3DC7 /* RMCoordinateGridSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 1656665315A1DF7900EF3DC7 /* RMCoordinateGridSource.h */; };
1656665615A1DF7900EF3DC7 /* RMCoordinateGridSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 1656665415A1DF7900EF3DC7 /* RMCoordinateGridSource.m */; };
16E5A63A15E531F200C92A5A /* RMCompositeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 16E5A63815E531F200C92A5A /* RMCompositeSource.h */; };
16E5A63B15E531F200C92A5A /* RMCompositeSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 16E5A63915E531F200C92A5A /* RMCompositeSource.m */; };
16EC85D2133CA6C300219947 /* RMAbstractMercatorTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 16EC85CC133CA6C300219947 /* RMAbstractMercatorTileSource.h */; };
16EC85D3133CA6C300219947 /* RMAbstractMercatorTileSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 16EC85CD133CA6C300219947 /* RMAbstractMercatorTileSource.m */; };
16EC85D4133CA6C300219947 /* RMAbstractWebMapSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 16EC85CE133CA6C300219947 /* RMAbstractWebMapSource.h */; };
... ... @@ -156,6 +158,8 @@
161E56391594664E00B00BB6 /* RMOpenSeaMapLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMOpenSeaMapLayer.m; sourceTree = "<group>"; };
1656665315A1DF7900EF3DC7 /* RMCoordinateGridSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMCoordinateGridSource.h; sourceTree = "<group>"; };
1656665415A1DF7900EF3DC7 /* RMCoordinateGridSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMCoordinateGridSource.m; sourceTree = "<group>"; };
16E5A63815E531F200C92A5A /* RMCompositeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMCompositeSource.h; sourceTree = "<group>"; };
16E5A63915E531F200C92A5A /* RMCompositeSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMCompositeSource.m; sourceTree = "<group>"; };
16EC85CC133CA6C300219947 /* RMAbstractMercatorTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMAbstractMercatorTileSource.h; sourceTree = "<group>"; };
16EC85CD133CA6C300219947 /* RMAbstractMercatorTileSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMAbstractMercatorTileSource.m; sourceTree = "<group>"; };
16EC85CE133CA6C300219947 /* RMAbstractWebMapSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMAbstractWebMapSource.h; sourceTree = "<group>"; };
... ... @@ -295,6 +299,8 @@
16EC85CB133CA69A00219947 /* Map sources */ = {
isa = PBXGroup;
children = (
16E5A63815E531F200C92A5A /* RMCompositeSource.h */,
16E5A63915E531F200C92A5A /* RMCompositeSource.m */,
1656665315A1DF7900EF3DC7 /* RMCoordinateGridSource.h */,
1656665415A1DF7900EF3DC7 /* RMCoordinateGridSource.m */,
D1437B33122869E400888DAE /* RMDBMapSource.h */,
... ... @@ -619,8 +625,9 @@
161E563A1594664E00B00BB6 /* RMOpenSeaMapLayer.h in Headers */,
1656665515A1DF7900EF3DC7 /* RMCoordinateGridSource.h in Headers */,
DD5A200B15CAD09400FE4157 /* GRMustache.h in Headers */,
DD5FA1EB15E2B020004EB6C5 /* RMLoadingTileView.h in Headers */,
DD4BE198161CE296003EF677 /* MapBox.h in Headers */,
16E5A63A15E531F200C92A5A /* RMCompositeSource.h in Headers */,
DD5FA1EB15E2B020004EB6C5 /* RMLoadingTileView.h in Headers */,
DD4195C9162356900049E6BA /* RMBingSource.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
... ... @@ -739,6 +746,7 @@
16FBF07715936BF1004ECAD1 /* RMTileSourcesContainer.m in Sources */,
161E563B1594664E00B00BB6 /* RMOpenSeaMapLayer.m in Sources */,
1656665615A1DF7900EF3DC7 /* RMCoordinateGridSource.m in Sources */,
16E5A63B15E531F200C92A5A /* RMCompositeSource.m in Sources */,
DD5FA1EC15E2B020004EB6C5 /* RMLoadingTileView.m in Sources */,
DD4195CA162356900049E6BA /* RMBingSource.m in Sources */,
);
... ...
... ... @@ -41,8 +41,8 @@ Please note that you are responsible for getting permission to use the map data,
[7]: https://github.com/mapbox/tilestream
[8]: http://mbtiles.org
Installing
----------
Installation
------------
There are three ways that you can install the SDK, depending upon your needs:
... ...