Class breaks renderer

Download Sample Viewer

Description

This sample demonstrates that the client can override the class breaks renderers specified by a dynamic map service layer. The dynamic layer needs to be enabled for dynamic rendering by calling setEnableDynamicLayers(true) on the layer before it is added to the map. The new renderers are created client-side but the information is applied at the server level when rendering the map image. This sample uses the execute method from the GenerateDataClassesTask class to generate a new class breaks renderer with the chosen parameters.

Code snippet


    // Create new drawing info from our renderer, with set transparency
    DrawingInfo drawingInfo = new DrawingInfo(renderer, TRANSPARENCY);
    
    // Get the layerInfo from the dynamic layer and set the drawing info
    DynamicLayerInfoCollection layerInfos = dynamicLayer.getDynamicLayerInfos();
    DynamicLayerInfo layerInfo = layerInfos.get(layerId);
    layerInfo.setDrawingInfo(drawingInfo);

    // Refresh the layer
    dynamicLayer.refresh();
  

Sample Code

/* Copyright 2014 Esri

All rights reserved under the copyright laws of the United States
and applicable international laws, treaties, and conventions.

You may freely redistribute and use this sample code, with or
without modification, provided you include the original copyright
notice and use restrictions.

See the use restrictions.*/
package com.esri.client.samples.dynamiclayers;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.HashMap;
import java.util.Random;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;

import com.esri.client.local.ArcGISLocalDynamicMapServiceLayer;
import com.esri.client.local.ArcGISLocalTiledLayer;
import com.esri.core.geometry.Envelope;
import com.esri.core.map.DrawingInfo;
import com.esri.core.map.DynamicLayerInfo;
import com.esri.core.map.DynamicLayerInfoCollection;
import com.esri.core.renderer.AlgorithmicColorRamp;
import com.esri.core.renderer.ClassBreaksDefinition;
import com.esri.core.renderer.ClassBreaksDefinition.ClassificationMethod;
import com.esri.core.renderer.ClassBreaksRenderer;
import com.esri.core.renderer.ColorRamp;
import com.esri.core.renderer.NormalizationType;
import com.esri.core.renderer.RampDefinition;
import com.esri.core.renderer.Renderer;
import com.esri.core.tasks.ags.GenerateRendererTask;
import com.esri.core.tasks.ags.GenerateRendererTaskParameters;
import com.esri.map.JMap;
import com.esri.map.LayerInitializeCompleteEvent;
import com.esri.map.LayerInitializeCompleteListener;
import com.esri.map.LayerList;
import com.esri.runtime.ArcGISRuntime;

/***
 * This sample demonstrates that the client can override the class breaks
 * renderers specified by a dynamic map service layer. The dynamic layer
 * needs to be enabled for dynamic rendering by calling
 * setEnableDynamicLayers(true) on the layer before it is added to the map.
 * The new renderers are created client-side but the information is applied at
 * the server level when rendering the map image.
 * <p>
 * This sample uses the execute method from the GenerateRendererTask class to
 * generate a new class breaks renderer with the chosen parameters.
 *
 */
public class LocalDynamicClassBreaksRenderer {

  private JComponent contentPane;
  private JMap map;
  private JProgressBar progressBar;
  private Random random;
  private JComboBox<String> classList;
  private JComboBox<String> methodList;
  private JComboBox<Integer> numBreaksList;
  private JComboBox<String> normalizeByList;
  private HashMap<String, ClassificationMethod> methodMap;
  private JButton renderButton;
  private JButton defaultButton;

  private static final int TRANSPARENCY = 30; // 0 is opaque, 100 is transparent
  private static final int COMPONENT_WIDTH = 220;
  private static final int COMPONENT_HEIGHT = 25;
  private static final Dimension BUTTON_DIMENSION = new Dimension(COMPONENT_WIDTH, COMPONENT_HEIGHT + 5);
  private int selectedLayerId;
  private int selectedBreaks;
  private ClassificationMethod selectedMethod;
  private NormalizationType selectedNormalizationMethod;
  private static final String classificationField = "POP2000";
  private static final String CLASS1_STRING = "Cities (Population 2000)";
  private static final String CLASS2_STRING = "States (Population 2000)";
  private ArcGISLocalDynamicMapServiceLayer dynamicLayer;

