Authored by Justin R. Miller

enhancements for interactivity & composite sources

* added RMCompositeSource (Core Graphics compositing)
 * added map view interactivity querying (not just sources)
 * interactivity parsing cleanups
 * TMS/XYZ scheme cleanups
  1 +//
  2 +// RMCompositeSource.h
  3 +// MapView
  4 +//
  5 +// Created by Justin Miller on 4/19/12.
  6 +// Copyright (c) 2012 MapBox / Development Seed. All rights reserved.
  7 +//
  8 +
  9 +#import "RMTileSource.h"
  10 +
  11 +@interface RMCompositeSource : NSObject <RMTileSource>
  12 +{
  13 + RMFractalTileProjection *tileProjection;
  14 +}
  15 +
  16 +@property (nonatomic, retain) NSMutableArray *compositeSources;
  17 +
  18 +- (id)initWithTileSource:(id <RMTileSource>)initialTileSource;
  19 +
  20 +- (UIImage *)imageForTile:(RMTile)tile inCache:(RMTileCache *)tileCache;
  21 +- (void)cancelAllDownloads;
  22 +
  23 +- (RMFractalTileProjection *)mercatorToTileProjection;
  24 +- (RMProjection *)projection;
  25 +
  26 +- (float)minZoom;
  27 +- (void)setMinZoom:(NSUInteger)aMinZoom;
  28 +
  29 +- (float)maxZoom;
  30 +- (void)setMaxZoom:(NSUInteger)aMaxZoom;
  31 +
  32 +- (int)tileSideLength;
  33 +- (void)setTileSideLength:(NSUInteger)aTileSideLength;
  34 +
  35 +- (RMSphericalTrapezium)latitudeLongitudeBoundingBox;
  36 +
  37 +- (NSString *)uniqueTilecacheKey;
  38 +
  39 +- (NSString *)shortName;
  40 +- (NSString *)longDescription;
  41 +- (NSString *)shortAttribution;
  42 +- (NSString *)longAttribution;
  43 +
  44 +- (void)didReceiveMemoryWarning;
  45 +
  46 +@end
  1 +//
  2 +// RMCompositeSource.m
  3 +// MapView
  4 +//
  5 +// Created by Justin Miller on 4/19/12.
  6 +// Copyright (c) 2012 MapBox / Development Seed. All rights reserved.
  7 +//
  8 +
  9 +#import "RMCompositeSource.h"
  10 +
  11 +#import "RMAbstractMercatorTileSource.h"
  12 +
  13 +@implementation RMCompositeSource
  14 +
  15 +@synthesize compositeSources;
  16 +
  17 +- (id)initWithTileSource:(id <RMTileSource>)initialTileSource
  18 +{
  19 + self = [super init];
  20 +
  21 + if (self)
  22 + compositeSources = [[NSMutableArray arrayWithObject:initialTileSource] retain];
  23 +
  24 + return self;
  25 +}
  26 +
  27 +- (void)dealloc
  28 +{
  29 + [compositeSources release];
  30 +
  31 + [super dealloc];
  32 +}
  33 +
  34 +- (UIImage *)imageForTile:(RMTile)tile inCache:(RMTileCache *)tileCache
  35 +{
  36 + // FIXME: cache
  37 +
  38 + UIImage *image = nil;
  39 +
  40 + for (id <RMTileSource>tileSource in self.compositeSources)
  41 + {
  42 + UIImage *sourceImage = [tileSource imageForTile:tile inCache:tileCache];
  43 +
  44 + if (sourceImage)
  45 + {
  46 + if (image != nil)
  47 + {
  48 + UIGraphicsBeginImageContext(image.size);
  49 +
  50 + [image drawAtPoint:CGPointMake(0,0)];
  51 +
  52 + [sourceImage drawAtPoint:CGPointMake(0,0)];
  53 +
  54 + image = UIGraphicsGetImageFromCurrentImageContext();
  55 +
  56 + UIGraphicsEndImageContext();
  57 + }
  58 + else
  59 + {
  60 + image = sourceImage;
  61 + }
  62 + }
  63 + }
  64 +
  65 + return image;
  66 +}
  67 +
  68 +- (void)cancelAllDownloads
  69 +{
  70 + NSLog(@"cancelAllDownloads");
  71 +}
  72 +
  73 +- (RMFractalTileProjection *)mercatorToTileProjection
  74 +{
  75 + if ( ! tileProjection)
  76 + {
  77 + tileProjection = [[RMFractalTileProjection alloc] initFromProjection:[self projection]
  78 + tileSideLength:[self tileSideLength]
  79 + maxZoom:[self maxZoom]
  80 + minZoom:[self minZoom]];
  81 + }
  82 +
  83 + return tileProjection;
  84 +}
  85 +
  86 +- (RMProjection *)projection
  87 +{
  88 + return [RMProjection googleProjection];
  89 +}
  90 +
  91 +- (float)minZoom
  92 +{
  93 + return kDefaultMinTileZoom;
  94 +}
  95 +
  96 +- (void)setMinZoom:(NSUInteger)aMinZoom
  97 +{
  98 + NSLog(@"setMinZoom:");
  99 +}
  100 +
  101 +- (float)maxZoom
  102 +{
  103 + return kDefaultMaxTileZoom;
  104 +}
  105 +
  106 +- (void)setMaxZoom:(NSUInteger)aMaxZoom
  107 +{
  108 + NSLog(@"setMaxZoom:");
  109 +}
  110 +
  111 +- (int)tileSideLength
  112 +{
  113 + return kDefaultTileSize;
  114 +}
  115 +
  116 +- (void)setTileSideLength:(NSUInteger)aTileSideLength
  117 +{
  118 + NSLog(@"setTileSideLength:");
  119 +}
  120 +
  121 +- (RMSphericalTrapezium)latitudeLongitudeBoundingBox
  122 +{
  123 + return kDefaultLatLonBoundingBox;
  124 +}
  125 +
  126 +- (NSString *)uniqueTilecacheKey
  127 +{
  128 + return NSStringFromClass([self class]);
  129 +}
  130 +
  131 +- (NSString *)shortName
  132 +{
  133 + return @"shortName";
  134 +}
  135 +
  136 +- (NSString *)longDescription
  137 +{
  138 + return @"longDescription";
  139 +}
  140 +
  141 +- (NSString *)shortAttribution
  142 +{
  143 + return @"shortAttribution";
  144 +}
  145 +
  146 +- (NSString *)longAttribution
  147 +{
  148 + return @"longAttribution";
  149 +}
  150 +
  151 +- (void)didReceiveMemoryWarning
  152 +{
  153 + NSLog(@"didReceiveMemoryWarning");
  154 +}
  155 +
  156 +@end
