AGSMapView's zoomWithFactor, keeping the map centered

Discussion created by pml on Sep 26, 2011
Latest reply on Sep 26, 2011 by njarecha-esristaff
I have some code that adjusts the zoom level of the map based on a zoom factor. The problem is the map does not zoom in or out from the center but slightly offset from the center. The map content shifts toward the upper left each time a zoom out occurs. The map shifts toward the lower right when a zoom in occurs.

The map has a UITextField (named scaleLabel) where it displays the current scale and the user can type in the desired scale in this textField (for example, the user might type in 600 to get 1"=600'). After dismissing the keyboard, the map adjusts to the new scale.

Please note that this posting only shows the code that I believe to be relevant. If you need more, I'd be glad to add it.

Header file:
#import <UIKit/UIKit.h>
#import "ArcGIS.h"

@interface MapView : UIViewController <UITextFieldDelegate, 
AGSMapViewLayerDelegate, AGSMapViewTouchDelegate> 

    AGSMapView *_mapView; 
 UITextField *scaleLabel;
 UIView *inputAccessoryView;
 float scaleCurrentValue;

// class properties
@property (nonatomic, retain) IBOutlet AGSMapView *mapView;
@property (nonatomic, retain) IBOutlet UITextField *scaleLabel;
// could not use retain (since a float is not an object?)
@property (nonatomic, assign) float scaleCurrentValue;

// method prototypes
-(void)respondToEnvChange:(NSNotification *)notification;
-(NSDictionary *)findMapCenter;


Selected methods from the implementation file:

In the code below, I setup the notifications to listen for changes in the zoom level or map's extent.
-(void)mapViewDidLoad:(AGSMapView *)mapView
 // register for "MapDidEndPanning" notifications
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(respondToEnvChange:) 
name:@"MapDidEndPanning" object:nil];
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(respondToEnvChange:) 
name:@"MapDidEndZooming" object:nil];
 // scaleLabel is a UITextField that shows and allows the user to change the map's scale.
 scaleLabel.text = [NSString stringWithFormat:@"%.0f", (self.mapView.mapScale / 2.54) / 12];


Below is how we calculate the center of the map. We need centerX and centerY to be used in zoomWithFactor's atAnchorPoint argument. Notice that this method is called by zoomMapByChangingScaleText and this method returns an NSDictionary.

-(NSDictionary *)findMapCenter
 NSDictionary *mapCenterMethod;
 float mapCenterX = ( (self.mapView.envelope.xmax - self.mapView.envelope.xmin ) / 2 ) + self.mapView.envelope.xmin;
 float mapCenterY = ((self.mapView.envelope.ymax - self.mapView.envelope.ymin) / 2) + self.mapView.envelope.ymin;
 mapCenterMethod = [NSDictionary dictionaryWithObjectsAndKeys:
        @"centerX", [NSNumber numberWithFloat:mapCenterX], 
        @"centerY", [NSNumber numberWithFloat:mapCenterY], nil];
 NSLog(@"mapCenterMethod: %f; %f",mapCenterX, mapCenterY);
 // put X and Y values into an NSDictionary
 return mapCenterMethod;

Below is where the zoom operation takes place.

 if ( self.scaleCurrentValue != [scaleLabel.text floatValue] )
 // run the findMapCenter method.
 // put the return value in an NSDictionary. 
 NSDictionary *mapCenter = [self findMapCenter];
 // get the new value in the UITextField since the done button was tapped.
 float newScaleValue = [scaleLabel.text floatValue];
 // calculate the zoom factor.
 float zoomFactor = newScaleValue / self.scaleCurrentValue;
 [self.mapView zoomWithFactor:zoomFactor 
     atAnchorPoint:CGPointMake([[mapCenter objectForKey:@"centerX"] floatValue], [[mapCenter objectForKey:@"centerY"] floatValue]) 
 [scaleLabel resignFirstResponder];

 else // scale did not change...hide the keyboard.

  [scaleLabel resignFirstResponder];

Thank you for any help,
Paul Lohr