PageRenderTime 45ms CodeModel.GetById 1ms app.highlight 40ms RepoModel.GetById 1ms app.codeStats 0ms

/MapView/Map/RMWebTileImage.m

http://github.com/route-me/route-me
Objective C | 287 lines | 190 code | 64 blank | 33 comment | 29 complexity | b9f38c92e98ecfcaaffdbbc6aaddce1b MD5 | raw file
  1//
  2//  RMWebTileImage.m
  3//
  4// Copyright (c) 2008-2009, Route-Me Contributors
  5// All rights reserved.
  6//
  7// Redistribution and use in source and binary forms, with or without
  8// modification, are permitted provided that the following conditions are met:
  9//
 10// * Redistributions of source code must retain the above copyright notice, this
 11//   list of conditions and the following disclaimer.
 12// * Redistributions in binary form must reproduce the above copyright notice,
 13//   this list of conditions and the following disclaimer in the documentation
 14//   and/or other materials provided with the distribution.
 15//
 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 17// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 18// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 19// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 20// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 21// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 22// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 23// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 24// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 25// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 26// POSSIBILITY OF SUCH DAMAGE.
 27
 28#import "RMWebTileImage.h"
 29#import <QuartzCore/CALayer.h>
 30
 31#import "RMMapContents.h"
 32#import "RMTileLoader.h"
 33
 34NSString *RMWebTileImageErrorDomain = @"RMWebTileImageErrorDomain";
 35NSString *RMWebTileImageHTTPResponseCodeKey = @"RMWebTileImageHTTPResponseCodeKey";
 36NSString *RMWebTileImageNotificationErrorKey = @"RMWebTileImageNotificationErrorKey";
 37
 38static NSOperationQueue *_queue = nil;
 39
 40@interface RMWebTileImage () <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
 41- (void)requestTile;
 42- (void)startLoading:(NSTimer *)timer;
 43@end
 44
 45@implementation RMWebTileImage
 46
 47+ (void) initialize
 48{
 49    _queue = [[NSOperationQueue alloc] init];
 50    [_queue setMaxConcurrentOperationCount:kMaxConcurrentConnections];
 51}
 52
 53- (id) initWithTile: (RMTile)_tile FromURL:(NSString*)urlStr
 54{
 55	if (![super initWithTile:_tile])
 56		return nil;
 57
 58	[super displayProxy:[RMTileProxy loadingTile]];
 59
 60	
 61	url = [[NSURL alloc] initWithString:urlStr];
 62
 63
 64    connectionOp = nil;
 65		
 66    data =[[NSMutableData alloc] initWithCapacity:0];
 67    
 68	retries = kWebTileRetries;
 69	
 70	[[NSNotificationCenter defaultCenter] postNotificationName:RMTileRequested object:self];
 71
 72	[self requestTile];
 73	
 74	return self;
 75}
 76
 77- (void) dealloc
 78{
 79	[self cancelLoading];
 80	
 81    if ( lastError ) [lastError release]; lastError = nil;
 82	
 83    [data release];
 84    data = nil;
 85    
 86	[url release];
 87	url = nil;
 88	
 89	[super dealloc];
 90}
 91
 92- (void) requestTile
 93{
 94	//RMLog(@"fetching: %@", url);
 95	if (connectionOp) // re-request
 96	{
 97		//RMLog(@"Refetching: %@: %d", url, retries);
 98		
 99        [connectionOp cancel];
100		[connectionOp release];
101		connectionOp = nil;
102
103		if(retries == 0) // No more retries
104		{
105			[super displayProxy:[RMTileProxy errorTile]];
106			[[NSNotificationCenter defaultCenter] postNotificationName:RMTileRetrieved object:self];
107
108			[[NSNotificationCenter defaultCenter] postNotificationName:RMTileError object:self userInfo:[NSDictionary dictionaryWithObject:lastError forKey:RMWebTileImageNotificationErrorKey]];
109            [lastError autorelease]; lastError = nil;
110
111			return;
112		}
113		retries--;		
114
115		[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(startLoading:) userInfo:nil repeats:NO];		
116	}
117	else 
118	{
119		[self startLoading:nil];
120	}
121}
122
123- (void)startLoading:(NSTimer *)timer
124{
125	NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
126
127    connectionOp = [[RMURLConnectionOperation alloc] initWithRequest:request delegate:self];
128    
129	if (!connectionOp)
130	{
131		[super displayProxy:[RMTileProxy errorTile]];
132		[[NSNotificationCenter defaultCenter] postNotificationName:RMTileRetrieved object:self];
133	} else {
134        [_queue addOperation:connectionOp];
135    }
136}
137
138- (void) cancelLoading
139{
140	if (!connectionOp)
141		return;
142	
143	[[NSNotificationCenter defaultCenter] postNotificationName:RMTileRetrieved object:self];
144	
145    [connectionOp cancel];	
146	[connectionOp release];
147	connectionOp = nil;
148    
149    if (lastError) [lastError release]; lastError = nil;
150    
151	[super cancelLoading];
152}
153
154
155#pragma mark - NSURLConnectionDelegate
156
157- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
158{
159    int statusCode = NSURLErrorUnknown; // unknown
160    
161	if ([response isKindOfClass:[NSHTTPURLResponse class]])
162        statusCode = [(NSHTTPURLResponse*)response statusCode];
163	
164    [data setLength:0];
165    
166    /// \bug magic number
167	if(statusCode < 400) { // Success
168	}
169    
170    /// \bug magic number
171	else if (statusCode == 404) { // Not Found
172	    [super displayProxy:[RMTileProxy missingTile]];
173        
174        NSError *error = [NSError errorWithDomain:RMWebTileImageErrorDomain 
175                                             code:RMWebTileImageErrorNotFoundResponse
176                                         userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
177                                                   NSLocalizedString(@"The requested tile was not found on the server", @""), NSLocalizedDescriptionKey, nil]];
178        
179        [[NSNotificationCenter defaultCenter] postNotificationName:RMTileError object:self userInfo:[NSDictionary dictionaryWithObject:error forKey:RMWebTileImageNotificationErrorKey]];
180		[self cancelLoading];
181	}
182    
183	else {  // Other Error
184        //RMLog(@"didReceiveResponse %@ %d", _connection, statusCode);
185
186		BOOL retry = FALSE;
187		
188		switch(statusCode) {
189                /// \bug magic number
190			case 500: retry = TRUE; break;
191			case 503: retry = TRUE; break;
192		}
193		
194        NSError *error = [NSError errorWithDomain:RMWebTileImageErrorDomain 
195                                             code:RMWebTileImageErrorUnexpectedHTTPResponse 
196                                         userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
197                                                   [NSNumber numberWithInt:statusCode], RMWebTileImageHTTPResponseCodeKey,
198                                                   [NSString stringWithFormat:NSLocalizedString(@"The server returned error code %d", @""), statusCode], NSLocalizedDescriptionKey, nil]];
199        
200		if (retry) {
201            if ( lastError ) [lastError release];
202            lastError = [error retain];
203			[self requestTile];
204		} else {
205			[[NSNotificationCenter defaultCenter] postNotificationName:RMTileError object:self userInfo:[NSDictionary dictionaryWithObject:error forKey:RMWebTileImageNotificationErrorKey]];
206			[self cancelLoading];
207		}
208	}
209}
210
211- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)newData
212{
213	[data appendData:newData];
214}
215
216- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
217{
218    BOOL retry = FALSE;
219	
220	switch([error code])
221	{
222        case NSURLErrorBadURL:                      // -1000
223        case NSURLErrorTimedOut:                    // -1001
224        case NSURLErrorUnsupportedURL:              // -1002
225        case NSURLErrorCannotFindHost:              // -1003
226        case NSURLErrorCannotConnectToHost:         // -1004
227        case NSURLErrorNetworkConnectionLost:       // -1005
228        case NSURLErrorDNSLookupFailed:             // -1006
229        case NSURLErrorResourceUnavailable:         // -1008
230        case NSURLErrorNotConnectedToInternet:      // -1009
231            retry = TRUE; 
232            break;
233	}
234	
235	if(retry)
236	{
237        if (lastError) [lastError release];
238        lastError = [error retain];
239        
240		[self requestTile];
241	}
242	else 
243	{
244		[[NSNotificationCenter defaultCenter] postNotificationName:RMTileError object:self userInfo:[NSDictionary dictionaryWithObject:error forKey:RMWebTileImageNotificationErrorKey]];
245		[self cancelLoading];
246	}
247}
248
249- (void)connectionDidFinishLoading:(NSURLConnection *)connection
250{
251    if ([data length] == 0) {
252        //RMLog(@"connectionDidFinishLoading %@ data size %d", _connection, [data length]);
253        
254        if ( lastError ) [lastError release];
255        lastError = [[NSError errorWithDomain:RMWebTileImageErrorDomain 
256                                         code:RMWebTileImageErrorZeroLengthResponse
257                                     userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
258                                               NSLocalizedString(@"The server returned a zero-length response", @""), NSLocalizedDescriptionKey, nil]] retain];
259		[self requestTile];
260	} else {
261		if ( ![self updateImageUsingData:data] ) {
262            if ( lastError ) [lastError release];
263            lastError = [[NSError errorWithDomain:RMWebTileImageErrorDomain 
264                                             code:RMWebTileImageErrorUnexpectedHTTPResponse
265                                         userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
266                                                   NSLocalizedString(@"The server returned an invalid response", @""), NSLocalizedDescriptionKey, nil]] retain];
267            [self requestTile];
268            return;
269        }
270		
271        [data release];
272		data = nil;
273        
274		[url release];
275		url = nil;
276        
277        [connectionOp cancel];
278		[connectionOp release];
279		connectionOp = nil;
280        
281		if (lastError) [lastError release]; lastError = nil;
282        
283		[[NSNotificationCenter defaultCenter] postNotificationName:RMTileRetrieved object:self];
284	}
285}
286
287@end