Dynamic Rendering

Version 3.7

This topic discusses various ways of dynamically altering the rendering of map service layers. These options include: updating symbology, labeling features, and changing layer order dynamically.

Dynamic symbology

The following provides a couple of different options to consider when dynamically symbolizing your data:

Taking existing renderers and applying to server-side features

Prior to ArcGIS 10.1 and the 3.x version of the Flex API, you would have sent the features from a service client-side using either a GraphicsLayer or a FeatureLayer and then possibly symbolized them based on a given renderer. Now, you can take advantage of dynamic symbology by using this existing 'legacy' code that created these renderers and instead of applying them client-side to either the GraphicsLayer or FeatureLayer, you can now apply the renderers to the map service features.

How do you apply existing renderers to server-side features?

The main difference is that the renderer is now applied using the LayerDrawingOptions property of the dynamic map service layer instead of applying it to the GraphicsLayer or FeatureLayer. Both samples, RangeValueRenderer and UniqueValueRenderer, show this behavior.

Small snippet taken from RangeValueRenderer sample.

<!--SimpleFillSymbol {SFS} set client-side...-->
<esri:layerDrawingOptions>
<esri:LayerDrawingOptions layerId="2">
<esri:ClassBreaksRenderer field="RENTER_OCC">
<esri:ClassBreakInfo label="0 to 500"
     maxValue="500"
     minValue="0"
     symbol="{sfs0}"/>
...

Dynamically generate renderers server-side

Another option is to have ArcGIS Server generate renderers that can then be applied to dynamic services. A new task was introduced with the Flex 3.x API called GenerateRendererTask.

This task groups data using a classification method such as ClassBreaksDefinition or a UniqueValueDefinition, resulting in a renderer object. This renderer can then be applied to a dynamic service where the service applies this renderer to its output image.

How does the GenerateRendererTask work?

When an ArcGISDynamicMapServiceLayer loads, you should verify that supportsDynamicLayers is true. Once this is done, call the createDynamicLayerInfosFromLayerInfos() method. This returns an array of DynamicLayerInfo objects. The DynamicLayerInfo class has a source property which references a LayerMapSource that represents the dynamic layer in the map service. This LayerMapSource is used to set the GenerateRendererTask's source property.

The GenerateRendererParameters object allows you to set the classificationDefinition and then the GenerateRendererParameters object is passed to the GenerateRendererTask's execute method. The renderer is then returned from the task, you then create a new LayerDrawingOptions object and set the layerID to that of the LayerMapSource's mapLayerID property. The generated renderer should then be applied to this newly created LayerDrawingOptions renderer property.

Sample snippet taken from Generate renderers sample

<!--detailsTask_getAllDetailsCompleteHandler-->
...
 layerMapSource = DynamicLayerInfo(dynamicLayerInfosArr[i]).source as LayerMapSource;
generateRendererTask.source = layerMapSource;
...
generateRendererTask.execute(generateRendererParams);

<!--generateRendererTask_executeCompleteHandler-->
...
var layerDrawingOptions:LayerDrawingOptions = new LayerDrawingOptions();
layerDrawingOptions.layerId = layerMapSource.mapLayerId; 
//states layer id in the service
layerDrawingOptions.renderer = renderer;
...
var layerDrawingOptionsArr:Array = [ layerDrawingOptions ];
demographicsLayer.layerDrawingOptions = layerDrawingOptionsArr;
...
Note:

The GenerateRendererTask's source property can also be a dynamic layer that does not exist in a map service. The sample, Thematic maps by table join, uses attributes from a joined table that is not in a map service.

Dynamic labeling

New at ArcGIS 10.1 is the ability to alter a map service during the request and change its rendering. This includes the ability to generate on-demand labels for features. In the previous section we discussed working with the LayerDrawingOptions class and how useful it is to modify symbology of a dynamic service. This class has the showLabels, scaleSymbols, and labelClasses properties to aid with dynamic labeling. Setting showLabels=false indicates that labels will not be displayed within the map. You must explicitly set this property for it to be recognized. Setting the scaleSymbols property to false stops the layer from rendering its symbols based on scale. Similar to showLabels, this must be explicitly set for it to be used. The labelClasses property is an array of LabelClass objects that is used to declare what will be labeled. By using various LabelClasses and setting scale thresholds you can control how features are labeled. You can achieve this by labeling features with different fields using the LabelClass' labelExpression property. You can also control what labels are displayed by filtering them using the where property.

Snippet taken from Dynamic labeling sample

<esri:layerDrawingOptions>
<!-- Layer drawing options for the states layer -->
<esri:LayerDrawingOptions layerId="3" showLabels="true">
   <esri:ClassBreaksRenderer field="RENTER_OCC">
      <esri:ClassBreakInfo label="58,094 to 216,867"
            maxValue="216867"
            minValue="58094"
            symbol="{sfs0}"/>
...
...
<esri:labelClasses>
   <esri:LabelClass id="initialLabelExpression"
                    labelExpression="[RENTER_OCC]" 
                    labelPlacement="esriServerPolygonPlacementAlwaysHorizontal"
                    maxScale="18489298">
         <esri:LabelOptions haloColor="0xFFFFFF" haloSize="2"/>
...
...

Change layer order

Another option when dynamically altering symbology is to change the order of your map service's layers and how they display index within the table of contents. The ArcGISDynamicMapServiceLayer's dynamicLayerInfos property accomplishes this. See the Change Layer Ordering sample for additional information regarding this.

Snippet taken from Change Layer Ordering sample

private var dynamicLayerInfos:Array;
...
protected function usaLayer_loadHandler(event:LayerEvent):void
{
   dynamicLayerInfos = usaLayer.createDynamicLayerInfosFromLayerInfos();
   for (var i:int = 0; i < dynamicLayerInfos.length; i++)
   {
      layerList.addItem({ data: dynamicLayerInfos[i], label: checkLayerNameForDisplay(DynamicLayerInfo(dynamicLayerInfos[i]).name)});
   }
   layerList.addEventListener(CollectionEvent.COLLECTION_CHANGE, layerListCollectionChangeHandler, false, 0, true);
}
...
protected function layerListCollectionChangeHandler(event:CollectionEvent):void
{
   if (event.kind == "remove")
   {
      movingLayerName = event.items[0].label;
      //add 1 to the position for readability
      removePosition = event.location + 1;
   }
   else if (event.kind == "add")
   {
      //add 1 to the position for readability
      addedPosition = event.location + 1;
      for (var i:int = 0; i < layerList.length; i++)
      {
         dynamicLayerInfos[i] = layerList.getItemAt(i).data;
      } 
      usaLayer.dynamicLayerInfos = dynamicLayerInfos;
      movingText.visible = true;
   }
}

Additional Resources