Dynamic layer labeling

Download Sample Viewer

Description

This sample demonstrates how to customize and control the labeling of a dynamic map service layer. Customization includes whether to show labels or not, where the labels appear relative to the features they are labeling, the content of the labels, and a scale threshold for the labels such that if the map is set to a scale outside this range, labels should not be shown.

Note: the scale threshold functionality depends on the screen DPI used in the application; obtain this value by calling ArcGISRuntime.getDPI(). To manually set a different DPI (before any map is created) use ArcGISRuntime.setDPI(int), or the command line argument -Dcom.esri.runtime.dpi=.

Code snippet


  // create labeling info
  labelingInfo = new LabelingInfo();
  labelingInfo.setLabelPlacement(LabelPlacement.POINT_BELOW_RIGHT);
  labelingInfo.setLabelExpression("[areaname]");
  labelingInfo.setMinScale(600000);
  labelingInfo.setMaxScale(30000);
  labelTextSymbol = new TextSymbol(10, null, Color.BLACK);
  labelingInfo.setSymbol(labelTextSymbol);

  // add labeling info to drawing info
  drawingInfo = new DrawingInfo();
  drawingInfo.setShowLabels(true);
  drawingInfo.setLabelingInfo(new LabelingInfo[] {labelingInfo});

  // update layer info to use the new drawing info
  DynamicLayerInfo labelLayerInfo = (dynamicLayer.getDynamicLayerInfos()).get(0);
  labelLayerInfo.setDrawingInfo(drawingInfo);

  // update 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.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashMap;
import java.util.Map;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

import com.esri.core.geometry.Envelope;
import com.esri.core.map.DrawingInfo;
import com.esri.core.map.DynamicLayerInfo;
import com.esri.core.map.LabelPlacement;
import com.esri.core.map.LabelingInfo;
import com.esri.core.symbol.TextSymbol;
import com.esri.map.ArcGISDynamicMapServiceLayer;
import com.esri.map.ArcGISTiledMapServiceLayer;
import com.esri.map.JMap;
import com.esri.map.LayerInitializeCompleteEvent;
import com.esri.map.LayerInitializeCompleteListener;
import com.esri.map.LayerList;
import com.esri.map.MapEvent;
import com.esri.map.MapEventListenerAdapter;

/**
 * This sample demonstrates how to customize and control the labeling of a dynamic map service layer.
 * Customization includes whether to show labels or not, where the labels appear relative to the features 
 * they are labeling, the content of the labels, and a scale threshold for the labels such that if the 
 * map is set to a scale outside this range, labels should not be shown.
 * <p>
 * <b>Note:</b> the scale threshold functionality depends on the screen DPI used in the application; 
 * obtain this value by calling <code>ArcGISRuntime.getDPI()</code>. To manually set a different DPI 
 * (before any map is created) use <code>ArcGISRuntime.setDPI(int)</code>, or the command line argument 
 * <code>-Dcom.esri.runtime.dpi=</code>.
 */
public class LabelingApp {

  // resources
  private final static String URL_BASE_LAYER = 
      "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer";
  private final static String URL_DYNAMIC_LAYER = 
      "http://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer";
  private final static Map<String, Integer> LAYER_NAMES_TO_IDS = new HashMap<>();
  private final static Map<Integer, String[]> LAYER_IDS_TO_FIELDS = new HashMap<>();
  // initialize info about sub layers and its fields
  // the dynamic layer has 4 sub layers - "cities", "highways", "states", and "counties". This sample uses
  // first 3 (indexed 0, 1, 2) of those.
  private final static Integer[] LAYER_IDS = new Integer[] {new Integer(0), new Integer(1), new Integer(2)};
  static {
    LAYER_NAMES_TO_IDS.put("Cities", LAYER_IDS[0]);
    LAYER_IDS_TO_FIELDS.put(LAYER_IDS[0], new String[] {"areaname", "pop2000"});
    LAYER_NAMES_TO_IDS.put("Highways", LAYER_IDS[1]);
    LAYER_IDS_TO_FIELDS.put(LAYER_IDS[1], new String[] {"type", "route"});
    LAYER_NAMES_TO_IDS.put("States", LAYER_IDS[2]);
    LAYER_IDS_TO_FIELDS.put(LAYER_IDS[2], new String[] {"state_name", "state_abbr"});
  }
  private ArcGISDynamicMapServiceLayer dynamicLayer;