@@ -33,11 +33,10 @@ @@ -33,11 +33,10 @@
33 // Based on the UTFGrid specification: https://github.com/mapbox/utfgrid-spec 33 // Based on the UTFGrid specification: https://github.com/mapbox/utfgrid-spec
34 // 34 //
35 35
  36 +#import "RMMapView.h"
36 #import "RMMBTilesSource.h" 37 #import "RMMBTilesSource.h"
37 #import "RMMapBoxSource.h" 38 #import "RMMapBoxSource.h"
38 39
39 -@class RMMapView;  
40 -  
41 // Interactivity currently supports two types of output: 'teaser' 40 // Interactivity currently supports two types of output: 'teaser'
42 // and 'full'. Ideal for master/detail interfaces or for showing 41 // and 'full'. Ideal for master/detail interfaces or for showing
43 // a MapKit-style detail-toggling point callout. 42 // a MapKit-style detail-toggling point callout.
@@ -47,6 +46,31 @@ typedef enum { @@ -47,6 +46,31 @@ typedef enum {
47 RMInteractiveSourceOutputTypeFull = 1, 46 RMInteractiveSourceOutputTypeFull = 1,
48 } RMInteractiveSourceOutputType; 47 } RMInteractiveSourceOutputType;
49 48
  49 +@protocol RMInteractiveMapView
  50 +
  51 +@required
  52 +
  53 +// Query if a map view supports interactivity features.
  54 +//
  55 +- (BOOL)supportsInteractivity;
  56 +
  57 +// Get the HTML-formatted output for a given point on a given map view.
  58 +//
  59 +- (NSString *)formattedOutputOfType:(RMInteractiveSourceOutputType)outputType forPoint:(CGPoint)point;
  60 +
  61 +@end
  62 +
  63 +#pragma mark -
  64 +
  65 +@interface RMMapView (RMInteractiveSource) <RMInteractiveMapView>
  66 +
  67 +- (BOOL)supportsInteractivity;
  68 +- (NSString *)formattedOutputOfType:(RMInteractiveSourceOutputType)outputType forPoint:(CGPoint)point;
  69 +
  70 +@end
  71 +
  72 +#pragma mark -
  73 +
