How to implement custom feature renderers


Summary Use custom feature renderers to control the way features are drawn in a feature layer. This topic briefly explains the essential steps and concepts to develop a custom feature renderer (that is, creating a Java class, annotating the class, implementing the methods, and deploying to ArcGIS).

In this topic


About implementing custom feature renderers

A feature renderer allows you to visualize a feature layer on a map by symbolizing the features in a layer. Standard feature renderers, such as SimpleRenderer, UniqueValueRenderer, BiUniqueValueRenderer, and so on, allow you to symbolize the features based on specific rules applied to the attributes of the feature layer. If none of the standard ArcGIS renderers meet your visualization requirements, and if you want to implement logic and control the way features are symbolized, you can implement custom feature renderers. This topic explains the major steps and concepts behind implementing a custom feature renderer. For a step by step walkthrough on implementing a custom feature renderer, see Building Custom Feature Renderer in Eclipse IDE.

Developing custom feature renderers

The custom feature renderers can be developed by creating simple Java class that implements IFeatureRenderer. The Java class must be annotated with @ArcGISExtension annotation and must override the methods of the IFeatureRenderer to suit your custom drawing. See the following code snippet.
[Java]
//Step 2: Annotate the class with @ArcGISExtension. 
@ArcGISExtension 
//Step 1: Create Java class that implements IFeatureRenderer.
public class FirstCustomRenderer implements IFeatureRenderer{
    //Step 3: Implement methods.
}
IFeatureRenderer is the minimum interface required to create a custom feature renderer. However, you can implement optional interfaces, particularly java.io.Externalizable, to persist the feature renderer in your map documents, IDocumentVersionSupportGEN to support version compatibility, and ILegendInfo to ensure the table of contents (TOC) and legends  appropriately. For more information, see Getting started with a custom feature renderer. The following section explains the methods that must be overridden in detail.

IFeatureRenderer methods

The following illustration shows the methods for IFeatureRenderer. You must override these methods to customize your feature renderer implementation:
The following illustration shows the sequence in which the IFeatureRenderer methods are invoked by the framework when rendering the feature layer.
 

canRender

The canRender method is the first method invoked by the ArcGIS framework. If the method did not return a value of true, the feature renderer will not be applied to the feature layer. The ArcGIS framework provides the feature class and the display references to which the feature renderer will be applied as arguments of the method. 
  • Significance—canRender lets you enforce conditions based on the feature class and display properties of the feature layer to apply the custom renderer . If the specified conditions of the custom renderer are not met, the method will not return a value of true and the renderer will not be applied to the feature layer by the ArcGIS framework. For example, a custom renderer designed for networks can verify the layer's feature class contains a particular type of network feature by checking its IFeatureClass.getFeatureType() property in its canRender method. In the following code example, the canRender method implementation verifies the shape feature of the feature class is a point feature. If your custom renderer is designed for point features, this method's implementation prevents it from being applied to feature layers with line or polygon shape types. 
[Java]
public boolean canRender(IFeatureClass fc, IDisplay display)throws IOException,
    AutomationException{
    if (fc.getShapeType() == esriGeometryType.esriGeometryPoint)
        return true;
    else
        return false;
}

isRenderPhase

If all the conditions specified by the canRender method returned true, the ArcGIS framework invokes the isRenderPhase() method. The isRenderPhase() method lets you specify the draw phase in which the features must be rendered. The framework provides the draw phase as the method argument to determine whether to render the feature layer in the specified draw phase. If the method implementation did not return a value of true for the particular draw phase provided by the framework, it will not render the layer for that draw phase.
  • Significance—The feature layer can be rendered in any or all of the three draw phases (that is, esriDPGeography, esriDPAnnotation, and esriDPSelection). Allowing the renderer to draw in all phases slows the rendering of the feature layer. Typically, all renderers are drawn in the geography phase (esriDPGeography). However the feature rendering can be manipulated to be rendered in multiple phases too. For example, the ArcGIS renderer, ProportionalSymbolRenderer, draws the background fill symbol during the geography phase and the proportionally sized marker symbol during the annotation phase. If there are selected features in the layer being rendered, all feature layer will be rendered for the selection phase by default irrespective of the value returned by the isRenderPhase() method. The following code example shows the isRenderPhase() method implementation where the renderer is restricted to be drawn only in the esriGeography phase:
[Java]
public boolean isRenderPhase(int drawPhase){
    if (drawPhase == esriDrawPhase.esriDPGeography)
        return true;
    else
        return false;
}

setExclusionSetByRef

If the isRenderPhase() method returns a value of true for the particular draw phase, the ArcGIS framework invokes the setExclusionSetByRef method. The framework provides a IFeatureIDSet reference, which provides a list of features that need to be excluded from rendering.
  • Significance—The significance of providing exclusion data to the framework is to eliminate erroneous values from rendering. For example, you might exclude features with values less than or equal to zero from drawing. Also, you might exclude other special values, such as null values. If applicable, you can choose to draw these excluded features with a unique symbol, and have that symbol labeled in the TOC and legend.
Another recommended alternative to prevent features from drawing and from appearing in tables and query results, is to set a layer definition query using IFeatureLayerDefinition.

prepareFilter

The ArcGIS framework invokes the prepareFilter method after setExclusionSetByRef() method.  By default, the framework prepares a QueryFilter that queries the feature class for the feature ID and its shape fields only. The fields returned by the QueryFilter are used by the draw() method for rendering. However, your custom rendering and symbolization may be based on other fields of the feature class. In such a scenario, you must add those fields to the QueryFilter reference provided in the prepareFilter method.
  • Significance—The prepareFilter method lets you specify the fields your renderer needs to draw. It is recommended to create specific fields for rendering since ArcMap renders data faster when the rendering is based on its fields, rather than calculating or programmatically deriving values. In the four-color map problem, it would be too slow if the renderer was responsible for figuring out the color to draw for each feature, when the map is drawn. In this case, create a color field calculate and populate its values to allow ArcMap to render the data faster. Also remember, ArcMap renders data based on an integer field faster than it would if the field was a text data type. This is true for ArcSDE geodatabases, since less data has to be interpreted and transferred over the network.
The following code example shows how you can add all the fields for the feature class to the query filter:
[Java]
public void prepareFilter(IFeatureClass fc, IQueryFilter qFilter)throws IOException,
    AutomationException{

    //Get fields from the feature class.
    IFields fields = fc.getFields();

    //Add all fields to the query filter.
    for (int i = 0; i < fields.getFieldCount(); i++)
        qFilter.addField(fields.getField(i).getName());

    //If an exclusion set is specified by the setExclusionSetByRef() method, 
    //make sure the ObjectID field is present.

    if (exclusionSet != null){
        if (exclusionSet.getCount() > 0){
            qFilter.addField(fc.getOIDFieldName());
        }
    }
}

draw

The ArcGIS framework invokes IFeatureRenderer.draw() to draw features on the display after invoking the prepareFilter method. The framework provides the featureCursor, drawPhase, display, and trackCancel references as method arguments. The featureCursor reference is the result of the query filter made by the prepareFilter method. It includes the feature ID, shape fields by default. If you have specified additional fields in the prepareFilter method, the featurecursor would have fetched those fields too.  The draw() method of a renderer will be called for each phase specified in isRenderPhase() and for the selection phase by default.
The drawPhase method argument indicates the phase in which the draw() method is invoked. The trackCancel method argument allows you to track whether the Esc key was pressed to cancel the drawing during rendering the features. Tracking ESC key is important, since the renderer can slow down in situations with large datasets. It is a good practice to track the Esc key in the draw() method.
  • Significance—The draw() method receives a featureCursor indicating the features to draw. The implementation of the draw() method must draw the specified symbol for the features by invoking IFeatureDraw.draw() on each feature. Other relevant text symbols accompanying the features, as in custom ValenceRenderers, can be drawn using IDisplay.drawText(), which draws adjacent to the feature.

    Remember, the featureCursor only contains features within the current display extent. Since, the queryFilter is used to fetch features, a spatial filter with the display extent is used to limit the features returned. However, there are usually more features in the cursor than in the display extent, since the spatial filter criteria is set against the spatial index rather than the feature geometries.  The renderer draws these offscreen features since it is more efficient to draw than to have a slower query. When data has no spatial index (for example, some shapefiles), all the features in the dataset will be fetched in the feature cursor. Therefore, you might find features in the cursor that are not within the current display extent. It is more efficient to draw these features than to check their extent. ArcGIS has produced the query for speed of execution. In the following code example, the simple renderer loops through the feature cursor and draws features by invoking IFeatureDraw.draw()
