Introduction
Here is a quick sample demonstrating the use of a Class Breaks Renderer in the ESRI ArcGIS iPhone API. The sample queries an ArcGIS Server Map Service for cities in California and renders them as graphics based on the population. Here’s a screenshot of the app running in the simulator.
Click here to download the source code from this article.
[more]
Discussion
If you’ve been programming with any of the ESRI Client APIs, then you’ll recognize the Class Breaks Renderer concept. For those who aren’t familiar, here’s a snippet from the Working with Symbols and Renderers section of the ArcGIS iPhone SDK Concepts documentation:
A class breaks renderer symbolizes each Graphic based on the value of some numeric attribute. Graphics with similar values for the attribute get the same Symbol. The "breaks" define the values at which the symbology changes.
Code Listings
ClassBreaksViewController.h
#import <UIKit/UIKit.h>
#import "AGSiPhone.h"
#define kTiledMapServiceURL @"http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer"
#define kDynamicMapServiceURL @"http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/0"
@interface ClassBreaksViewController : UIViewController<AGSMapViewDelegate, AGSQueryTaskDelegate> {
AGSMapView *mapView;
AGSGraphicsLayer *cityGraphicsLayer;
AGSQueryTask *cityQueryTask;
}
@property (nonatomic, retain) IBOutlet AGSMapView *mapView;
@property (nonatomic, retain) AGSGraphicsLayer *cityGraphicsLayer;
@property (nonatomic, retain) AGSQueryTask *cityQueryTask;
@end
ClassBreaksViewController.m
#import "ClassBreaksViewController.h"
@implementation ClassBreaksViewController
@synthesize mapView;
@synthesize cityGraphicsLayer;
@synthesize cityQueryTask;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
// Set map view delegate
self.mapView.mapViewDelegate = self;
// Create tile base map layer
AGSTiledMapServiceLayer *tiledLayer = [[AGSTiledMapServiceLayer alloc] initWithURL:[NSURL URLWithString:kTiledMapServiceURL]];
[self.mapView addMapLayer:tiledLayer withName:@"BaseLayer"];
[tiledLayer release];
// Create grpahics layer
self.cityGraphicsLayer = [AGSGraphicsLayer graphicsLayer];
// Create symbols for the three class breaks
AGSSimpleMarkerSymbol *lowSymbol = [AGSSimpleMarkerSymbol simpleMarkerSymbol];
lowSymbol.color = [UIColor colorWithRed:151.0/255.0 green:216.0/255.0 blue:255.0/255.0 alpha:0.8];
lowSymbol.outline.width = 0;
lowSymbol.size = 15;
AGSSimpleMarkerSymbol *mediumSymbol = [AGSSimpleMarkerSymbol simpleMarkerSymbol];
mediumSymbol.color = [UIColor colorWithRed:255.0/255.0 green:165.0/255.0 blue:83.0/255.0 alpha:0.8];
mediumSymbol.outline.width = 0;
mediumSymbol.size = 20;
AGSSimpleMarkerSymbol *highSymbol = [AGSSimpleMarkerSymbol simpleMarkerSymbol];
highSymbol.color = [UIColor colorWithRed:222.0/255.0 green:0.0 blue:0.0 alpha:0.8];
highSymbol.outline.width = 0;
highSymbol.size = 25;
// Create a class breaks renderer with a default simple marker symbol and an attribute field
AGSClassBreaksRenderer *cityRenderer = [AGSClassBreaksRenderer
classBreaksRendererWithDefaultSymbol:lowSymbol
forAttributeField:@"POP1990"];
// Create three AGSClassBreak objects, one each for low, medium and high populations
AGSClassBreak* lowClassBreak = [AGSClassBreak
classBreakInfoWithSymbol:lowSymbol forMinValue:DBL_MIN
maxValue:50000.0];
AGSClassBreak* mediumClassBreak = [AGSClassBreak
classBreakInfoWithSymbol:mediumSymbol forMinValue:50000.0
maxValue:250000];
AGSClassBreak* highClassBreak = [AGSClassBreak
classBreakInfoWithSymbol:highSymbol forMinValue:250000.0
maxValue:DBL_MAX];
// Create an NSMutableArray, fill it with the class break objects,
// and set it to the renderer’s classBreaks property
cityRenderer.classBreaks = [NSMutableArray arrayWithObjects:
lowClassBreak, mediumClassBreak, highClassBreak, nil];
// Add the renderer to the graphics layer
self.cityGraphicsLayer.renderer = cityRenderer;
// Add the graphics layer to the map view
[self.mapView addMapLayer:self.cityGraphicsLayer withName:@"CityGraphicsLayer"];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.cityGraphicsLayer = nil;
self.mapView = nil;
[super viewDidUnload];
}
- (void)dealloc {
[cityGraphicsLayer release];
[mapView release];
[super dealloc];
}
#pragma mark AGSMapViewDelegate
// Called when the map view is loaded (after the view is loaded)
- (void)mapViewDidLoad:(AGSMapView *)mapView {
// Set up query task for cities and perform query returning all attributes
self.cityQueryTask = [AGSQueryTask queryTaskWithURL:[NSURL URLWithString:kDynamicMapServiceURL]];
self.cityQueryTask.delegate = self;
AGSQuery *cityQuery = [AGSQuery query];
cityQuery.where = @"STATE_NAME = 'California'";
cityQuery.outFields = [NSArray arrayWithObject:@"*"];
[self.cityQueryTask executeWithQuery:cityQuery];
// Create extent to be used as default
AGSEnvelope *envelope = [AGSEnvelope envelopeWithXmin:-118.6
ymin:33.6
xmax:-118.1
ymax:34.2
spatialReference:self.mapView.spatialReference];
// Call method to set extent, pass in envelope
[self.mapView performSelector:@selector(zoomToEnvelope:animated:)
withObject:envelope
afterDelay:0.5];
}
#pragma mark AGSQueryTaskDelegate
// When query is executed ....
- (void)queryTask:(AGSQueryTask *)queryTask didExecuteWithFeatureSetResult:(AGSFeatureSet *)featureSet {
// Iterate the returned features (graphics) and add them to the graphics layer
for (AGSGraphic *graphic in featureSet.features) {
[self.cityGraphicsLayer addGraphic:graphic];
}
[self.cityGraphicsLayer dataChanged];
// Clean up query task memory
self.cityQueryTask = nil;
}
// If there's an error with the query task give info to user
- (void)queryTask:(AGSQueryTask *)queryTask didFailWithError:(NSError *)error {
// Clean up query task memory
self.cityQueryTask = nil;
// Display error message
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
@end
Summary
In this post you learned how to use a Class Breaks Renderer in the ESRI ArcGIS iPhone API to display cities of varying population with different symbols.
Click here to download the source code from this article.
I hope you are finding these posts on the ESRI iPhone API to be helpful. If you have any suggestions, ideas or feedback, please leave them in the comments below.
Additional Resources