50 @protocol RMInteractiveSource 74 @protocol RMInteractiveSource
51 75
52 @required 76 @required
@@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
33 33
34 #import "RMInteractiveSource.h" 34 #import "RMInteractiveSource.h"
35 35
36 -#import "RMMapView.h" 36 +#import "RMCompositeSource.h"
37 37
38 #import "FMDatabase.h" 38 #import "FMDatabase.h"
39 #import "FMDatabaseQueue.h" 39 #import "FMDatabaseQueue.h"
@@ -185,7 +185,7 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa @@ -185,7 +185,7 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa
185 { 185 {
186 NSString *formattedOutput = nil; 186 NSString *formattedOutput = nil;
187 187
188 - id <RMTileSource>source = mapView.tileSource; 188 + id <RMTileSource, RMInteractiveSource>source = [mapView interactiveTileSource];
189 189
190 NSDictionary *interactivityDictionary = [(id <RMInteractiveSourcePrivate>)source interactivityDictionaryForPoint:point inMapView:mapView]; 190 NSDictionary *interactivityDictionary = [(id <RMInteractiveSourcePrivate>)source interactivityDictionaryForPoint:point inMapView:mapView];
191 191
@@ -291,6 +291,78 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa @@ -291,6 +291,78 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa
291 @end 291 @end
292 292
293 #pragma mark - 293 #pragma mark -
  294 +#pragma mark RMMapView Interactivity
  295 +
  296 +@interface RMMapView (RMInteractiveSourcePrivate) <RMInteractiveSourcePrivate>
  297 +
  298 +- (id <RMTileSource, RMInteractiveSource>)interactiveTileSource;
  299 +- (NSDictionary *)interactivityDictionaryForPoint:(CGPoint)point;
  300 +- (NSString *)interactivityFormatterTemplate;
  301 +
  302 +@end
  303 +
  304 +@implementation RMMapView (RMInteractiveSource)
  305 +
  306 +- (id <RMTileSource, RMInteractiveSource>)interactiveTileSource
  307 +{
  308 + id <RMTileSource, RMInteractiveSource>interactiveTileSource = nil;
  309 +
  310 + if ([self.tileSource isKindOfClass:[RMCompositeSource class]])
  311 + {
  312 + // currently, we iterate top-down and return the first interactive source
  313 + //
  314 + for (id <RMTileSource>source in [[((RMCompositeSource *)self.tileSource).compositeSources reverseObjectEnumerator] allObjects])
  315 + {
  316 + NSLog(@"checking %@", source);
  317 +
  318 + if (([source isKindOfClass:[RMMBTilesSource class]] || [source isKindOfClass:[RMMapBoxSource class]]) &&
  319 + [source conformsToProtocol:@protocol(RMInteractiveSource)] &&
  320 + [(id <RMInteractiveSource>)source supportsInteractivity])
  321 + {
  322 + interactiveTileSource = (id <RMTileSource, RMInteractiveSource>)source;
  323 +
  324 + break;
  325 + }
  326 + }
  327 + }
  328 + else
  329 + {
  330 + if (([self.tileSource isKindOfClass:[RMMBTilesSource class]] || [self.tileSource isKindOfClass:[RMMapBoxSource class]]) &&
  331 + [self.tileSource conformsToProtocol:@protocol(RMInteractiveSource)] &&
  332 + [(id <RMInteractiveSource>)self.tileSource supportsInteractivity])
  333 + {
  334 + interactiveTileSource = (id <RMTileSource, RMInteractiveSource>)self.tileSource;
  335 + }
  336 + }
  337 +
  338 + NSLog(@"determined interactive source as %@", interactiveTileSource);
  339 +
  340 + return interactiveTileSource;
  341 +}
  342 +
  343 +- (BOOL)supportsInteractivity
  344 +{
  345 + return ([self interactiveTileSource] != nil);
  346 +}
  347 +
  348 +- (NSDictionary *)interactivityDictionaryForPoint:(CGPoint)point
  349 +{
  350 + return [(id <RMInteractiveSourcePrivate>)[self interactiveTileSource] interactivityDictionaryForPoint:point inMapView:self];
  351 +}
  352 +
  353 +- (NSString *)interactivityFormatterTemplate
  354 +{
  355 + return [(id <RMInteractiveSourcePrivate>)[self interactiveTileSource] interactivityFormatterTemplate];
  356 +}
  357 +
  358 +- (NSString *)formattedOutputOfType:(RMInteractiveSourceOutputType)outputType forPoint:(CGPoint)point
  359 +{
  360 + return [(id <RMInteractiveSourcePrivate>)[self interactiveTileSource] formattedOutputOfType:outputType forPoint:point inMapView:self];
  361 +}
  362 +
  363 +@end
  364 +
  365 +#pragma mark -
294 #pragma mark MBTiles Interactivity 366 #pragma mark MBTiles Interactivity
295 367
296 @interface RMMBTilesSource (RMInteractiveSourcePrivate) <RMInteractiveSourcePrivate> 368 @interface RMMBTilesSource (RMInteractiveSourcePrivate) <RMInteractiveSourcePrivate>
@@ -414,7 +486,7 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa @@ -414,7 +486,7 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa
414 [results close]; 486 [results close];
415 }]; 487 }];
416 488
417 - return template; 489 + return ([template length] ? template : nil);
418 } 490 }
419 491
420 - (NSString *)formattedOutputOfType:(RMInteractiveSourceOutputType)outputType forPoint:(CGPoint)point inMapView:(RMMapView *)mapView 492 - (NSString *)formattedOutputOfType:(RMInteractiveSourceOutputType)outputType forPoint:(CGPoint)point inMapView:(RMMapView *)mapView
@@ -459,7 +531,14 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa @@ -459,7 +531,14 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa
459 531
460 - (NSDictionary *)interactivityDictionaryForPoint:(CGPoint)point inMapView:(RMMapView *)mapView; 532 - (NSDictionary *)interactivityDictionaryForPoint:(CGPoint)point inMapView:(RMMapView *)mapView;
461 { 533 {
462 - if ([self.infoDictionary objectForKey:@"grids"]) 534 + NSString *gridURLString = nil;
  535 +
  536 + if ([self.infoDictionary objectForKey:@"grids"] && [[self.infoDictionary objectForKey:@"grids"] isKindOfClass:[NSArray class]])
  537 + gridURLString = [[self.infoDictionary objectForKey:@"grids"] objectAtIndex:0];
  538 + else
  539 + gridURLString = [self.infoDictionary objectForKey:@"gridURL"];
  540 +
  541 + if ([gridURLString length])
463 { 542 {
464 RMTilePoint tilePoint = RMInteractiveSourceNormalizedTilePointForMapView(point, mapView); 543 RMTilePoint tilePoint = RMInteractiveSourceNormalizedTilePointForMapView(point, mapView);
465 544
@@ -467,8 +546,6 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa @@ -467,8 +546,6 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa
467 NSInteger x = tilePoint.tile.x; 546 NSInteger x = tilePoint.tile.x;
468 NSInteger y = tilePoint.tile.y; 547 NSInteger y = tilePoint.tile.y;
469 548
470 - NSString *gridURLString = [[self.infoDictionary objectForKey:@"grids"] objectAtIndex:0];  
471 -  
472 gridURLString = [gridURLString stringByReplacingOccurrencesOfString:@"{z}" withString:[[NSNumber numberWithInteger:zoom] stringValue]]; 549 gridURLString = [gridURLString stringByReplacingOccurrencesOfString:@"{z}" withString:[[NSNumber numberWithInteger:zoom] stringValue]];
473 gridURLString = [gridURLString stringByReplacingOccurrencesOfString:@"{x}" withString:[[NSNumber numberWithInteger:x] stringValue]]; 550 gridURLString = [gridURLString stringByReplacingOccurrencesOfString:@"{x}" withString:[[NSNumber numberWithInteger:x] stringValue]];
474 gridURLString = [gridURLString stringByReplacingOccurrencesOfString:@"{y}" withString:[[NSNumber numberWithInteger:y] stringValue]]; 551 gridURLString = [gridURLString stringByReplacingOccurrencesOfString:@"{y}" withString:[[NSNumber numberWithInteger:y] stringValue]];
@@ -525,7 +602,7 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa @@ -525,7 +602,7 @@ RMTilePoint RMInteractiveSourceNormalizedTilePointForMapView(CGPoint point, RMMa
525 602
526 - (NSString *)interactivityFormatterTemplate 603 - (NSString *)interactivityFormatterTemplate
527 { 604 {
528 - if ([self.infoDictionary objectForKey:@"template"]) 605 + if ([self.infoDictionary objectForKey:@"template"] && [[self.infoDictionary objectForKey:@"template"] length])
529 return [self.infoDictionary objectForKey:@"template"]; 606 return [self.infoDictionary objectForKey:@"template"];
530 607
531 return nil; 608 return nil;
@@ -49,8 +49,6 @@ @@ -49,8 +49,6 @@
49 { 49 {
50 if (self = [super init]) 50 if (self = [super init])
51 { 51 {
52 - NSAssert([NSJSONSerialization class], @"JSON serialization not supported by SDK");  
53 -  
54 infoDictionary = (NSDictionary *)[[NSJSONSerialization JSONObjectWithData:[tileJSON dataUsingEncoding:NSUTF8StringEncoding] 52 infoDictionary = (NSDictionary *)[[NSJSONSerialization JSONObjectWithData:[tileJSON dataUsingEncoding:NSUTF8StringEncoding]
55 options:0 53 options:0
56 error:nil] retain]; 54 error:nil] retain];
@@ -84,8 +82,18 @@ @@ -84,8 +82,18 @@
84 if ([[referenceURL pathExtension] isEqualToString:@"json"] && (dataObject = [NSString stringWithContentsOfURL:referenceURL encoding:NSUTF8StringEncoding error:nil]) && dataObject) 82 if ([[referenceURL pathExtension] isEqualToString:@"json"] && (dataObject = [NSString stringWithContentsOfURL:referenceURL encoding:NSUTF8StringEncoding error:nil]) && dataObject)
85 return [self initWithTileJSON:dataObject]; 83 return [self initWithTileJSON:dataObject];
86 84
87 - else if ([[referenceURL pathExtension] isEqualToString:@"plist"] && (dataObject = [[[NSDictionary alloc] initWithContentsOfURL:referenceURL] autorelease]) && dataObject)  
88 - return [self initWithInfo:dataObject]; 85 + else if ([[referenceURL pathExtension] isEqualToString:@"plist"])
  86 + {
  87 + NSMutableDictionary *mutableInfoDictionary = [NSMutableDictionary dictionaryWithContentsOfURL:referenceURL];
  88 +
  89 + if (mutableInfoDictionary)
  90 + {
  91 + if ( ! [mutableInfoDictionary objectForKey:@"scheme"])
  92 + [mutableInfoDictionary setObject:@"tms" forKey:@"scheme"]; // assume older plists are TMS, not XYZ per TileJSON default
  93 +
  94 + return [self initWithInfo:mutableInfoDictionary];
  95 + }
  96 + }
89 97
90 return nil; 98 return nil;
91 } 99 }
@@ -136,6 +136,8 @@ @@ -136,6 +136,8 @@
136 B8F3FC650EA2E792004D8F85 /* RMMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F3FC630EA2E792004D8F85 /* RMMarker.m */; }; 136 B8F3FC650EA2E792004D8F85 /* RMMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F3FC630EA2E792004D8F85 /* RMMarker.m */; };
137 D1437B36122869E400888DAE /* RMDBMapSource.m in Sources */ = {isa = PBXBuildFile; fileRef = D1437B32122869E400888DAE /* RMDBMapSource.m */; }; 137 D1437B36122869E400888DAE /* RMDBMapSource.m in Sources */ = {isa = PBXBuildFile; fileRef = D1437B32122869E400888DAE /* RMDBMapSource.m */; };
138 D1437B37122869E400888DAE /* RMDBMapSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D1437B33122869E400888DAE /* RMDBMapSource.h */; }; 138 D1437B37122869E400888DAE /* RMDBMapSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D1437B33122869E400888DAE /* RMDBMapSource.h */; };
  139 + DD103E241540E3CF00AA65DD /* RMCompositeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DD103E221540E3CF00AA65DD /* RMCompositeSource.h */; };
  140 + DD103E251540E3CF00AA65DD /* RMCompositeSource.m in Sources */ = {isa = PBXBuildFile; fileRef = DD103E231540E3CF00AA65DD /* RMCompositeSource.m */; };
139 DD2B374514CF8041008DE8CB /* FMDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = DD2B373F14CF8041008DE8CB /* FMDatabase.h */; }; 141 DD2B374514CF8041008DE8CB /* FMDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = DD2B373F14CF8041008DE8CB /* FMDatabase.h */; };
140 DD2B374614CF8041008DE8CB /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = DD2B374014CF8041008DE8CB /* FMDatabase.m */; }; 142 DD2B374614CF8041008DE8CB /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = DD2B374014CF8041008DE8CB /* FMDatabase.m */; };
141 DD2B374714CF8041008DE8CB /* FMDatabaseAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DD2B374114CF8041008DE8CB /* FMDatabaseAdditions.h */; }; 143 DD2B374714CF8041008DE8CB /* FMDatabaseAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DD2B374114CF8041008DE8CB /* FMDatabaseAdditions.h */; };
@@ -301,6 +303,8 @@ @@ -301,6 +303,8 @@
301 B8F3FC630EA2E792004D8F85 /* RMMarker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMMarker.m; sourceTree = "<group>"; }; 303 B8F3FC630EA2E792004D8F85 /* RMMarker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMMarker.m; sourceTree = "<group>"; };
302 D1437B32122869E400888DAE /* RMDBMapSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMDBMapSource.m; sourceTree = "<group>"; }; 304 D1437B32122869E400888DAE /* RMDBMapSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMDBMapSource.m; sourceTree = "<group>"; };
303 D1437B33122869E400888DAE /* RMDBMapSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMDBMapSource.h; sourceTree = "<group>"; }; 305 D1437B33122869E400888DAE /* RMDBMapSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMDBMapSource.h; sourceTree = "<group>"; };
  306 + DD103E221540E3CF00AA65DD /* RMCompositeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMCompositeSource.h; sourceTree = "<group>"; };
  307 + DD103E231540E3CF00AA65DD /* RMCompositeSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMCompositeSource.m; sourceTree = "<group>"; };
304 DD2B373F14CF8041008DE8CB /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = "<group>"; }; 308 DD2B373F14CF8041008DE8CB /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = "<group>"; };
305 DD2B374014CF8041008DE8CB /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = "<group>"; }; 309 DD2B374014CF8041008DE8CB /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = "<group>"; };
306 DD2B374114CF8041008DE8CB /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseAdditions.h; sourceTree = "<group>"; }; 310 DD2B374114CF8041008DE8CB /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseAdditions.h; sourceTree = "<group>"; };
@@ -397,6 +401,8 @@ @@ -397,6 +401,8 @@
397 DD2B375414CF8197008DE8CB /* RMMBTilesSource.m */, 401 DD2B375414CF8197008DE8CB /* RMMBTilesSource.m */,
398 DD6380DB152E72880074E66E /* RMMapBoxSource.h */, 402 DD6380DB152E72880074E66E /* RMMapBoxSource.h */,
399 DD6380DC152E72880074E66E /* RMMapBoxSource.m */, 403 DD6380DC152E72880074E66E /* RMMapBoxSource.m */,
  404 + DD103E221540E3CF00AA65DD /* RMCompositeSource.h */,
  405 + DD103E231540E3CF00AA65DD /* RMCompositeSource.m */,