  // drawing info
  private DrawingInfo drawingInfo;
  private LabelingInfo labelingInfo;
  private TextSymbol labelTextSymbol;

  // JMap
  private JMap jMap;

  // UI
  private final JCheckBox cbxShowLabels = new JCheckBox("Show Labels");
  private final JComboBox<String> cbxPlacement  = new JComboBox<>();
  private final JTextField txtMinScale = new JTextField("");
  private final JTextField txtMaxScale = new JTextField("");
  private final JTextField txtTextSize = new JTextField("");
  private final JTextField txtCurrentScale = new JTextField("");
  private final JComboBox<String> cbxLayers = new JComboBox<>();
  private final JComboBox<String> cbxFields = new JComboBox<>();
  private final static int PANEL_HEIGHT = 400;
  private final static int PANEL_WIDTH = 220;
  private final static int BUTTON_HEIGHT = 25;
  private final static Color CONTROL_PANEL_COLOR = new Color(0, 0, 0, 255);

  // label placement values for drop-down
  private final static Map<String, LabelPlacement> POINT_LABEL_PLACEMENTS = 
      new HashMap<>();
  private final static Map<String, LabelPlacement> LINE_LABEL_PLACEMENTS = 
      new HashMap<>();
  private final static Map<String, LabelPlacement> POLYGON_LABEL_PLACEMENTS = 
      new HashMap<>();
  static {
    POINT_LABEL_PLACEMENTS.put("Above Center", LabelPlacement.POINT_ABOVE_CENTER);
    POINT_LABEL_PLACEMENTS.put("Above Left", LabelPlacement.POINT_ABOVE_LEFT);
    POINT_LABEL_PLACEMENTS.put("Above Right", LabelPlacement.POINT_ABOVE_RIGHT);
    POINT_LABEL_PLACEMENTS.put("Below Center", LabelPlacement.POINT_BELOW_CENTER);
    POINT_LABEL_PLACEMENTS.put("Below Left", LabelPlacement.POINT_BELOW_LEFT);
    POINT_LABEL_PLACEMENTS.put("Below Right", LabelPlacement.POINT_BELOW_RIGHT);
    POINT_LABEL_PLACEMENTS.put("Center Center", LabelPlacement.POINT_CENTER_CENTER);
    POINT_LABEL_PLACEMENTS.put("Center Left", LabelPlacement.POINT_CENTER_LEFT);
    POINT_LABEL_PLACEMENTS.put("Center Right", LabelPlacement.POINT_CENTER_RIGHT);

    LINE_LABEL_PLACEMENTS.put("Above Left", LabelPlacement.LINE_ABOVE_AFTER);
    LINE_LABEL_PLACEMENTS.put("Above Along", LabelPlacement.LINE_ABOVE_ALONG);
    LINE_LABEL_PLACEMENTS.put("Above Before", LabelPlacement.LINE_ABOVE_BEFORE);
    LINE_LABEL_PLACEMENTS.put("Above Start", LabelPlacement.LINE_ABOVE_START);
    LINE_LABEL_PLACEMENTS.put("Below End", LabelPlacement.LINE_ABOVE_END);
    LINE_LABEL_PLACEMENTS.put("Below After", LabelPlacement.LINE_BELOW_AFTER);
    LINE_LABEL_PLACEMENTS.put("Below Along", LabelPlacement.LINE_BELOW_ALONG);
    LINE_LABEL_PLACEMENTS.put("Below Left", LabelPlacement.LINE_BELOW_BEFORE);
    LINE_LABEL_PLACEMENTS.put("Below Start", LabelPlacement.LINE_BELOW_START);
    LINE_LABEL_PLACEMENTS.put("Below End", LabelPlacement.LINE_BELOW_END);
    LINE_LABEL_PLACEMENTS.put("Center After", LabelPlacement.LINE_CENTER_AFTER);
    LINE_LABEL_PLACEMENTS.put("Center Along", LabelPlacement.LINE_CENTER_ALONG);
    LINE_LABEL_PLACEMENTS.put("Center Left", LabelPlacement.LINE_CENTER_BEFORE);
    LINE_LABEL_PLACEMENTS.put("Center Start", LabelPlacement.LINE_CENTER_START);
    LINE_LABEL_PLACEMENTS.put("Center End", LabelPlacement.LINE_CENTER_END);

    POLYGON_LABEL_PLACEMENTS.put("Above Center", LabelPlacement.POLYGON_ABOVE_CENTER);
  }
  private final static Map<Integer, DefaultComboBoxModel<String>> LAYER_TO_CBMODEL_MAP = new HashMap<>();
  
