AGSMapView's zoomWithFactor, keeping the map centered

3468
3
09-26-2011 12:35 AM
PaulLohr
Occasional Contributor III
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)zoomToGraphicsLayer;
-(void)respondToEnvChange:(NSNotification *)notification;
-(NSDictionary *)findMapCenter;
-(void)zoomMapByChangingScaleText;


@end


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.

-(void)zoomMapByChangingScaleText
{
 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]) 
       animated:YES];
  
 [scaleLabel resignFirstResponder];
 }

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

 {
  [scaleLabel resignFirstResponder];
  
 }
}




Thank you for any help,
Paul Lohr
0 Kudos
3 Replies
NimeshJarecha
Esri Regular Contributor
Hi Paul,

I've not tested your code but you are doing all calculations to get the map's center and zoom. Use below code and check whether you're still seeing the same issue.

CGPoint screenPoint = [self.mapView toScreenPoint:self.mapView.envelope.center];
[self.mapView zoomWithFactor:zoomFactor atAnchorPoint:screenPoint animated:YES];


Regards,
Nimesh
0 Kudos
PaulLohr
Occasional Contributor III
That is much better, Nimesh - the map now stays centered. Thank you for helping once again.

I assume I was working in map units since I did not use AGSMapView's toScreenPoint method to convert.

Your solution eliminates many lines of code, and best of all makes it work.

Thanks for helping me, Nimesh.

Paul Lohr
0 Kudos
NimeshJarecha
Esri Regular Contributor
Great! Glad to help!

Regards,
Nimesh
0 Kudos