  // ------------------------------------------------------------------------
  // Constructor
  // ------------------------------------------------------------------------
  /**
   * Creates an instance of this sample.
   */
  public LocalDynamicClassBreaksRenderer() {
  }

  // ------------------------------------------------------------------------
  // Core functionality: how to change rendering options for Dynamic Layers
  // ------------------------------------------------------------------------
  /**
   * Sets the drawing info of sublayer number 'layerId' of a dynamic layer
   * with the given renderer
   * @param renderer
   * @param layerId
   */
  private void render(Renderer renderer, int layerId) {

    // Create new drawing info from our renderer, with set transparency
    DrawingInfo drawingInfo = new DrawingInfo(renderer, TRANSPARENCY);

    // Get the layerInfo from the dynamic layer and set the drawing info
    DynamicLayerInfoCollection layerInfos = dynamicLayer.getDynamicLayerInfos();
    DynamicLayerInfo layerInfo = layerInfos.get(layerId);
    layerInfo.setDrawingInfo(drawingInfo);

    // Refresh the layer
    dynamicLayer.refresh();
  }

  /**
   * Resets to the default rendering options
   */
  private void resetDefault() {
    DynamicLayerInfoCollection layerInfos = dynamicLayer.getDynamicLayerInfos();
    for (DynamicLayerInfo layerInfo : layerInfos) {
      layerInfo.resetToDefault();
    }
    dynamicLayer.refresh();
  }

  private ClassBreaksRenderer getClassBreaksRenderer(
      int layerId, ClassificationMethod classificationMethod, int numBreaks, NormalizationType normalizationMethod) {

    ClassBreaksDefinition classificationDef = null;

    if (normalizationMethod.compareTo(NormalizationType.None) == 0){
      // no normalization, could use 'createByClassificationMethod' or pass NormalizationType.None to 'createByNormalizationMethod'
      classificationDef = ClassBreaksDefinition.createByClassificationMethod(
          classificationField, classificationMethod, numBreaks);
    }else {// normalize
      classificationDef = ClassBreaksDefinition.createByNormalizationMethod(
          classificationField, classificationMethod, numBreaks, normalizationMethod);
      if (normalizationMethod.compareTo(NormalizationType.ByField) == 0){
        classificationDef.setNormalizationField("FAMILIES");
      }
    }

    Color[] colors = getFromAndToColor();
    ColorRamp colorRamp = new AlgorithmicColorRamp(colors[0].getRGB(), colors[1].getRGB(), RampDefinition.Algorithm.HSV);
    classificationDef.setColorRamp(colorRamp);

    // generate a class breaks renderer using our class breaks definition
    GenerateRendererTask rendererTask = new GenerateRendererTask(
        dynamicLayer.getUrl() + "/" + layerId);
    GenerateRendererTaskParameters taskParams = new GenerateRendererTaskParameters(classificationDef);
    ClassBreaksRenderer cbRenderer = null;
    try {
      cbRenderer = (ClassBreaksRenderer) rendererTask.execute(taskParams);
    } catch (Exception e) {
      e.printStackTrace();
      JOptionPane.showMessageDialog(contentPane.getParent(), wrap(e.getMessage()), "", JOptionPane.ERROR_MESSAGE);
    }
    // Note that using the above, the Standard Deviation interval is set to the
    // default value of 1 and the number of breaks is ignored in this case.
    // It is possible to set the interval when creating a ClassBreaksDefinition
    // via the createByStandardDeviation method or by calling setStdDevInterval(float)
    // on the ClassBreaksDefinition object.

    return cbRenderer;
  }

