Authored by Joseph G

Refactored a bunch of cache code. Removed all singletons. Made the whole thing

handle having the tile source changed.
... ... @@ -10,9 +10,13 @@
#import "RMTileSource.h"
@class RMTileCache;
// Simple wrapper around a tilesource which checks the image cache first.
@interface RMCachedTileSource : NSObject<RMTileSource> {
@interface RMCachedTileSource : NSObject<RMTileSource>
{
id <RMTileSource> tileSource;
RMTileCache *cache;
}
- (id) initWithSource: (id<RMTileSource>) source;
... ... @@ -20,4 +24,6 @@
// Bleah ugly name.
+ (RMCachedTileSource*) cachedTileSourceWithSource: (id<RMTileSource>) source;
- (id<RMTileSource>) underlyingTileSource;
@end
... ...
... ... @@ -24,12 +24,15 @@
tileSource = [_source retain];
cache = [[RMTileCache alloc] initWithTileSource:tileSource];
return self;
}
- (void) dealloc
{
[tileSource release];
[cache release];
[super dealloc];
}
... ... @@ -40,14 +43,16 @@
-(RMTileImage *) tileImage: (RMTile) tile
{
RMTileImage *cachedImage = [[RMTileCache sharedCache] cachedImage:tile];
RMTileImage *cachedImage = [cache cachedImage:tile];
if (cachedImage != nil)
{
return cachedImage;
}
else
{
return [tileSource tileImage:tile];
RMTileImage *image = [tileSource tileImage:tile];
[cache addTile:tile WithImage:image];
return image;
}
}
... ... @@ -66,5 +71,11 @@
return [tileSource bounds];
}
- (id<RMTileSource>) underlyingTileSource
{
// I'm assuming that our tilesource isn't itself a cachedtilesource.
// This class's initialiser should make sure of that.
return tileSource;
}
@end
... ...
... ... @@ -3,7 +3,7 @@
// MapView
//
// Created by Dmytro Golub on 10/29/08.
// Copyright 2008 Cloudmade. All rights reserved.
// Copyright 2008 Cloudmade. Refer to project license.
//
#import "RMCloudMadeMapSource.h"
... ...
... ... @@ -9,10 +9,14 @@
#import <UIKit/UIKit.h>
#import "RMTileCache.h"
@class RMTileCacheDAO;
@interface RMDatabaseCache : NSObject<RMTileCache> {
RMTileCacheDAO *dao;
}
// +(void) install;
+ (NSString*)dbPathForTileSource: (id<RMTileSource>) source;
-(id) initWithDatabase: (NSString*)path;
-(id) initWithTileSource: (id<RMTileSource>) source;
@end
... ...
... ... @@ -11,23 +11,38 @@
#import "RMTileImage.h"
#import "RMTile.h"
//static BOOL installed = NO;
@implementation RMDatabaseCache
-(id) init
+ (NSString*)dbPathForTileSource: (id<RMTileSource>) source
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if ([paths count] > 0) // Should only be one...
{
NSString *filename = [NSString stringWithFormat:@"Map%@.sqlite", [source description]];
return [[paths objectAtIndex:0] stringByAppendingPathComponent:filename];
}
return nil;
}
-(id) initWithDatabase: (NSString*)path
{
if (![super init])
return nil;
// NSLog(@"%d items in DB", [[DAO sharedManager] count]);
// NSLog(@"%d items in DB", [[DAO sharedManager] count]);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(addImageData:)
name:RMMapImageLoadedNotification
object:nil];
dao = [[RMTileCacheDAO alloc] initWithDatabase:path];
if (dao == nil)
return nil;
return self;
return self;
}
-(id) initWithTileSource: (id<RMTileSource>) source
{
return [self initWithDatabase:[RMDatabaseCache dbPathForTileSource:source]];
}
-(void) dealloc
... ... @@ -37,22 +52,40 @@
[super dealloc];
}
-(void)addTile: (RMTile)tile WithImage: (RMTileImage*)image
{
// The tile probably hasn't loaded any data yet... we must be patient.
// However, if the image is already loaded we probably don't need to cache it.
// This will be the case for any other web caches which are active.
if (![image isLoaded])
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(addImageData:)
name:RMMapImageLoadedNotification
object:image];
}
}
-(void) addImageData: (NSNotification *)notification
{
// NSLog(@"AddImageData");
NSData *data = [[notification userInfo] objectForKey:@"data"];
RMTileImage *image = (RMTileImage*)[notification object];
[[RMTileCacheDAO sharedManager] addData:data LastUsed:[image lastUsedTime] ForTile:RMTileHash([image tile])];
[dao addData:data LastUsed:[image lastUsedTime] ForTile:RMTileHash([image tile])];
// NSLog(@"%d items in DB", [[DAO sharedManager] count]);
[[NSNotificationCenter defaultCenter] removeObserver:self
name:RMMapImageLoadedNotification
object:image];
// NSLog(@"%d items in DB", [dao count]);
}
-(RMTileImage*) cachedImage:(RMTile)tile
{
// NSLog(@"Looking for cached image in DB");
NSData *data = [[RMTileCacheDAO sharedManager] dataForTile:RMTileHash(tile)];
NSData *data = [dao dataForTile:RMTileHash(tile)];
if (data == nil)
return nil;
... ... @@ -60,17 +93,5 @@
// NSLog(@"DB cache hit for tile %d %d %d", tile.x, tile.y, tile.zoom);
return image;
}
/*
+(void) install
{
if (installed)
return;
RMDatabaseCache *dbCache = [[RMDatabaseCache alloc] init];
[[RMTileCache sharedCache] addCache:dbCache];
[dbCache release];
installed = YES;
}*/
@end
... ...
... ... @@ -25,14 +25,11 @@
#import "RMMarker.h"
//#import "RMCloudMadeMapSource.h"
@implementation RMMapContents
#pragma mark Initialisation
- (id) initForView: (UIView*) view
{
// id<RMTileSource> _tileSource = [[RMCloudMadeMapSource alloc] init];
id<RMTileSource> _tileSource = [[RMOpenStreetMapsSource alloc] init];
RMMapRenderer *_renderer = [[RMCoreAnimationRenderer alloc] initWithContent:self];
... ... @@ -72,7 +69,7 @@
[self setRenderer:_renderer];
imagesOnScreen = [[RMTileImageSet alloc] initWithDelegate:renderer];
[imagesOnScreen setTileSource:[RMCachedTileSource cachedTileSourceWithSource:tileSource]];
[imagesOnScreen setTileSource:tileSource];
tileLoader = [[RMTileLoader alloc] initWithContent:self];
[tileLoader setSuppressLoading:YES];
... ... @@ -121,11 +118,8 @@
- (void)moveToXYPoint: (RMXYPoint)aPoint
{
[mercatorToScreenProjection setXYCenter:aPoint];
// [imagesOnScreen removeAllTiles];
[tileLoader clearLoadedBounds];
[tileLoader updateLoadedImages];
[tileLoader reload];
[renderer setNeedsDisplay];
}
... ... @@ -155,6 +149,11 @@
- (void) setTileSource: (id<RMTileSource>)newTileSource
{
if (tileSource == newTileSource)
return;
newTileSource = [RMCachedTileSource cachedTileSourceWithSource:newTileSource];
[tileSource release];
tileSource = [newTileSource retain];
... ... @@ -165,6 +164,8 @@
mercatorToTileProjection = [[tileSource mercatorToTileProjection] retain];
[imagesOnScreen setTileSource:tileSource];
[tileLoader reload];
}
- (id<RMTileSource>) tileSource
... ...
... ... @@ -59,12 +59,6 @@
{
NSNumber *key = [RMTileCache tileHash: tile];
RMTileImage *image = [cache objectForKey:key];
/* if (image == nil)
NSLog(@"cache miss %@", key);
else
NSLog(@"cache hit %@", key);
*/
return image;
}
... ...
... ... @@ -29,13 +29,14 @@
NSMutableArray *caches;
}
+(RMTileCache*)sharedCache;
-(id)initWithTileSource: (id<RMTileSource>) tileSource;
+(NSNumber*) tileHash: (RMTile)tile;
// Add tile to cache
-(void)addTile: (RMTile)tile WithImage: (RMTileImage*)image;
// Add another cache to the chain
-(void)addCache: (id<RMTileCache>)cache;
@end
\ No newline at end of file
... ...
... ... @@ -13,11 +13,11 @@
#import "RMConfiguration.h"
static RMTileCache *cache = nil;
#import "RMTileSource.h"
@implementation RMTileCache
-(id)init
-(id)initWithTileSource: (id<RMTileSource>) tileSource
{
if (![super init])
return nil;
... ... @@ -51,7 +51,7 @@ static RMTileCache *cache = nil;
if ([@"db-cache" isEqualToString: type])
{
newCache = [[RMDatabaseCache alloc] init];
newCache = [[RMDatabaseCache alloc] initWithTileSource: tileSource];
}
if (newCache)
... ... @@ -79,15 +79,6 @@ static RMTileCache *cache = nil;
[super dealloc];
}
+(RMTileCache*)sharedCache
{
if (cache == nil)
{
cache = [[RMTileCache alloc] init];
}
return cache;
}
-(void)addCache: (id<RMTileCache>)cache
{
[caches addObject:cache];
... ...
... ... @@ -14,7 +14,7 @@
FMDatabase* db;
}
+(RMTileCacheDAO*) sharedManager;
-(id) initWithDatabase: (NSString*)path;
-(NSUInteger) count;
-(NSData*) dataForTile: (uint64_t) tileHash;
... ...
... ... @@ -11,7 +11,6 @@
#import "RMTileCache.h"
#import "RMTileImage.h"
static RMTileCacheDAO *sharedDAOManager = nil;
@implementation RMTileCacheDAO
... ... @@ -21,30 +20,18 @@ static RMTileCacheDAO *sharedDAOManager = nil;
[db executeUpdate:@"CREATE TABLE IF NOT EXISTS ZCACHE (ztileHash INTEGER PRIMARY KEY, zlastUsed DATE, zdata BLOB)"];
}
- (NSString*)dbPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if ([paths count] > 0)
{
// only copying one file
return [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Map.sqlite"];
}
return nil;
}
- (id)init
-(id) initWithDatabase: (NSString*)path
{
if (![super init])
return nil;
NSString *path = [self dbPath];
NSLog(@"Opening database at %@", path);
db = [[FMDatabase alloc] initWithPath:path];
if (![db open])
{
NSLog(@"Could not connect to database - %@", [db lastErrorMessage]);
// return nil;
return nil;
}
[db setCrashOnErrors:TRUE];
... ... @@ -100,14 +87,17 @@ static RMTileCacheDAO *sharedDAOManager = nil;
return data;
}
-(void) removeOldestFromDatabase
{
}
-(void) touchTile: (uint64_t) tileHash
{
}
-(void) addData: (NSData*) data LastUsed: (NSDate*)date ForTile: (uint64_t) tileHash
{
// Fixme
... ... @@ -120,48 +110,4 @@ static RMTileCacheDAO *sharedDAOManager = nil;
// NSLog(@"done\t%d", tileHash);
}
// Singleton gunk as per CocoaFundamentals page 99.
+ (RMTileCacheDAO*)sharedManager
{
@synchronized(self) {
if (sharedDAOManager == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedDAOManager;
}
+ (id)allocWithZone:(NSZone *)zone
{
@synchronized(self) {
if (sharedDAOManager == nil) {
sharedDAOManager = [super allocWithZone:zone];
return sharedDAOManager; // assignment and return on first allocation
}
}
return nil; //on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return UINT_MAX; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
@end
... ...
... ... @@ -62,11 +62,13 @@ extern NSString * const RMMapImageLoadingCancelledNotification;
- (void)makeLayer;
-(void) cancelLoading;
- (void)cancelLoading;
- (void)setImageToData: (NSData*) data;
-(void) touch;
- (void)touch;
- (BOOL)isLoaded;
@property (readwrite, assign) CGRect screenLocation;
@property (readonly, assign) RMTile tile;
... ...
... ... @@ -21,7 +21,7 @@ NSString * const RMMapImageLoadingCancelledNotification = @"MapImageLoadingCance
@synthesize tile, layer, image, lastUsedTime;
- (id) initWithTile: (RMTile)_tile AddToCache: (BOOL) addToCache
- (id) initWithTile: (RMTile)_tile
{
if (![super init])
return nil;
... ... @@ -39,18 +39,9 @@ NSString * const RMMapImageLoadingCancelledNotification = @"MapImageLoadingCance
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(tileRemovedFromScreen:)
name:RMMapImageRemovedFromScreenNotification object:self];
// Should this be done as a notification?
if (addToCache)
[[RMTileCache sharedCache] addTile:tile WithImage:self];
return self;
}
- (id) initWithTile: (RMTile)_tile
{
return [self initWithTile:_tile AddToCache: YES];
}
-(void) tileRemovedFromScreen: (NSNotification*) notification
{
... ... @@ -66,7 +57,7 @@ NSString * const RMMapImageLoadingCancelledNotification = @"MapImageLoadingCance
+ (RMTileImage*) dummyTile: (RMTile)tile
{
return [[[RMTileImage alloc] initWithTile:tile AddToCache:NO] autorelease];
return [[[RMTileImage alloc] initWithTile:tile] autorelease];
}
- (void)dealloc
... ... @@ -139,11 +130,6 @@ NSString * const RMMapImageLoadingCancelledNotification = @"MapImageLoadingCance
[[NSNotificationCenter defaultCenter] postNotificationName:RMMapImageLoadingCancelledNotification
object:self];
}
//
//- (void)setImageToData: (NSData*) data
//{
// image = [[UIImage imageWithData:data] retain];
//}
- (void) loadPendingData: (NSNotification*)notification
{
... ... @@ -170,21 +156,17 @@ NSString * const RMMapImageLoadingCancelledNotification = @"MapImageLoadingCance
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loadPendingData:) name:RMResumeExpensiveOperations object:nil];
return;
}
// CGContextRef context =
CGDataProviderRef provider = CGDataProviderCreateWithCFData ((CFDataRef)data);
CGImageRef cgImage = CGImageCreateWithPNGDataProvider(provider, NULL, FALSE, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
// CGImageRetain(image);
// NSLog(@"setImageToData");
if (layer == nil)
{
image = [[UIImage imageWithCGImage:cgImage] retain];
}
else
{
// NSLog(@"Replacing layer contents with data");
layer.contents = (id)cgImage;
}
... ... @@ -196,6 +178,12 @@ NSString * const RMMapImageLoadingCancelledNotification = @"MapImageLoadingCance
userInfo:d];
}
- (BOOL)isLoaded
{
return image != nil
|| (layer != nil && layer.contents != NULL);
}
- (NSUInteger)hash
{
return (NSUInteger)RMTileHash(tile);
... ...
... ... @@ -34,14 +34,16 @@ extern NSString * const RMResumeExpensiveOperations;
}
// Designated initialiser
-(id) initWithContent: (RMMapContents *)contents;
- (id)initWithContent: (RMMapContents *)contents;
-(void) updateLoadedImages;
- (void)updateLoadedImages;
- (void)moveBy: (CGSize) delta;
- (void)zoomByFactor: (float) zoomFactor near:(CGPoint) center;
-(void) clearLoadedBounds;
- (void)clearLoadedBounds;
- (void)reload;
@property (readonly, nonatomic) CGRect loadedBounds;
@property (readonly, nonatomic) int loadedZoom;
... ...
... ... @@ -135,6 +135,12 @@ NSString* const RMResumeExpensiveOperations = @"RMResumeExpensiveOperations";
[self updateLoadedImages];
}
- (void)reload
{
[self clearLoadedBounds];
[self updateLoadedImages];
}
//-(BOOL) containsRect: (CGRect)bounds
//{
// return CGRectContainsRect(loadedBounds, bounds);
... ...