[Java]
//draw method of IFeatureRenderer that draws the feature.
public void draw(IFeatureCursor featureCursor, int drawPhase, IDisplay display,
    ITrackCancel trackCancel)throws IOException, AutomationException{

    //declare local variable to to track cancel
    boolean bContinue = false;
    // Do not draw features if there is no display or incorrect drawPhase.
    if (display == null || drawPhase != esriDrawPhase.esriDPGeography)
        return ;

    //Create and set the display symbol to represent the features.
    //Set the display symbol. 
    CharacterMarkerSymbol markerSym = new CharacterMarkerSymbol();
    markerSym.setCharacterIndex(72);
    markerSym.setSize(12.0);
    IColor redColor = new RgbColor();
    redColor.setRGB(0xFF0000);
    markerSym.setColor(redColor);
    display.setSymbol(markerSym);

    //Verify Track Cancel
    if (trackCancel != null)
        bContinue = trackCancel.esri_continue();

    //Loop through the features and draw them using the display symbol.
    IFeature feature = featureCursor.nextFeature();
    while ((feature != null) && (bcontinue == true)){
        IFeatureDraw featureDraw = (IFeatureDraw)feature;
        featureDraw.draw(drawPhase, display, markerSym, false, null,
            esriDrawStyle.esriDSNormal);
        feature = featureCursor.nextFeature();
        bContinue = trackCancel.esri_continue();
    }
}

getSymbolByFeature

The getSymbolByFeature() method returns the appropriate symbol to a feature. You must invoke the getSymbolByFeature() method within the draw() method explicitly.
  • Significance—Using getSymbolByFeature enables the possibility to contain other renderers within your custom renderer. For example, instead of using a single symbol to disperse points, use another symbology option, such as proportional symbols or unique values. This can be achieved by keeping a reference to a contained renderer class (the custom renderer), then for each feature in the draw loop, calling getSymbolByFeature on the contained renderer to determine the symbol to use.

What's Next

When the custom feature renderer is created, compile them and bundle the class files as a Java Archive (JAR) file. The custom feature renderer is then deployed to ArcGIS by placing the JAR file in the ArcGIS extension folder (<ArcGIS Install Dir>/java/lib/ext). An ArcGIS application (ArcGIS Engine, ArcMap, ArcCatalog, ArcGIS Server) recognizes and registers the  custom feature renderer when started, by the extension annotation. If the ArcGIS application is running, restart it after deploying the custom feature renderer. If there are code modifications in the custom feature renderer Java classes in the bundled JAR file, recreate and redeploy the JAR file, then restart the ArcGIS application after redeployment.
When deployed to ArcGIS, the custom feature renderer can be consumed in ArcGIS Engine and ArcGIS Server applications like other ArcGIS feature renderers. For more information about consuming custom feature renderers, see Consuming custom feature renderers. To consume the custom renderer in ArcGIS Desktop, create an associated property page for the custom feature renderer and deploy to ArcGIS. For more information about creating custom property pages for custom feature renderers, see How to implement custom property pages for custom feature renderers.


See Also:

Getting started with a custom feature renderer
Building custom feature renderers in Eclipse IDE
How to implement property pages for custom feature renderers
Implementing persistence and version compatibility
How to implement TOC legends for custom feature renderers




Development licensing Deployment licensing
Engine Developer Kit Engine Runtime
Server Server
ArcView ArcView
ArcEditor ArcEditor
ArcInfo ArcInfo