// // FindNearest.m // Geopher OpenCaching // // Created by Jeremy on 12/9/10. // Copyright 2010 Stone Software. All rights reserved. // #import "FindNearest.h" #import "JSON.h" #import "Calculations.h" #import "SingleCache.h" #import "Geopher_OpenCachingAppDelegate.h" #import "LocatorModel.h" #define kCachedDataFilename @"CachedResults.plist" #define kCachedGPXDataFilename @"CachedGPXResults.plist" @interface FindNearest () - (void) finishDataProcessing:(NSData*)responseData; - (void) saveGPXData:(NSData*)inData; - (void) beginBackgroundPass; @end @implementation FindNearest @synthesize currentCacheList, spinner, tableView, navController; #pragma mark - #pragma mark OpenCachingRequestProtocol delegate - (void) returnDataComplete:(NSData*) responseData { if (!backgroundPass) { [self finishDataProcessing:responseData]; [self beginBackgroundPass]; } else { [self saveGPXData:responseData]; } } #pragma mark - #pragma mark file management - (void) loadCachedResponseData { NSData* responseData = nil; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); if (paths) { NSString *finalPath = [[paths lastObject] stringByAppendingPathComponent:kCachedDataFilename]; responseData = [[NSMutableData alloc] initWithContentsOfFile:finalPath]; } if (responseData) { [self performSelectorOnMainThread:@selector(finishDataProcessing:) withObject:responseData waitUntilDone:NO]; } } - (void) saveCachedResponseData:(NSData*)responseData { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); if (paths) { NSString *finalPath = [[paths lastObject] stringByAppendingPathComponent:kCachedDataFilename]; if (responseData) [responseData writeToURL:[NSURL fileURLWithPath:finalPath] atomically:YES]; } } - (void) saveGPXData:(NSData*)inData { // we have this data available when loading (offline) individual cache information, but don't yet use it! NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); if (paths) { NSString *finalPath = [[paths lastObject] stringByAppendingPathComponent:kCachedGPXDataFilename]; if (inData) [inData writeToURL:[NSURL fileURLWithPath:finalPath] atomically:YES]; } } #pragma mark - #pragma mark openCaching stuff - (NSString*) findURLStringWithLat:(double)inLat Lon:(double)inLon asGPX:(BOOL)useGPX { // most of these should be a setting NSInteger limit = [[[GetAppDelegate() appPrefs] objectForKey:kSearchResultsPrefKey] intValue]; NSInteger logsReturned = 7; NSMutableString* retVal = [NSMutableString stringWithFormat:@"%@%@", kOpenCachingURL, @"/api/geocache"]; // append .GPX if (useGPX) [retVal appendString:@".GPX"]; // add the center coordinates. Center is first, so has a '?' prepended instead of a '&'. everything hereafter should have a '&' at the beginning. [retVal appendFormat:@"?center=%f,%f", inLat, inLon]; // add the limit [retVal appendFormat:@"&limit=%d", limit]; // add the developer API key [retVal appendFormat:@"&%@", kDeveloperSiteAuthenticationKey]; // add the log limit (likely only applies to GPX data) [retVal appendFormat:@"&log_limit=%d", logsReturned]; // later we will look at the settings to determine what else should go here. See this link for more options: http://www.opencaching.com/api_doc/get/geocache.html // [retVal appendFormat:@"&found=false"]; // needs logged in to be active return [NSString stringWithString:retVal]; } - (void) finishDataProcessing:(NSData*)responseData { NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; NSError *error; SBJSON *json = [[SBJSON new] autorelease]; NSArray *cacheResults = [json objectWithString:responseString error:&error]; if (cacheResults == nil) { NSLog(@"JSON parsing failed: %@", [error localizedDescription]); NSLog(@"%@", responseString); } else { NSMutableString *text = [NSMutableString stringWithFormat:@"Request Text:\r%@\r\r", @"[url]"]; for (NSDictionary* dict in cacheResults) { [text appendString:@"\rNew dictionary:\r"]; NSArray* keys = [dict allKeys]; for (NSString* key in keys) { [text appendFormat:@"%@: %@\r", key, [[dict objectForKey:key] description]]; } [text appendString:@"End dictionary\r"]; [currentCacheList addObject:dict]; } } // cleanup [responseString release]; // save the results to disk here, elsewhere we will read these results if we don't have a current connection. if (NetworkIsReachable() == YES) // if we're running off of cached data this is re-saving what we just loaded! [self saveCachedResponseData:responseData]; [self.spinner stopAnimating]; [self.tableView reloadData]; } - (void) startDownloadingNearby { if ([openCachingObject requestIsInProgress]) { if (backgroundPass = YES) [openCachingObject cancelProgress]; else return; } backgroundPass = NO; [self.spinner startAnimating]; if (NetworkIsReachable() == YES) { if (currentCacheList) { [currentCacheList removeAllObjects]; [self.tableView reloadData]; } CLLocationDegrees lat = [[[LocatorModel sharedInstance] currentLocation] coordinate].latitude; CLLocationDegrees lon = [[[LocatorModel sharedInstance] currentLocation] coordinate].longitude; NSString* str = [self findURLStringWithLat:lat Lon:lon asGPX:NO]; if (!openCachingObject) openCachingObject = [[OpenCachingRequest alloc] init]; [openCachingObject makeGetRequest:str withDelegate:self]; } else { // grab it off of disk and reload it after we're done. [self performSelectorInBackground:@selector(loadCachedResponseData) withObject:nil]; } } - (void) beginBackgroundPass { if (!openCachingObject) // should always do the background pass after the first pass, this should be valid! return; backgroundPass = YES; CLLocationDegrees lat = [[[LocatorModel sharedInstance] currentLocation] coordinate].latitude; CLLocationDegrees lon = [[[LocatorModel sharedInstance] currentLocation] coordinate].longitude; NSString* str = [self findURLStringWithLat:lat Lon:lon asGPX:YES]; [openCachingObject makeGetRequest:str withDelegate:self]; } #pragma mark - #pragma mark View lifecycle - (void)viewDidLoad { [super viewDidLoad]; self.currentCacheList = [NSMutableArray array]; // create the cache list array openCachingObject = nil; // create a reload nav button UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(startDownloadingNearby)]; self.navigationItem.rightBarButtonItem = refreshButton; [refreshButton release]; // tableView.backgroundColor = kWebpageBackgroundColor; UIImageView* imgView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]] autorelease]; tableView.backgroundView = imgView; [self startDownloadingNearby]; } /* - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; } */ /* - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; } */ /* - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; } */ /* - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; } */ /* // Override to allow orientations other than the default portrait orientation. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations. return (interfaceOrientation == UIInterfaceOrientationPortrait); } */ #pragma mark - #pragma mark Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return [currentCacheList count]; } // Customize the appearance of table view cells. - (UITableViewCell *)tableView:(UITableView *)inTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [inTableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]; } cell.backgroundColor = [UIColor clearColor]; UIColor* textColor = kForestGreenColor; // Configure the cell... NSDictionary* dict = [self.currentCacheList objectAtIndex:indexPath.row]; if (dict && [dict isKindOfClass:[NSDictionary class]]) { NSString* oxcode = [dict objectForKey:kOXCodeOXKey]; if (!oxcode) oxcode = [NSString string]; NSDictionary* location = [dict objectForKey:kLocationOXKey]; if (location && [location isKindOfClass:[NSDictionary class]]) { double lat = [[location objectForKey:kLatitudeOXKey] doubleValue]; double lon = [[location objectForKey:kLongitudeOXKey] doubleValue]; CLLocation* curLocation = [[LocatorModel sharedInstance] currentLocation]; CLLocationDistance distToCache = [Calculations calcDistanceFromLat:lat lon:lon toLat:curLocation.coordinate.latitude lon:curLocation.coordinate.longitude]; NSString* bearingText = [Calculations bearingDirectionFromLatitude:curLocation.coordinate.latitude latitude:lat longitude:curLocation.coordinate.longitude longitude:lon]; NSString* bearingDegrees = [NSString stringWithFormat:@"%.0f°", [Calculations calcBearingWithLatitude:curLocation.coordinate.latitude latitude:lat longitude:curLocation.coordinate.longitude longitude:lon]]; NSString* latLonString = [NSString stringWithFormat:@"%@, (%@, bearing %@)", oxcode, [Calculations distToDisplayInDecimal:distToCache], bearingDegrees]; // set the detail text cell.detailTextLabel.text = latLonString; cell.detailTextLabel.font = [UIFont systemFontOfSize:12]; cell.detailTextLabel.backgroundColor = [UIColor clearColor]; cell.detailTextLabel.textColor = textColor; // create a view to tell the user the direction of the cache UILabel* label = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 40, cell.frame.size.height - 2)] autorelease]; label.textAlignment = UITextAlignmentRight; label.font = [UIFont systemFontOfSize:16]; label.text = [NSString stringWithFormat:@"%@ ", bearingText]; label.textColor = textColor; //[UIColor darkGrayColor]; label.backgroundColor = [UIColor clearColor]; cell.accessoryView = label; } // set the name NSString* name = [dict objectForKey:kNameOXKey]; cell.textLabel.text = [NSString stringWithFormat:@"%@", name]; cell.textLabel.font = [UIFont systemFontOfSize:16]; cell.textLabel.textColor = textColor; // set the icon NSString* cacheType = [dict objectForKey:kTypeOXKey]; if ([cacheType rangeOfString:@"Traditional"].location != NSNotFound) [cell.imageView setImage:[UIImage imageNamed:@"Traditional.png"]]; else if ([cacheType rangeOfString:@"Multi"].location != NSNotFound) [cell.imageView setImage:[UIImage imageNamed:@"Multi-Cache Icon.png"]]; else [cell.imageView setImage:[UIImage imageNamed:@"Puzzle Icon.png"]]; } else { cell.textLabel.text = @"bummer"; } return cell; } #pragma mark - #pragma mark Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSDictionary* dict = [self.currentCacheList objectAtIndex:indexPath.row]; if (dict && [dict isKindOfClass:[NSDictionary class]]) { NSString* oxcode = [dict objectForKey:kOXCodeOXKey]; // Create and push another view controller. SingleCache* detailViewController = [[[SingleCache alloc] initWithNibName:@"SingleCache" bundle:nil] autorelease]; [detailViewController loadCacheWithCode:oxcode]; // Pass the selected object to the new view controller. [self.navController pushViewController:detailViewController animated:YES]; } } #pragma mark - #pragma mark Memory management - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Relinquish ownership any cached data, images, etc. that aren't in use. } - (void)viewDidUnload { // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand. // For example: self.myOutlet = nil; } - (void)dealloc { [currentCacheList release]; [openCachingObject release]; [tableView release]; [spinner release]; [navController release]; [super dealloc]; } @end