Managing Graphic features
Once you have created a Graphics layer, you need to add Graphics to it in order to display data. In most cases, you will use Graphics that have been created by actions such as executing a query or drawing a shape on the Map. To add a Graphic, you must:
- Create or retrieve the Graphics layer to add the Graphic to
- Create or retrieve a Graphic
- Set the Graphic's Geometry, if this hasn't been done for you
- Apply the Symbol to the Graphic
- Add the Graphic to the Graphics layer
The discussion that follows will show you how to create and add Graphics to a Graphics layer.
Retreiving Graphics layers from a Map View
If you want to access a Graphics layer that was previously added to a map you will need to know either the name of the Graphics layer or the index associated with that layer. There are two properties of AGSMapView which will allow you to get information about layers associated with a map: mapLayerNames and mapLayerViews.
The mapLayerNames method returns an array of the names of layers currently added to the map. The order of the layer names reflects the order of the map layers, with layer at index 0 at the bottom and the last layer name at the top.
//Get the array of map layer names
NSArray* mapLayerNamesArray = self.mapView.mapLayerNames;
You can get the layer name directly if you know the index of the layer:
//get the name of the first layer (index 0)
NSString* sLayerName = [self.mapView.mapLayerNames
objectAtIndex:0];
In order to get a Graphics layer (or any other map layer) from the map you will use the mapLayerViews method. mapLayerViews returns an NSDictionary object. This is a collection of key/value pairs. The key is a unique string which is the layer name used when adding the layer to the map (via the AGSMapView addMapLayer method). The value is the the layer view object.
This layer view object implements the AGSLayerView protocol, which will be used to get the actual Graphics layer (or any other map layer). The code below gets the dictionary of layer name/layer view pairs then gets the layer view from that dictionary using the name of the layer. Since AGSLayerView is not a class but a protocol, the syntax for declaring the local variable is a little different:
//Get the dictionary of layer views
NSDictionary<AGSLayerView> *myLayerViewDict = self.mapView.mapLayerViews;
//get the AGSLayerView for the layer named “Graphics Layer”
//Note that ‘id<AGSLayerView>’ syntax allows for the creation
//of a local variable of the correct type
id<AGSLayerView> myGraphicsLayerView = [myLayerViewDict objectForKey:@”Graphics Layer”];
Alternately, you could do it all in one step:
id<AGSLayerView> myGraphicsLayerView =
[self.mapView.mapLayerViews objectForKey:@”Graphics Layer”];
The layer name can either be a literal string, as in the above code, or one retrieved from the array returned by the mapLayerNames method.
The AGSLayerView protocol has properties for the name of the layer, the map view the layer is in, the latest error (if any) returned by the service or client for that layer, and the AGSLayer. Using the myGraphicsLayerView variable above, use the agsLayer property to get the layer (casting the result to type AGSGraphicsLayer):
AGSGraphicsLayer* myGraphicsLayer =
(AGSGraphicsLayer*)myGraphicsLayerView.agsLayer;
Once you have your Graphics layer, you are ready to add graphic features to it.
Adding Graphic Features
In most cases, the Graphics you add to a Graphics layer will be created by tasks, such as a query, as mentioned above. You can also create Graphics programmatically, in response to a user event, for example. A Graphic is an instance of the AGSGraphic class. To create an AGSGraphic object, you need a geometry which defines the Graphic. The graphic can contain a symbol which provides the display for the graphic, or the GraphicsLayer can be associated with a renderer which decides how all graphics in that layer are displayed.. Optionally, you can have custom attributes associated with the Graphic.
The following code creates a Graphic and adds it to an existing Graphics layer called myGraphicsLayer:
//create a marker symbol to be used by our Graphic
AGSSimpleMarkerSymbol *myMarkerSymbol =
[AGSSimpleMarkerSymbol simpleMarkerSymbol];
myMarkerSymbol.color = [UIColor blueColor];
//Create an AGSPoint (which inherits from AGSGeometry) that
//defines where the Graphic will be drawn
AGSPoint* myMarkerPoint =
[AGSPoint pointWithX:-93.2984
y:44.9409
spatialReference:self.mapView.spatialReference];
//Create the Graphic, using the symbol and
//geometry created earlier
AGSGraphic* myGraphic =
[AGSGraphic graphicWithGeometry:myMarkerPoint
symbol:myMarkerSymbol
attributes:nil
infoTemplateDelegate:nil];
//Add the graphic to the Graphics layer
[myGraphicsLayer addGraphic:myGraphic];
//Tell the layer to redraw itself
[myGraphicsLayer dataChanged];
Graphics that are returned as the result of a task are already populated with a geometry but not a symbol, so you will either need to give those Graphics a symbol or apply a renderer to the Graphics layer before adding them to your Graphics layer. To set a symbol into Graphics which have been returned as the result of a Query task you would do the following:
//create a simple fill symbol
AGSSimpleFillSymbol *fillSymbol =
[AGSSimpleFillSymbol simpleFillSymbol];
fillSymbol.color =
[[UIColor purpleColor] colorWithAlphaComponent:0.25];
fillSymbol.outline.color = [UIColor darkGrayColor];
//featureSet.features is the result of a Query task.
//It is an array of AGSGraphic objects containing
//geometries, but not symbols.
for (AGSGraphic *graphic in featureSet.features) {
//set the graphics’s symbol to fillSymbol
graphic.symbol = fillSymbol;
//add the graphic to the layer
[myGraphicsLayer addGraphic:graphic];
}
//Tell the layer to redraw itself
[myGraphicsLayer dataChanged];
See Working with Symbols and Renderers for more information on renderers.
Displaying attribute information in the callout
A graphic can contain custom attributes which more information about the graphic. For instance, a graphic representing a city might have attributes providing information about it's total population, average household income, etc. Attributes are key-value pairs. A graphic can also optionally contain an infoTemplateDelegate. The infoTemplateDelegate provides a convenient way to specify what information about the graphic should be displayed in the callout.
To display a callout when a user taps on a graphic, you need to set one of your classes as the infoTemplateDelegate of the AGSGraphic instance. You do this by making the class adopt the AGSInfoTemplateDelegate protocol.
@interface MyTemplate : NSObject <AGSInfoTemplateDelegate>
...
@end
@implementation MyTemplate
...
- (NSString *)titleForGraphic:(AGSGraphic *)graphic screenPoint:(CGPoint)screen mapPoint:(AGSPoint *)map {
return @"<title for this graphic>";
}
- (NSString *)detailForGraphic:(AGSGraphic *)graphic screenPoint:(CGPoint)screen mapPoint:(AGSPoint *)map {
return @"<detail for this graphic>";
}
@end
Finally, an instance of your class must be assigned as the graphic's infoTemplateDelegate. This allows your class to be consulted before showing the callout.
AGSGraphic* graphic = ... ;
MyTemplate* template = ... ;
graphic.infoTemplateDelegate = template;
If you're only interested in showing attribute values in the callout, the API provides an AGSCalloutTemplate which is a convenient implementation of the AGSInfoTemplateDelegate protocol. AGSCalloutTemplate allows you to specify title and detail text using tokens. Tokens are attribute keys enclosed by ${ } characters. Attributes keys are automatically replaced by attribute values for the graphic when the callout needs to be displayed.
AGSCalloutTemplate* template = [[AGSCalloutTempate alloc] init] ;
template.titleTemplate = @"${CITY_NAME}"; //show the value for attribute key 'CITY_NAME'
template.detailTemplate = @"${POPULATION}"; //show the value for attribute key 'POPULATION'
AGSGraphic* graphic = ... ;
graphic.infoTemplateDelegate = template;
If you don't assign an infoTemplateDelegate for a graphic, the map does not display the callout when the user taps on that graphic.