Authored by Justin R. Miller

MBTiles support

Subproject commit be9cbd1bfa3bd16914cfef75cb05053b80f41b24
Subproject commit 5f14e7da23612c11b6e7016f0e44ba417626fbd3
... ...
//
// RMMBTilesTileSource.h
//
// Created by Justin R. Miller on 6/18/10.
// Copyright 2010, Code Sorcery Workshop, LLC and Development Seed, Inc.
// 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.
//
// * Neither the names of Code Sorcery Workshop, LLC or Development Seed,
// Inc., nor the names of its contributors may be used to endorse or
// promote products derived from this software without specific prior
// written permission.
//
// 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.
//
// http://mbtiles.org
//
// Example usage at https://github.com/mapbox/mbtiles-ios-example
//
#import <Foundation/Foundation.h>
#import "RMTileSource.h"
@class RMFractalTileProjection;
@class FMDatabaseQueue;
#define kMBTilesDefaultTileSize 256
#define kMBTilesDefaultMinTileZoom 0
#define kMBTilesDefaultMaxTileZoom 22
#define kMBTilesDefaultLatLonBoundingBox ((RMSphericalTrapezium){.northEast = {.latitude = 90, .longitude = 180}, .southWest = {.latitude = -90, .longitude = -180}})
@interface RMMBTilesTileSource : NSObject <RMTileSource>
{
RMFractalTileProjection *tileProjection;
FMDatabaseQueue *queue;
}
- (id)initWithTileSetURL:(NSURL *)tileSetURL;
- (BOOL)coversFullWorld;
- (NSString *)legend;
@end
\ No newline at end of file
... ...
//
// RMMBTilesTileSource.m
//
// Created by Justin R. Miller on 6/18/10.
// Copyright 2010, Code Sorcery Workshop, LLC and Development Seed, Inc.
// 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.
//
// * Neither the names of Code Sorcery Workshop, LLC or Development Seed,
// Inc., nor the names of its contributors may be used to endorse or
// promote products derived from this software without specific prior
// written permission.
//
// 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 "RMMBTilesTileSource.h"
#import "RMTileImage.h"
#import "RMProjection.h"
#import "RMFractalTileProjection.h"
#import "FMDatabase.h"
#import "FMDatabaseQueue.h"
@implementation RMMBTilesTileSource
- (id)initWithTileSetURL:(NSURL *)tileSetURL
{
if ( ! [super init])
return nil;
tileProjection = [[RMFractalTileProjection alloc] initFromProjection:[self projection]
tileSideLength:kMBTilesDefaultTileSize
maxZoom:kMBTilesDefaultMaxTileZoom
minZoom:kMBTilesDefaultMinTileZoom];
queue = [[FMDatabaseQueue databaseQueueWithPath:[tileSetURL relativePath]] retain];
if ( ! queue)
return nil;
[[queue database] setShouldCacheStatements:YES];
return self;
}
- (void)cancelAllDownloads
{
// no-op
}
- (void)dealloc
{
[tileProjection release];
[queue release];
[super dealloc];
}
- (int)tileSideLength
{
return tileProjection.tileSideLength;
}
- (void)setTileSideLength:(NSUInteger)aTileSideLength
{
[tileProjection setTileSideLength:aTileSideLength];
}
- (UIImage *)imageForTile:(RMTile)tile inCache:(RMTileCache *)tileCache
{
NSAssert4(((tile.zoom >= self.minZoom) && (tile.zoom <= self.maxZoom)),
@"%@ tried to retrieve tile with zoomLevel %d, outside source's defined range %f to %f",
self, tile.zoom, self.minZoom, self.maxZoom);
NSInteger zoom = tile.zoom;
NSInteger x = tile.x;
NSInteger y = pow(2, zoom) - tile.y - 1;
__block UIImage *image;
[queue inDatabase:^(FMDatabase *db)
{
FMResultSet *results = [db executeQuery:@"select tile_data from tiles where zoom_level = ? and tile_column = ? and tile_row = ?",
[NSNumber numberWithShort:zoom],
[NSNumber numberWithUnsignedInt:x],
[NSNumber numberWithUnsignedInt:y]];
if ([db hadError])
image = [RMTileImage errorTile];
[results next];
NSData *data = [results dataForColumn:@"tile_data"];
if ( ! data)
image = [RMTileImage errorTile];
else
image = [UIImage imageWithData:data];
[results close];
}];
return image;
}
- (NSString *)tileURL:(RMTile)tile
{
return nil;
}
- (NSString *)tileFile:(RMTile)tile
{
return nil;
}
- (NSString *)tilePath
{
return nil;
}
- (RMFractalTileProjection *)mercatorToTileProjection
{
return [[tileProjection retain] autorelease];
}
- (RMProjection *)projection
{
return [RMProjection googleProjection];
}
- (float)minZoom
{
__block double minZoom;
[queue inDatabase:^(FMDatabase *db)
{
FMResultSet *results = [db executeQuery:@"select min(zoom_level) from tiles"];
if ([db hadError])
minZoom = kMBTilesDefaultMinTileZoom;
[results next];
minZoom = [results doubleForColumnIndex:0];
[results close];
}];
return minZoom;
}
- (float)maxZoom
{
__block double maxZoom;
[queue inDatabase:^(FMDatabase *db)
{
FMResultSet *results = [db executeQuery:@"select max(zoom_level) from tiles"];
if ([db hadError])
maxZoom = kMBTilesDefaultMaxTileZoom;
[results next];
maxZoom = [results doubleForColumnIndex:0];
[results close];
}];
return maxZoom;
}
- (void)setMinZoom:(NSUInteger)aMinZoom
{
[tileProjection setMinZoom:aMinZoom];
}
- (void)setMaxZoom:(NSUInteger)aMaxZoom
{
[tileProjection setMaxZoom:aMaxZoom];
}
- (RMSphericalTrapezium)latitudeLongitudeBoundingBox
{
__block RMSphericalTrapezium bounds = kMBTilesDefaultLatLonBoundingBox;
[queue inDatabase:^(FMDatabase *db)
{
FMResultSet *results = [db executeQuery:@"select value from metadata where name = 'bounds'"];
[results next];
NSString *boundsString = [results stringForColumnIndex:0];
[results close];
if (boundsString)
{
NSArray *parts = [boundsString componentsSeparatedByString:@","];
if ([parts count] == 4)
{
bounds.southWest.longitude = [[parts objectAtIndex:0] doubleValue];
bounds.southWest.latitude = [[parts objectAtIndex:1] doubleValue];
bounds.northEast.longitude = [[parts objectAtIndex:2] doubleValue];
bounds.northEast.latitude = [[parts objectAtIndex:3] doubleValue];
}
}
}];
return bounds;
}
- (BOOL)coversFullWorld
{
RMSphericalTrapezium ownBounds = [self latitudeLongitudeBoundingBox];
RMSphericalTrapezium defaultBounds = kMBTilesDefaultLatLonBoundingBox;
if (ownBounds.southWest.longitude <= defaultBounds.southWest.longitude + 10 &&
ownBounds.northEast.longitude >= defaultBounds.northEast.longitude - 10)
return YES;
return NO;
}
- (NSString *)legend
{
__block NSString *legend;
[queue inDatabase:^(FMDatabase *db)
{
FMResultSet *results = [db executeQuery:@"select value from metadata where name = 'legend'"];
if ([db hadError])
legend = nil;
[results next];
legend = [results stringForColumn:@"value"];
[results close];
}];
return legend;
}
- (void)didReceiveMemoryWarning
{
NSLog(@"*** didReceiveMemoryWarning in %@", [self class]);
}
- (NSString *)uniqueTilecacheKey
{
return [NSString stringWithFormat:@"MBTiles%@", [queue.path lastPathComponent]];
}
- (NSString *)shortName
{
__block NSString *shortName;
[queue inDatabase:^(FMDatabase *db)
{
FMResultSet *results = [db executeQuery:@"select value from metadata where name = 'name'"];
if ([db hadError])
shortName = nil;
[results next];
shortName = [results stringForColumnIndex:0];
[results close];
}];
return shortName;
}
- (NSString *)longDescription
{
__block NSString *description;
[queue inDatabase:^(FMDatabase *db)
{
FMResultSet *results = [db executeQuery:@"select value from metadata where name = 'description'"];
if ([db hadError])
description = nil;
[results next];
description = [results stringForColumnIndex:0];
[results close];
}];
return [NSString stringWithFormat:@"%@ - %@", [self shortName], description];
}
- (NSString *)shortAttribution
{
__block NSString *attribution;
[queue inDatabase:^(FMDatabase *db)
{
FMResultSet *results = [db executeQuery:@"select value from metadata where name = 'attribution'"];
if ([db hadError])
attribution = @"Unknown MBTiles attribution";
[results next];
attribution = [results stringForColumnIndex:0];
[results close];
}];
return attribution;
}
- (NSString *)longAttribution
{
return [NSString stringWithFormat:@"%@ - %@", [self shortName], [self shortAttribution]];
}
- (void)removeAllCachedImages
{
NSLog(@"*** removeAllCachedImages in %@", [self class]);
}
@end
\ No newline at end of file
... ...
... ... @@ -138,6 +138,12 @@
DD2B374814CF8041008DE8CB /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DD2B374214CF8041008DE8CB /* FMDatabaseAdditions.m */; };
DD2B374914CF8041008DE8CB /* FMResultSet.h in Headers */ = {isa = PBXBuildFile; fileRef = DD2B374314CF8041008DE8CB /* FMResultSet.h */; };
DD2B374A14CF8041008DE8CB /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = DD2B374414CF8041008DE8CB /* FMResultSet.m */; };
DD2B374F14CF814F008DE8CB /* FMDatabasePool.h in Headers */ = {isa = PBXBuildFile; fileRef = DD2B374B14CF814F008DE8CB /* FMDatabasePool.h */; };
DD2B375014CF814F008DE8CB /* FMDatabasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = DD2B374C14CF814F008DE8CB /* FMDatabasePool.m */; };
DD2B375114CF814F008DE8CB /* FMDatabaseQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = DD2B374D14CF814F008DE8CB /* FMDatabaseQueue.h */; };
DD2B375214CF814F008DE8CB /* FMDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DD2B374E14CF814F008DE8CB /* FMDatabaseQueue.m */; };
DD2B375514CF8197008DE8CB /* RMMBTilesTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DD2B375314CF8197008DE8CB /* RMMBTilesTileSource.h */; };
DD2B375614CF8197008DE8CB /* RMMBTilesTileSource.m in Sources */ = {isa = PBXBuildFile; fileRef = DD2B375414CF8197008DE8CB /* RMMBTilesTileSource.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
... ... @@ -275,6 +281,12 @@
DD2B374214CF8041008DE8CB /* FMDatabaseAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMDatabaseAdditions.m; path = src/FMDatabaseAdditions.m; sourceTree = "<group>"; };
DD2B374314CF8041008DE8CB /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMResultSet.h; path = src/FMResultSet.h; sourceTree = "<group>"; };
DD2B374414CF8041008DE8CB /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMResultSet.m; path = src/FMResultSet.m; sourceTree = "<group>"; };
DD2B374B14CF814F008DE8CB /* FMDatabasePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMDatabasePool.h; path = src/FMDatabasePool.h; sourceTree = "<group>"; };
DD2B374C14CF814F008DE8CB /* FMDatabasePool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMDatabasePool.m; path = src/FMDatabasePool.m; sourceTree = "<group>"; };
DD2B374D14CF814F008DE8CB /* FMDatabaseQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMDatabaseQueue.h; path = src/FMDatabaseQueue.h; sourceTree = "<group>"; };
DD2B374E14CF814F008DE8CB /* FMDatabaseQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMDatabaseQueue.m; path = src/FMDatabaseQueue.m; sourceTree = "<group>"; };
DD2B375314CF8197008DE8CB /* RMMBTilesTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMBTilesTileSource.h; sourceTree = "<group>"; };
DD2B375414CF8197008DE8CB /* RMMBTilesTileSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMMBTilesTileSource.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
... ... @@ -485,6 +497,8 @@
16EC85CD133CA6C300219947 /* RMAbstractMercatorTileSource.m */,
16EC85CE133CA6C300219947 /* RMAbstractWebMapSource.h */,
16EC85CF133CA6C300219947 /* RMAbstractWebMapSource.m */,
DD2B375314CF8197008DE8CB /* RMMBTilesTileSource.h */,
DD2B375414CF8197008DE8CB /* RMMBTilesTileSource.m */,
);
name = "Tile Source";
sourceTree = "<group>";
... ... @@ -508,6 +522,10 @@
DD2B374214CF8041008DE8CB /* FMDatabaseAdditions.m */,
DD2B374314CF8041008DE8CB /* FMResultSet.h */,
DD2B374414CF8041008DE8CB /* FMResultSet.m */,
DD2B374B14CF814F008DE8CB /* FMDatabasePool.h */,
DD2B374C14CF814F008DE8CB /* FMDatabasePool.m */,
DD2B374D14CF814F008DE8CB /* FMDatabaseQueue.h */,
DD2B374E14CF814F008DE8CB /* FMDatabaseQueue.m */,
);
path = FMDB;
sourceTree = "<group>";
... ... @@ -620,6 +638,9 @@
DD2B374514CF8041008DE8CB /* FMDatabase.h in Headers */,
DD2B374714CF8041008DE8CB /* FMDatabaseAdditions.h in Headers */,
DD2B374914CF8041008DE8CB /* FMResultSet.h in Headers */,
DD2B374F14CF814F008DE8CB /* FMDatabasePool.h in Headers */,
DD2B375114CF814F008DE8CB /* FMDatabaseQueue.h in Headers */,
DD2B375514CF8197008DE8CB /* RMMBTilesTileSource.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ... @@ -830,6 +851,9 @@
DD2B374614CF8041008DE8CB /* FMDatabase.m in Sources */,
DD2B374814CF8041008DE8CB /* FMDatabaseAdditions.m in Sources */,
DD2B374A14CF8041008DE8CB /* FMResultSet.m in Sources */,
DD2B375014CF814F008DE8CB /* FMDatabasePool.m in Sources */,
DD2B375214CF814F008DE8CB /* FMDatabaseQueue.m in Sources */,
DD2B375614CF8197008DE8CB /* RMMBTilesTileSource.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ...