The ArcGIS SDK for iOS provides just as much and even more functionality as Apple's standard MapKit framework. Some of the vocabulary and terms used may be unfamiliar to those coming from the MapKit APIs. Below are some common and simple examples of the basics of replacing MapKit with ArcGIS.
Install the SDK and configure your XCode project
ESRI's ArcGIS resource center has good instructions on how to accomplish this. See these pages:
Replace MKMapView with AGSMapView
Let's say you have a standard MKMapView
in your XIB, with @property
and
@synthesize
directives for it in your interface and implementation:
Once you've installed the ArcGIS SDK for iOS and configured your project, you
should have access to <ArcGIS/ArcGIS.h>
:
You can then select and delete the MKMapView from your XIB, and drag in a
vanilla UIView
object to replace it. With the new UIView
selected, change
the class field in the Identity Inspector view on the Utilities sidebar to
AGSMapView
and wire it up to your interface:
You could build and run your application now, but this view would not show you
anything. Since ArcGIS has the ability to use many different basemaps, you
need to create a AGSTiledMapServiceLayer
, feed it the basemap you want, and
add it to the AGSMapView
. For this example, I chose "World Street Map":
#define BASEMAP_URL @"http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"
...
- (void)viewDidLoad
{
...
NSURL *basemap = [NSURL URLWithString:BASEMAP_URL];
[self.mapView addMapLayer:[AGSTiledMapServiceLayer tiledMapServiceLayerWithURL:basemap]
withName:@"Tiled Layer"];
...
}
Now you should be able to run this application and see an AGSMapView
with tiles
loading in:
Configuring AGSMapView
As you can see in the image above, we are zoomed all the way out, and the map
does not wrap when you scroll. MKMapView
maps don't wrap either, but this is
an easy place to start:
// AGSMapView objects have a `wrapAround` property
self.mapView.wrapAround = YES;
It would also be very handy if the map started out life by showing us our immediate vicinity. This entails a few steps that can be generified as:
- find out the device's current location in latitude and longitude
- create a box around that location
- zoom the map to that box
These steps are the same regardless of using MapKit or ArcGIS so let's go through each one in a bit more detail.
Getting the device's current location
CoreLocation
is fantastic, albeit opaque. However, for our current purposes,
it will do just fine. In this first step, this process is the same for both
types of map views:
- (void)viewDidLoad
{
[super viewDidLoad];
// locationManager is a private instance var
locationManager = [CLLocationManager new];
locationManager.delegate = self;
[locationManager startUpdatingLocation];
}
// this method is deprecated in iOS 6
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSMutableArray *locations = [NSMutableArray arrayWithObject:newLocation];
if (oldLocation) [locations insertObject:oldLocation atIndex:0];
[self locationManager:locationManager didUpdateLocations:locations];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// most recent is last in the list
CLLocation *location = [locations objectAtIndex:(locations.count - 1)];
// let's wait until we get a good enough fix...
if (location.horizontalAccuracy < 100.0) {
CLLocationCoordinate2D latlng = location.coordinate;
// TODO make bounding box
// TODO tell map to zoom to it
// got a good fix, stop updating
[locationManager stopUpdatingLocation];
}
}
Creating a bounding box around the location
In normal MapKit programming, you would now build up a MKCoordinateRegion
data structure so that you can "zoom" the map to it. You would replace that
first TODO
above with something like this:
// MKCoordinateSpan values are for the whole screen...
float extentDelta = 0.05;
MKCoordinateRegion region = {latlng, (MKCoordinateSpan){extendDelta, extentDelta}};
However, in GIS terms, this is usually not called a "Coordinate Region", but instead is known as an "Extent" or "Envelope". The ArcGIS classes are named accordingly. Also, ArcGIS classes know about another term, "Spatial Reference". Discussion of different types of spatial references is beyond the scope of this document. Here is the equivalent code when using ArcGIS:
// ... while these X/Y max/min values represent corners
float extentDelta = 0.025;
AGSEnvelope *envelope = [AGSEnvelope envelopeWithXmin:(latlng.longitude - extentDelta)
ymin:(latlng.latitude - extentDelta)
xmax:(latlng.longitude + extentDelta)
ymax:(latlng.latitude + extentDelta)
spatialReference:[AGSSpatialReference wgs84SpatialReference]];
As you can see, AGSEnvelope
gives you much finer control of the "box",
requiring you to set your SW and NE corners individually.
Also note that AGSEnvelope
, like many things in ArcGIS SDK for iOS, uses
x
and y
terminology instead of latitude
and longitude
. It is important to
realize that x corresponds to longitude and y corresponds to latitude.
This may seem backwards at first, but it does makes sense.
"Zooming" the map to the bounding box
With an MKCoordinateRegion
in hand, we can tell MKMapView
to show it very
easily. You could replace the second TODO
above with:
[self.mapView setRegion:region animated:YES];
It is equally easy to acheive the same result with AGSMapView
:
[self.mapView zoomToEnvelope:envelope animated:YES];
Here are comparison screenshots, MapKit on the left, and ArcGIS Maps on the right.