0

I'm trying to implement custom renderer to display device aging in different colors. For example: 0-10 years > green 10-15 years > yellow 15+ years > red

My feature has installed date attribute which we use to calculate its age. I've tried using ClassBreaksRenderer but its returning wrong color for age when we show the feature on map, when we test getSymbol(feature) method it returns correct color. I don't know if its a bug or I'm doing something wrong.

So I've implemented my own renderer by extending BaseRenderer but its getSymbol(feature) method is not called. Only getType() is called.

Here's the source code of my custom renderer:

import com.esri.core.geometry.Geometry;
import com.esri.core.map.Feature;
import com.esri.core.renderer.BaseRenderer;
import com.esri.core.symbol.SimpleFillSymbol;
import com.esri.core.symbol.SimpleLineSymbol;
import com.esri.core.symbol.SimpleMarkerSymbol;
import com.esri.core.symbol.Symbol;
import java.awt.Color;
import java.util.Date;
import java.util.TreeMap;

/**
 *
 * @author MethoD
 */
public class DeviceAgingRenderer extends BaseRenderer {

    private final String attribute;
    private final Geometry.Type geometryType;
    private final TreeMap<Integer, Color> ageToColorMap;

    public DeviceAgingRenderer(String attribute, Geometry.Type geometryType) {
        this.attribute = attribute;
        this.geometryType = geometryType;
        //this.ageToColorMap = ageToColorMap;
        ageToColorMap = new TreeMap<>();
        ageToColorMap.put(-1, new Color(30, 109, 237)); // no data > blue
        ageToColorMap.put(0, new Color(22, 183, 22)); // 0-10 > green
        ageToColorMap.put(10, new Color(244, 156, 4)); // 10-15 > yellow
        ageToColorMap.put(15, new Color(224, 30, 13)); // 15+ red
    }

    @Override
    protected String getType() {
        return "deviceAging";
    }

    @Override
    public Symbol getSymbol(Feature feature) {
        System.out.println("feature: " + feature); // not prninted and not called
        int age = -1;
        if (feature.getAttributeValue(attribute) != null) {
            Long timestamp = (Long) feature.getAttributeValue(attribute);
            age = getDiffYears(new Date(), new Date(timestamp));
        }
        Color color = ageToColorMap.floorEntry(age).getValue();
        switch (geometryType) {
            case POLYLINE:
            case LINE:
                return new SimpleLineSymbol(color, 2, SimpleLineSymbol.Style.SOLID);
            case POLYGON:
            case ENVELOPE:
                return new SimpleFillSymbol(color);
            case MULTIPOINT:
            case POINT:
            default:
                return new SimpleMarkerSymbol(color, 5, SimpleMarkerSymbol.Style.CIRCLE);
        }
    }

    public static int getDiffYears(Date end, Date start) {
        Calendar c_start = getCalendar(start);
        Calendar c_end = getCalendar(end);
        int diff = c_end.get(Calendar.YEAR) - c_start.get(Calendar.YEAR);
        if (c_start.get(Calendar.MONTH) > c_end.get(Calendar.MONTH)
                || (c_start.get(Calendar.MONTH) == c_end.get(Calendar.MONTH) && c_start.get(Calendar.DATE) > c_end.get(Calendar.DATE))) {
            diff--;
        }
        return diff;
    }
}

I'm adding my renderer as following.

final GeodatabaseFeatureServiceTable fsTable = new GeodatabaseFeatureServiceTable(layerData.getFullUrl(), arcgisSdkConfigurator.getUserCredentials(), layerData.getLayerIndex());
fsTable.initialize(new CallbackListener<GeodatabaseFeatureServiceTable.Status>() {
    @Override
    public void onCallback(GeodatabaseFeatureServiceTable.Status status) {
        if (status == GeodatabaseFeatureServiceTable.Status.INITIALIZED) {
            // prepare out fields
            List<String> attributeList = AstFeatureNameUtil.extractAttributeList(layerData.getIdentifierPattern());
            attributeList.add("InstallationDate");
            String[] attributes = new String[attributeList.size()];
            attributeList.toArray(attributes);
            fsTable.setOutFields(attributes);

            final FeatureLayer featureLayer = new FeatureLayer(fsTable);
            featureLayer.initializeAsync(); // need?
            featureLayer.addLayerInitializeCompleteListener(new LayerInitializeCompleteListener() {
                @Override
                public void layerInitializeComplete(LayerInitializeCompleteEvent lice) {
                    //LOGGER.log(Level.INFO, "Found features: {0} - {1}", new Object[]{layerData.getLayerId(), fsTable.getNumberOfFeatures()});
                    //LOGGER.log(Level.INFO, "Layer initialized: {0}", lice.getID());
                    DeviceAgingRenderer renderer = new DeviceAgingRenderer("InstallationDate", featureLayer.getGeometryType());
                    //LOGGER.log(Level.INFO, "Renderer: {0}", renderer);
                    featureLayer.setRenderer(renderer);

                    jMap.getLayers().add(featureLayer);
                    jMap.addMapOverlay(new DeviceAgingFeatureMapOverlay(jMap, featureLayer));
                    jLayerTree.refresh();
                }
            });
        }
    }

    @Override
    public void onError(Throwable e) {
        LOGGER.log(Level.SEVERE, null, e);
        makeBusy(false);
    }
});
1

As ESRI staff said custom renderer is not supported: https://geonet.esri.com/message/688467-re-implement-custom-renderer-like-classbreaksrenderer?commentID=688467&et=watches.email.thread#comment-688467

It even mentioned in the apidoc: https://developers.arcgis.com/java/10-2/api-reference/com/esri/core/renderer/BaseRenderer.html

So we have to use the existing ClassBreaksRenderer. ClassBreaksRenderer is not working correctly with double or float values, it works fine on integer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.