400 ); 406 );
401 name = "Map sources"; 407 name = "Map sources";
402 sourceTree = "<group>"; 408 sourceTree = "<group>";
@@ -749,6 +755,7 @@ @@ -749,6 +755,7 @@
749 DDC4BEEB152E3DE700089409 /* GRMustacheTemplateRepository.h in Headers */, 755 DDC4BEEB152E3DE700089409 /* GRMustacheTemplateRepository.h in Headers */,
750 DDC4BEEC152E3DE700089409 /* GRMustacheVersion.h in Headers */, 756 DDC4BEEC152E3DE700089409 /* GRMustacheVersion.h in Headers */,
751 DD6380DD152E72880074E66E /* RMMapBoxSource.h in Headers */, 757 DD6380DD152E72880074E66E /* RMMapBoxSource.h in Headers */,
  758 + DD103E241540E3CF00AA65DD /* RMCompositeSource.h in Headers */,
752 ); 759 );
753 runOnlyForDeploymentPostprocessing = 0; 760 runOnlyForDeploymentPostprocessing = 0;
754 }; 761 };
@@ -967,6 +974,7 @@ @@ -967,6 +974,7 @@
967 16FFF2CC14E3DBF700A170EC /* RMMapQuestOpenAerialSource.m in Sources */, 974 16FFF2CC14E3DBF700A170EC /* RMMapQuestOpenAerialSource.m in Sources */,
968 DD6380DE152E72880074E66E /* RMMapBoxSource.m in Sources */, 975 DD6380DE152E72880074E66E /* RMMapBoxSource.m in Sources */,
969 DDC4BEF2152E3FAE00089409 /* RMInteractiveSource.m in Sources */, 976 DDC4BEF2152E3FAE00089409 /* RMInteractiveSource.m in Sources */,
  977 + DD103E251540E3CF00AA65DD /* RMCompositeSource.m in Sources */,
970 ); 978 );
971 runOnlyForDeploymentPostprocessing = 0; 979 runOnlyForDeploymentPostprocessing = 0;
972 }; 980 };