  static {
    LAYER_TO_CBMODEL_MAP.put(LAYER_IDS[0], 
        new DefaultComboBoxModel<>(POINT_LABEL_PLACEMENTS.keySet().toArray(new String[POINT_LABEL_PLACEMENTS.size()]))); 
    LAYER_TO_CBMODEL_MAP.put(LAYER_IDS[1], 
        new DefaultComboBoxModel<>(LINE_LABEL_PLACEMENTS.keySet().toArray(new String[LINE_LABEL_PLACEMENTS.size()])));
    LAYER_TO_CBMODEL_MAP.put(LAYER_IDS[2], 
        new DefaultComboBoxModel<>(POLYGON_LABEL_PLACEMENTS.keySet().toArray(new String[POLYGON_LABEL_PLACEMENTS.size()])));
  }

  // Constructor
  public LabelingApp() { }

  // ------------------------------------------------------------------------
  // Core functionality
  // ------------------------------------------------------------------------
  private void initLabelingInfo() {
    // create labeling info
    labelingInfo = new LabelingInfo();
    labelingInfo.setLabelPlacement(LabelPlacement.POINT_BELOW_RIGHT);
    labelingInfo.setLabelExpression("[areaname]");
    labelingInfo.setMinScale(600000);
    labelingInfo.setMaxScale(30000);
    labelTextSymbol = new TextSymbol(10, null, Color.BLACK);
    labelingInfo.setSymbol(labelTextSymbol);

    // add labeling info to drawing info
    drawingInfo = new DrawingInfo();
    drawingInfo.setShowLabels(true);
    drawingInfo.setLabelingInfo(new LabelingInfo[] {labelingInfo});

    // update layer info to use the new drawing info
    DynamicLayerInfo labelLayerInfo = (dynamicLayer.getDynamicLayerInfos()).get(0);
    labelLayerInfo.setDrawingInfo(drawingInfo);

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

  private void updateLabelingInfo(
      boolean showLabels,
      LabelPlacement labelPlacement,
      int layerIndex,
      String fieldName,
      int textSize,
      Color textColor,
      double minScale,
      double maxScale) {
    // update labeling info
    labelingInfo.setLabelPlacement(labelPlacement);
    labelingInfo.setLabelExpression("[" + fieldName + "]");
    labelTextSymbol.setSize(textSize);
    labelTextSymbol.setColor(textColor);
    labelingInfo.setSymbol(labelTextSymbol);
    labelingInfo.setMinScale(minScale);
    labelingInfo.setMaxScale(maxScale);

    // update drawing info
    drawingInfo.setShowLabels(showLabels);

    // update layer info to use the updated drawing info
    DynamicLayerInfo labelLayerInfo = (dynamicLayer.getDynamicLayerInfos()).get(layerIndex);
    labelLayerInfo.setDrawingInfo(drawingInfo);

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

  // ------------------------------------------------------------------------
  // 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
          LabelingApp LabelingApp = new LabelingApp();

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

  // ------------------------------------------------------------------------
  // Public methods
  // ------------------------------------------------------------------------

  /**
   * Creates and displays the UI, including the map, for this application.
   */
  public JComponent createUI() throws Exception {
    // application content
    JComponent contentPane = createContentPane();

    // map
    jMap = createMap();

    // control panel
    final JPanel controlPanel = createControlPanel();
    controlPanel.setLocation(10, 10 + BUTTON_HEIGHT);
    controlPanel.setSize(PANEL_WIDTH, PANEL_HEIGHT);
    contentPane.add(controlPanel);

    contentPane.add(jMap);

    return contentPane;
  }

  // ------------------------------------------------------------------------
  // Private methods
  // ------------------------------------------------------------------------
  /**
   * Creates a window.
   * @return a window.
   */
  private JFrame createWindow() {
    JFrame window = new JFrame("Dynamic Layer Labeling");
    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);
        jMap.dispose();
      }
    });
    return window;
  }

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

  /**
   * Creates a map.
   * @return a map.
   */
  private JMap createMap() throws Exception {
    jMap = new JMap();
    // -----------------------------------------------------------------------------------------
    // Base Layer
    // -----------------------------------------------------------------------------------------
    final ArcGISTiledMapServiceLayer baseLayer = new ArcGISTiledMapServiceLayer(URL_BASE_LAYER);

    // -----------------------------------------------------------------------------------------
    // Dynamic layer
    // -----------------------------------------------------------------------------------------
    dynamicLayer = new ArcGISDynamicMapServiceLayer(URL_DYNAMIC_LAYER);
    dynamicLayer.addLayerInitializeCompleteListener(new LayerInitializeCompleteListener() {

      @Override
      public void layerInitializeComplete(LayerInitializeCompleteEvent e) {
        // update labeling info and UI
        initLabelingInfo();
        SwingUtilities.invokeLater(new Runnable() {
          @Override
          public void run() {
            popuplateControlPanel();
          }
        });
      }
    });

    // zoom to area of interest
    jMap.setExtent(new Envelope(-13683056, 4474041, -13532934, 4575038));
    jMap.addMapEventListener(new MapEventListenerAdapter() {
      @Override
      public void mapExtentChanged(MapEvent event) {
        if (txtMinScale.getText().isEmpty() || txtMaxScale.getText().isEmpty()) {
          return;
        }
        if (jMap.getScale() < Integer.parseInt(txtMinScale.getText()) && 
            jMap.getScale() > Integer.parseInt(txtMaxScale.getText())) {
          txtCurrentScale.setBackground(Color.BLACK);
          txtCurrentScale.setText(jMap.getScale() + " (Visible)");
        } else {
          txtCurrentScale.setBackground(Color.RED);
          txtCurrentScale.setText(jMap.getScale() + " (Hidden)");
        }

      }
    });

    // add layers to map
    LayerList layers = jMap.getLayers();
    layers.add(baseLayer);
    layers.add(dynamicLayer);

    return jMap;
  }

  private JPanel createControlPanel() {
    // label for title
    JTextField lblTitle = new JTextField("Label Properties");
    lblTitle.setHorizontalAlignment(SwingConstants.CENTER);
    lblTitle.setMaximumSize(new Dimension(250, 20));
    lblTitle.setForeground(Color.WHITE);
    lblTitle.setBackground(null);
    lblTitle.setEditable(false);

    // checkbox option for label visibility
    cbxShowLabels.setMaximumSize(new Dimension(250, 20));
    cbxShowLabels.setForeground(Color.WHITE);
    cbxShowLabels.setBackground(null);
    cbxShowLabels.setAlignmentX(SwingConstants.LEFT);
    cbxShowLabels.setFocusPainted(false);

    // label for show placement
    JTextField lblPlacement = new JTextField("Label Placement:");
    lblPlacement.setMaximumSize(new Dimension(250, 20));
    lblPlacement.setForeground(Color.WHITE);
    lblPlacement.setBackground(null);
    lblPlacement.setEditable(false);
    lblPlacement.setBorder(null);

    // drop-down list for the label placement
    cbxPlacement.setMaximumSize(new Dimension(250, 20));

    // label for fields
    JTextField lblFields = new JTextField("Label Field: ");
    lblFields.setMaximumSize(new Dimension(250, 20));
    lblFields.setForeground(Color.WHITE);
    lblFields.setBackground(null);
    lblFields.setEditable(false);
    lblFields.setBorder(null);

    // drop-down list for the fields
    cbxFields.setMaximumSize(new Dimension(250, 20));

    // label for layers
    JTextField lblLayers = new JTextField("Layers: ");
    lblLayers.setMaximumSize(new Dimension(250, 20));
    lblLayers.setForeground(Color.WHITE);
    lblLayers.setBackground(null);
    lblLayers.setEditable(false);
    lblLayers.setBorder(null);

    // drop-down list for the layers
    cbxLayers.setMaximumSize(new Dimension(250, 20));
    cbxLayers.addItemListener(new ItemListener() {
      @Override
      public void itemStateChanged(ItemEvent e) {
        Integer selectedLayer = LAYER_NAMES_TO_IDS.get(cbxLayers.getSelectedItem());
        cbxFields.setModel(new DefaultComboBoxModel<>(LAYER_IDS_TO_FIELDS.get(selectedLayer)));
        cbxPlacement.setModel(LAYER_TO_CBMODEL_MAP.get(selectedLayer));
      }
    });

    // label for text
    JTextField lblText = new JTextField("Text Size:");
    lblText.setMaximumSize(new Dimension(250, 20));
    lblText.setForeground(Color.WHITE);
    lblText.setBackground(null);
    lblText.setEditable(false);
    lblText.setBorder(null);

    // textbox for text size
    txtTextSize.setMaximumSize(new Dimension(250, 20));
    txtTextSize.setForeground(Color.WHITE);
    txtTextSize.setBackground(null);

    // color chooser for text color
    final JButton btnTextColor = new JButton("Text Color");
    btnTextColor.setMaximumSize(new Dimension(250, 20));
    btnTextColor.setBackground(Color.DARK_GRAY);
    btnTextColor.setForeground(Color.WHITE);
    btnTextColor.setFocusPainted(false);
    btnTextColor.setAlignmentX(Component.CENTER_ALIGNMENT);
    btnTextColor.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        Color labelTextColor = JColorChooser.showDialog(
            jMap,
            "Choose Label text color",
            btnTextColor.getBackground());
        btnTextColor.setBackground(labelTextColor);
        btnTextColor.setForeground(
            new Color(255 - labelTextColor.getRed(), 255 - labelTextColor.getGreen(), 255 - labelTextColor.getBlue()));

      }
    });

    // label for min scale
    JTextField lblMinScale = new JTextField("Minimum scale:");
    lblMinScale.setMaximumSize(new Dimension(250, 20));
    lblMinScale.setForeground(Color.WHITE);
    lblMinScale.setBackground(null);
    lblMinScale.setEditable(false);
    lblMinScale.setBorder(null);

    // input for min scale
    txtMinScale.setMaximumSize(new Dimension(250, 20));
    txtMinScale.setForeground(Color.WHITE);
    txtMinScale.setBackground(null);

    // label for current scale
    JTextField lblCurrentScale = new JTextField("Current Scale:");
    lblCurrentScale.setMaximumSize(new Dimension(250, 20));
    lblCurrentScale.setForeground(Color.WHITE);
    lblCurrentScale.setBackground(null);
    lblCurrentScale.setEditable(false);
    lblCurrentScale.setBorder(null);

    // current scale
    txtCurrentScale.setMaximumSize(new Dimension(250, 20));
    txtCurrentScale.setForeground(Color.WHITE);
    txtCurrentScale.setBackground(null);
    txtCurrentScale.setEditable(false);
    txtCurrentScale.setBorder(null);

    // label for max scale
    JTextField lblMaxScale = new JTextField("Maximum scale:");
    lblMaxScale.setMaximumSize(new Dimension(250, 20));
    lblMaxScale.setForeground(Color.WHITE);
    lblMaxScale.setBackground(null);
    lblMaxScale.setEditable(false);
    lblMaxScale.setBorder(null);

    // input for max scale
    txtMaxScale.setMaximumSize(new Dimension(250, 20));
    txtMaxScale.setForeground(Color.WHITE);
    txtMaxScale.setBackground(null);

    // button to apply
    JButton btnApply = new JButton("Apply");
    btnApply.setMaximumSize(new Dimension(250, 20));
    btnApply.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        int textSize = 10;
        try {
          textSize = Integer.valueOf(txtTextSize.getText()).intValue();
        } catch (Exception ex) {
          JOptionPane.showMessageDialog(jMap, "Enter a valid integer for text size.",
              "", JOptionPane.ERROR_MESSAGE);
          return;
        }
        int selectedLayer = LAYER_NAMES_TO_IDS.get(cbxLayers.getSelectedItem()).intValue();
        LabelPlacement labelPlacement;
        if (selectedLayer == 0) {
          labelPlacement = POINT_LABEL_PLACEMENTS.get(cbxPlacement.getSelectedItem());
        } else if (selectedLayer == 1) {
          labelPlacement = LINE_LABEL_PLACEMENTS.get(cbxPlacement.getSelectedItem());
        } else {
          labelPlacement = POLYGON_LABEL_PLACEMENTS.get(cbxPlacement.getSelectedItem());
        }

        double minScale = 0;
        double maxScale = 0;
        try {
          minScale = Double.parseDouble(txtMinScale.getText());
          maxScale = Double.parseDouble(txtMaxScale.getText());
        } catch (Exception ex) {
          JOptionPane.showMessageDialog(jMap, "Enter a valid double for scale.",
              "", JOptionPane.ERROR_MESSAGE);
        }

        updateLabelingInfo(
            cbxShowLabels.isSelected(),
            labelPlacement,
            selectedLayer,
            (String) cbxFields.getSelectedItem(),
            textSize,
            btnTextColor.getBackground(),
            minScale,
            maxScale);
      }
    });
    btnApply.setAlignmentX(Component.CENTER_ALIGNMENT);

    // group the above UI items into a control panel
    final JPanel controlPanel = new JPanel();
    BoxLayout boxLayout = new BoxLayout(controlPanel, BoxLayout.Y_AXIS);
    controlPanel.setAlignmentX(SwingConstants.CENTER);
    controlPanel.setLayout(boxLayout);
    controlPanel.setBackground(CONTROL_PANEL_COLOR);
    controlPanel.setDoubleBuffered(true);
    controlPanel.setBorder(new LineBorder(Color.BLACK, 5, false));
    controlPanel.setVisible(true);

    controlPanel.add(lblTitle);
    controlPanel.add(Box.createHorizontalStrut(5));
    controlPanel.add(cbxShowLabels);
    controlPanel.add(lblPlacement);
    controlPanel.add(cbxPlacement);
    controlPanel.add(lblLayers);
    controlPanel.add(cbxLayers);
    controlPanel.add(lblFields);
    controlPanel.add(cbxFields);
    controlPanel.add(lblText);
    controlPanel.add(txtTextSize);
    controlPanel.add(btnTextColor);
    controlPanel.add(lblMinScale);
    controlPanel.add(txtMinScale);
    controlPanel.add(lblCurrentScale);
    controlPanel.add(txtCurrentScale);
    controlPanel.add(lblMaxScale);
    controlPanel.add(txtMaxScale);
    controlPanel.add(Box.createHorizontalStrut(5));
    controlPanel.add(btnApply);

    return controlPanel;
  }

  private void popuplateControlPanel() {
    // initialize UI to match state
    cbxShowLabels.setSelected(true);
    txtTextSize.setText("10");
    cbxLayers.setModel(new DefaultComboBoxModel<>(LAYER_NAMES_TO_IDS.keySet().toArray(new String[LAYER_NAMES_TO_IDS.size()])));
    cbxFields.setModel(new DefaultComboBoxModel<>(LAYER_IDS_TO_FIELDS.get(LAYER_IDS[0])));
    cbxPlacement.setModel(LAYER_TO_CBMODEL_MAP.get(LAYER_IDS[0]));
    txtMinScale.setText("600000");
    txtMaxScale.setText("30000");
  }
}
Feedback on this topic?