// // MapViewController.m // Geopher Lite // // Created by Jeremy on 7/11/09. // Copyright 2009 Stone Software. All rights reserved. // #import "MapViewController.h" #import "PinAnnotation.h" #import "LocatorModel.h" #import "GeopherAppDelegate.h" @implementation MapViewController @synthesize map, spinWheel, targetLocation, backButton, currentPosButton, targetButton, typeButton; #pragma mark - #pragma mark Utils // --------------------------------------------------------------------------- // ¥ zoomToFitMapAnnotations: // --------------------------------------------------------------------------- // pass a map view to zoom it according to it's annotations (usually pins) // -(void)zoomToFitMapAnnotations:(MKMapView*)mapView { if([mapView.annotations count] == 0) return; CLLocationCoordinate2D topLeftCoord; topLeftCoord.latitude = -90; topLeftCoord.longitude = 180; CLLocationCoordinate2D bottomRightCoord; bottomRightCoord.latitude = 90; bottomRightCoord.longitude = -180; for(id annotation in mapView.annotations) { topLeftCoord.longitude = fmin(topLeftCoord.longitude, annotation.coordinate.longitude); topLeftCoord.latitude = fmax(topLeftCoord.latitude, annotation.coordinate.latitude); bottomRightCoord.longitude = fmax(bottomRightCoord.longitude, annotation.coordinate.longitude); bottomRightCoord.latitude = fmin(bottomRightCoord.latitude, annotation.coordinate.latitude); } MKCoordinateRegion region; region.center.latitude = topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5; region.center.longitude = topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5; region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.3; // Add a little extra space on the sides region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 1.3; // Add a little extra space on the sides region = [mapView regionThatFits:region]; [mapView setRegion:region animated:YES]; } #pragma mark - - (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error { [spinWheel stopAnimating]; NSLog(@"Animation stopped with error: %s", [error localizedDescription]); } - (void)mapViewWillStartLoadingMap:(MKMapView *)mapView { [spinWheel startAnimating]; } - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView { [spinWheel stopAnimating]; } #pragma mark - - (IBAction) goBack { [self dismissModalViewControllerAnimated:YES]; } - (IBAction) toggleType:(id)sender { MKMapType theType = map.mapType; // rotate through the map types if (theType == MKMapTypeStandard) theType = MKMapTypeHybrid; else if (theType == MKMapTypeSatellite) theType = MKMapTypeStandard; else if (theType == MKMapTypeHybrid) theType = MKMapTypeSatellite; map.mapType = theType; } - (void) centerOnLocation:(CLLocation*)location { // set the new coordinates [map setCenterCoordinate:location.coordinate animated:YES]; } - (IBAction) centerOnTarget:(id)sender { // use the auto-zoom I dug up on the net somewhere. [self zoomToFitMapAnnotations:self.map]; lastButtonPressed = sender; return; // this is the old code that now no longer gets called. // MKMapView* mapView = self.map; // // get the locations and then the number of pixels from those locations they are apart. // CLLocationCoordinate2D targetCoord = [[LocatorModel sharedInstance].targetLocation coordinate]; // CLLocationCoordinate2D userLocation = [[[self.map userLocation] location] coordinate]; // CGPoint userPt = [mapView convertCoordinate:userLocation toPointToView:mapView]; // CGPoint targetPt = [mapView convertCoordinate:targetCoord toPointToView:mapView]; // // CGSize size; // size.width = MAX(userPt.x, targetPt.x) - MIN(userPt.x, targetPt.x); // size.height = MAX(userPt.y, targetPt.y) - MIN(userPt.y, targetPt.y); // // if (!CGRectContainsPoint(self.view.frame, targetPt) || !CGRectContainsPoint(self.view.frame, userPt)) // { // CLLocationCoordinate2D southWest = targetCoord; // CLLocationCoordinate2D northEast = southWest; // // southWest.latitude = MIN(southWest.latitude, userLocation.latitude); // southWest.longitude = MIN(southWest.longitude, userLocation.longitude); // // northEast.latitude = MAX(northEast.latitude, userLocation.latitude); // northEast.longitude = MAX(northEast.longitude, userLocation.longitude); // // CLLocation *locSouthWest = [[CLLocation alloc] initWithLatitude:southWest.latitude longitude:southWest.longitude]; // CLLocation *locNorthEast = [[CLLocation alloc] initWithLatitude:northEast.latitude longitude:northEast.longitude]; // // // This is a diag distance (if you wanted tighter you could do NE-NW or NE-SE) // CLLocationDistance meters = [locSouthWest distanceFromLocation:locNorthEast]; // // MKCoordinateRegion region; // region.center.latitude = (southWest.latitude + northEast.latitude) / 2.0; // region.center.longitude = (southWest.longitude + northEast.longitude) / 2.0; // region.span.latitudeDelta = meters / 111319.5; // double degreeRatio = fabs(region.center.longitude) / 180.0; // region.span.longitudeDelta = (meters / 111319.5) * (1.0 - degreeRatio); // // region = [mapView regionThatFits:region]; // [mapView setRegion:region animated:YES]; // // [locSouthWest release]; // [locNorthEast release]; // } // else // otherwise simply center on the point. // { // [self centerOnLocation:[LocatorModel sharedInstance].targetLocation]; // } } - (IBAction) centerOnSelf:(id)sender { static id alreadyZoomed = (id)0xDEADBEEF; // zoom in on the center location. if (lastButtonPressed && lastButtonPressed == sender && lastButtonPressed != alreadyZoomed) { MKCoordinateRegion region; region.center.latitude = [LocatorModel sharedInstance].currentLocation.coordinate.latitude; region.center.longitude = [LocatorModel sharedInstance].currentLocation.coordinate.longitude; region.span.latitudeDelta = 0.005; region.span.longitudeDelta = 0.005; [map setRegion:region animated:YES]; lastButtonPressed = alreadyZoomed; // so we don't come in here over and over if the button keeps getting pressed. } [self centerOnLocation:[LocatorModel sharedInstance].currentLocation]; if (lastButtonPressed != alreadyZoomed) lastButtonPressed = sender; } #pragma mark - - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization. lastButtonPressed = nil; } return self; } - (void) viewWillAppear:(BOOL)animated { if (!NetworkIsReachable()) { UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"" message:@"Unable to find a network. Map information is will be unreliable." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; } else { [self loadTargetPin]; } } - (void) loadTargetPin { if (![LocatorModel sharedInstance].hasValidTarget) // no target, so nothing to do. return; // get the current location target CLLocationCoordinate2D loc; loc.latitude = [LocatorModel sharedInstance].targetLocation.coordinate.latitude; loc.longitude = [LocatorModel sharedInstance].targetLocation.coordinate.longitude; NSArray* annotations = [map annotations]; BOOL isEqual = NO; NSMutableString* coordStr = [[NSMutableString alloc] init]; [coordStr appendString:[[LocatorModel sharedInstance] currentLatitudeText: NO]]; [coordStr appendString:@", "]; [coordStr appendString:[[LocatorModel sharedInstance] currentLongitudeText: NO]]; // check to see if we have a match. for (MKUserLocation* ann in annotations) { // FLOAT_EQ(ann.location.latitude, loc.latitude) && // FLOAT_EQ(loc.longitude, ann.location.longitude) && if ([ann.title isEqualToString:coordStr]) { // we're in the same place isEqual = YES; } } // if the existing pin exists and is not the same as the previous pin, re-init if (NO == isEqual || [annotations count] == 0) { // remove any existing annotations. [map removeAnnotations:annotations]; // re-init the map // MKCoordinateRegion region; // region.center.latitude = [LocatorModel sharedInstance].currentLocation.coordinate.latitude; // region.center.longitude = [LocatorModel sharedInstance].currentLocation.coordinate.longitude; // region.span.latitudeDelta = 0.005; // region.span.longitudeDelta = 0.005; // [map setRegion:region animated:YES]; // apparently I don't need a current location as the built in map will do it for you. For now // just add the target as a pin. [targetLocation setText:coordStr]; // create the target PinAnnotation* target = [[PinAnnotation alloc] initWithCoordinate:loc]; NSMutableString* str = [[NSMutableString alloc] init]; // fill in tooltip-esque name of the cache // if ([[rootController.cacheIDTextView text] isEqualToString:@"GC"] == NO && // [[rootController.cacheIDTextView text] length] > 0) // { // [str appendString:@"Geocache "]; // [str appendString:[rootController.cacheIDTextView text]]; // } // else { [str appendString:@"Target Location"]; } // now set the text on the tooltip (callout) popup target.currentTitle = str; target.currentSubTitle = coordStr; // cleanup memory [str release]; // now make the pin with the "target" we set up above. MKPinAnnotationView* targetPin = [[MKPinAnnotationView alloc] initWithAnnotation:target reuseIdentifier:@"targetPin"]; targetPin.pinColor = MKPinAnnotationColorPurple; targetPin.animatesDrop = YES; targetPin.canShowCallout = YES; [map addAnnotation:target]; [target release]; [targetPin release]; // wait a bit to let things load, then auto-zoom the current position and the target. [self performSelector:@selector(zoomToFitMapAnnotations:) withObject:self.map afterDelay:1]; } [coordStr release]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Overriden to allow any orientation. return YES; } - (void)dealloc { [super dealloc]; } @end