  // ------------------------------------------------------------------------
  // Public methods
  // ------------------------------------------------------------------------
  /**
   * Creates and displays the UI, including the map, for this application.
   * @return the UI component.
   */
  public JComponent createUI() {

    random = new Random();

    contentPane = createContentPane();

    // button panel
    JPanel buttonPanel = createButtonPanel();
    contentPane.add(buttonPanel);

    // progress bar
    progressBar = createProgressBar(contentPane);
    contentPane.add(progressBar);

    // create map
    map = createMap();
    contentPane.add(map);

    return contentPane;
  }

  // ------------------------------------------------------------------------
  // Static methods
  // ------------------------------------------------------------------------
  /**
   * Starting point of this application.
   * @param args arguments to this application.
   */
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        try {
          // instance of this application
          LocalDynamicClassBreaksRenderer app = new LocalDynamicClassBreaksRenderer();

          // create the UI, including the map, for the application.
          JFrame appWindow = app.createWindow();
          appWindow.add(app.createUI());
          appWindow.setVisible(true);
        } catch (Exception e) {
          // on any error, display the stack trace.
          e.printStackTrace();
        }
      }
    });
  }

  // ------------------------------------------------------------------------
  // Private methods
  // ------------------------------------------------------------------------
  /**
   * Creates the map.
   * @return a map.
   */
  private JMap createMap() {

    final JMap jMap = new JMap();
    jMap.setExtent(new Envelope(-15000000, 2000000, -7000000, 8000000));

    // setting a tiled layer as basemap
    ArcGISLocalTiledLayer tiledLayer = new ArcGISLocalTiledLayer(
        getPathSampleData() + "tpks/Topographic.tpk");
    final LayerList layers = jMap.getLayers();
    layers.add(tiledLayer);

    updateProgresBarUI("Starting local dynamic map service...", true);
    // create the dynamic layer
    dynamicLayer = new ArcGISLocalDynamicMapServiceLayer(
        getPathSampleData() + "mpks/USCitiesStates.mpk");
    // Allow the client to override the default rendering options by setting the enabling
    // dynamic layers to be true. This must be done before adding the layer to the map.
    dynamicLayer.setEnableDynamicLayers(true);
    dynamicLayer
        .addLayerInitializeCompleteListener(new LayerInitializeCompleteListener() {
          @Override
          public void layerInitializeComplete(LayerInitializeCompleteEvent arg0) {
            synchronized (progressBar) {
              if (arg0.getID() == LayerInitializeCompleteEvent.LOCALLAYERCREATE_ERROR) {
                String errMsg = "Failed to initialize due to "
                    + dynamicLayer.getInitializationError();
                JOptionPane.showMessageDialog(jMap, wrap(errMsg), "",
                    JOptionPane.ERROR_MESSAGE);
              }
              updateProgresBarUI(null, false);
            }

            // activate buttons once the dynamic layer is done initializing
            renderButton.setEnabled(true);
            defaultButton.setEnabled(true);
          }
        });
    // then, add the layer to the map
    layers.add(dynamicLayer);

    return jMap;
  }

  private JPanel createButtonPanel() {
    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    panel.setSize(COMPONENT_WIDTH + 20, 320);
    panel.setLocation(10, 10);

    JLabel classes = new JLabel("Layer:");
    classes.setForeground(Color.WHITE);
    classes.setAlignmentX(Component.LEFT_ALIGNMENT);
    String[] classStrings = {CLASS1_STRING, CLASS2_STRING};
    classList = new JComboBox<>(classStrings);
    classList.setMaximumSize(new Dimension(COMPONENT_WIDTH, COMPONENT_HEIGHT));
    classList.setSelectedIndex(1);
    classList.setAlignmentX(Component.LEFT_ALIGNMENT);

    // set up info for classification methods
    // other option is to used the Enum Strings as is
    String[] methodStrings =
      {"Natural Breaks", "Equal Interval", "Quantile", "Standard Deviation", "Geometrical Interval"};
    methodMap = new HashMap<>();
    ClassificationMethod[] classificationMethods = ClassificationMethod.values();
    for (int i = 0; i < classificationMethods.length; i++) {
      methodMap.put(methodStrings[i], classificationMethods[i]);
    }

    // set up UI for classification methods
    JLabel methods = new JLabel("Classification Method:");
    methods.setForeground(Color.WHITE);
    methods.setAlignmentX(Component.LEFT_ALIGNMENT);

    methodList = new JComboBox<>(methodStrings);
    methodList.setMaximumSize(new Dimension(COMPONENT_WIDTH, COMPONENT_HEIGHT));
    methodList.setSelectedIndex(2); // Quantile as default
    methodList.setAlignmentX(Component.LEFT_ALIGNMENT);

    // set up UI for number of breaks
    JLabel numBreaks = new JLabel("Number of Class Breaks:");
    numBreaks.setForeground(Color.WHITE);
    numBreaks.setAlignmentX(Component.LEFT_ALIGNMENT);
    Integer[] numBreaksInts = {new Integer(3), new Integer(4), new Integer(5), 
        new Integer(6), new Integer(7), new Integer(8)};
    numBreaksList = new JComboBox<>(numBreaksInts);
    numBreaksList.setMaximumSize(new Dimension(COMPONENT_WIDTH, COMPONENT_HEIGHT));
    numBreaksList.setSelectedIndex(2);
    numBreaksList.setAlignmentX(Component.LEFT_ALIGNMENT);

    // set up UI for normalization method
    JLabel normalizeByLabel = new JLabel("Normalize by:");
    normalizeByLabel.setForeground(Color.WHITE);
    normalizeByLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
    String[] normalizeMethods = {NormalizationType.None.name(), NormalizationType.ByField.name(), NormalizationType.ByLog.name(), NormalizationType.ByPercentOfTotal.name()};
    normalizeByList = new JComboBox<>(normalizeMethods);
    normalizeByList.setMaximumSize(new Dimension(COMPONENT_WIDTH, COMPONENT_HEIGHT));
    normalizeByList.setSelectedIndex(0);
    normalizeByList.setAlignmentX(Component.LEFT_ALIGNMENT);

    renderButton = createRenderButton();
    defaultButton = createDefaultButton();

    // layout all the components together into a panel
    panel.setBackground(new Color(0, 0, 0, 120));
    panel.add(classes);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(classList);
    panel.add(Box.createRigidArea(new Dimension(0,15)));
    panel.add(methods);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(methodList);
    panel.add(Box.createRigidArea(new Dimension(0,15)));
    panel.add(numBreaks);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(numBreaksList);
    panel.add(Box.createRigidArea(new Dimension(0,15)));
    panel.add(normalizeByLabel);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(normalizeByList);
    panel.add(Box.createRigidArea(new Dimension(0,15)));
    panel.add(renderButton);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(defaultButton);
    panel.setBorder(BorderFactory.createEmptyBorder(5,10,5,10));
    return panel;
  }

  private JButton createRenderButton() {
    JButton button = new JButton("Change Renderer");
    button.setMaximumSize(BUTTON_DIMENSION);
    button.setMinimumSize(BUTTON_DIMENSION);
    button.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        // get the selected layer ID
        String selectedLayer = (String) classList.getSelectedItem();
        if (selectedLayer.equals(CLASS1_STRING)) {
          selectedLayerId = 0;
        } else if (selectedLayer.equals(CLASS2_STRING)) {
          selectedLayerId = 2;
        }
        // get the selected classification method
        selectedMethod = methodMap.get(methodList.getSelectedItem());
        // get the selected number of breaks
        selectedBreaks = ((Integer) numBreaksList.getSelectedItem()).intValue();
        //get the selected normalization method
        selectedNormalizationMethod = NormalizationType.valueOf(normalizeByList.getSelectedItem().toString());
        // generate a class breaks renderer
        ClassBreaksRenderer cbRenderer =
            getClassBreaksRenderer(selectedLayerId, selectedMethod, selectedBreaks, selectedNormalizationMethod);
        // render the dynamic layer with the generated renderer
        render(cbRenderer, selectedLayerId);
      }
    });
    button.setEnabled(false);
    return button;
  }

  private JButton createDefaultButton() {
    JButton button = new JButton("Reset Renderer");
    button.setMaximumSize(BUTTON_DIMENSION);
    button.setMinimumSize(BUTTON_DIMENSION);
    button.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        resetDefault();
      }
    });
    button.setEnabled(false);
    return button;
  }

  private Color[] getFromAndToColor(){
    Color[] colors = new Color[2];
    int scheme = random.nextInt(3) + 1;
    switch (scheme) {
      case 1:
        // blue
        colors[0] = new Color(200, 230, 255);
        colors[1] = new Color(5, 35, 180);
        break;
      case 2:
        // red
        colors[0] = new Color(240, 240, 240);
        colors[1] = new Color(160, 0, 0);
        break;
      case 3:
        // grayscale
        colors[0] = Color.WHITE;
        colors[1] = Color.BLACK;
        break;
    }
    return colors;
  }

  /**
   * Creates a content pane.
   * @return a content pane.
   */
  private static JLayeredPane createContentPane() {
    JLayeredPane contentPane = new JLayeredPane();
    contentPane.setBounds(100, 100, 1000, 700);
    contentPane.setLayout(new BorderLayout(0, 0));
    contentPane.setVisible(true);
    return contentPane;
  }

  /**
   * Creates a progress bar.
   * @param parent progress bar's parent. The horizontal axis of the progress bar will be
   * center-aligned to the parent.
   * @return a progress bar.
   */
  private static JProgressBar createProgressBar(final JComponent parent) {
    final JProgressBar progressBar = new JProgressBar();
    progressBar.setSize(280, 20);
    parent.addComponentListener(new ComponentAdapter() {
      @Override
      public void componentResized(ComponentEvent e) {
        progressBar.setLocation(
            parent.getWidth()/2 - progressBar.getWidth()/2,
            parent.getHeight() - progressBar.getHeight() - 20);
      }
    });
    progressBar.setStringPainted(true);
    progressBar.setIndeterminate(true);
    return progressBar;
  }

  /**
   * Updates progress bar UI from the Swing's Event Dispatch Thread.
   * @param str string to be set.
   * @param visible flag to indicate visibility of the progress bar.
   */
  private void updateProgresBarUI(final String str, final boolean visible) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        if (str != null) {
          progressBar.setString(str);
        }
        progressBar.setVisible(visible);
      }
    }); 
  }

  /**
   * Creates a window.
   * @return a window.
   */
  private JFrame createWindow() {
    JFrame window = new JFrame("Local Dynamic Class Breaks Renderer Application");
    window.setBounds(100, 100, 1000, 700);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.getContentPane().setLayout(new BorderLayout(0, 0));
    window.addWindowListener(new WindowAdapter() {
      @Override
      public void windowClosing(WindowEvent windowEvent) {
        super.windowClosing(windowEvent);
        map.dispose();
      }
    });
    return window;
  }

  private String getPathSampleData() {
    String FSP = System.getProperty("file.separator");
    String dataPath = null;
    String javaPath = ArcGISRuntime.getInstallDirectory();
    if (javaPath != null) {
      if (!(javaPath.endsWith("/") || javaPath.endsWith("\\"))){
        javaPath += FSP;
      }
      dataPath = javaPath + "sdk" + FSP + "samples" + FSP + "data" + FSP;
    }
    File dataFile = new File(dataPath);
    if (!dataFile.exists()) { 
      dataPath = ".." + FSP + "data" + FSP;
    }
    return dataPath;
  }
  
  private String wrap(String str) {
    // create a HTML string that wraps text when longer
    return "<html><p style='width:200px;'>" + str + "</html>";
  }
  
}
Feedback on this topic?