Military symbol dictionary

Download Sample Viewer

Description

This application shows how to use the SymbolDictionary and MessageProcessor to interactively add MIL-STD-2525C military symbology to a map. A symbol dictionary is created and queried using the user-chosen keywords and filters, returning a list of symbols. A symbol can then be selected from this list and added to the map via mouse clicks. In order to add a symbol to the map, a Message is created using the symbol ID and the clicked point. The message is then sent to the message processor which adds the symbol to the map.

Code snippet


    //creates a symbol dictionary of type Mil2525C
    symDictionary = new SymbolDictionary(SymbolDictionary.DictionaryType.Mil2525C);

    // query the symbol dictionary
    symbols = new ArrayList<SymbolProperties>();
    try {
        symbols = symDictionary.findSymbols(selectedKeywords, selectedFilters);
    } catch (IOException e) {
        // handle the exception as desired
    }
  

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.displayinfo;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.LineBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.Geometry.Type;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.Polygon;
import com.esri.core.geometry.Polyline;
import com.esri.core.map.Feature;
import com.esri.core.symbol.SimpleFillSymbol;
import com.esri.core.symbol.SimpleLineSymbol;
import com.esri.core.symbol.SimpleMarkerSymbol;
import com.esri.core.symbol.SimpleMarkerSymbol.Style;
import com.esri.core.symbol.advanced.Message;
import com.esri.core.symbol.advanced.MessageProcessor;
import com.esri.core.symbol.advanced.SymbolDictionary;
import com.esri.core.symbol.advanced.SymbolProperties;
import com.esri.map.ArcGISTiledMapServiceLayer;
import com.esri.map.GraphicsLayer;
import com.esri.map.JMap;
import com.esri.map.MessageGroupLayer;
import com.esri.toolkit.overlays.DrawingCompleteEvent;
import com.esri.toolkit.overlays.DrawingCompleteListener;
import com.esri.toolkit.overlays.DrawingOverlay;
import com.esri.toolkit.overlays.DrawingOverlay.DrawingMode;

/***
 * <p>
 * This application shows how to use the {@link SymbolDictionary} and {@link MessageProcessor} to interactively add
 * MIL-STD-2525C military symbols to a map. A symbol dictionary is created and queried using the user-chosen keywords
 * and filters, returning a list of symbols. A symbol can then be selected from this list and added to the map via mouse
 * clicks.
 * </p>
 * <p>
 * In order to add a symbol to the map, a {@link Message} is created using the symbol ID and drawn geometry. The message
 * is then sent to the message processor which adds the symbol to the map.
 * </p>
 */

public class SymbolDictionaryApp {
  
  //ArcGIS objects
  private JMap map;
  private MessageProcessor msgProcessor;
  private SymbolDictionary symDictionary;
  private GraphicsLayer graphicsLayer;
  private DrawingOverlay drawingOverlay;
  
  //symbols resulting from searching symbol dictionary
  private List<SymbolProperties> symbols;
  //symbol geometry type (Point Symbol, Line Symbol or Polygon Symbol) and description shown as tooltip of result list
  private List<String> symbolTips;  
  //symbol names shown in result list
  private List<String> symbolNames;
  //symbol images shown in result list
  private List<ImageIcon> symbolImages;

  // Swing components
  private JTree treeFilters;
  private JProgressBar progressBar;
  private JButton btnSearch;
  private JLabel lblResults;
  private JList<String> lstKeywords;
  private JList<String> lstResults;
  private JList<ImageIcon> lstResultsImages;
  
  // ------------------------------------------------------------------------
  // Core functionality
  // ------------------------------------------------------------------------
  /**
   * Creates the symbol dictionary, creates and populates the keyword list and filter tree.
   */
  private void setupSearchDialog() {

    // create a symbol dictionary from the message processor
    symDictionary = new SymbolDictionary(SymbolDictionary.DictionaryType.Mil2525C);

    // get the available keywords and put them in the selection list
    List<String> keywords = new ArrayList<>();
    keywords = symDictionary.getKeywords();
    lstKeywords.setListData(keywords.toArray(new String[keywords.size()]));

    // get the filters and their values and pop them into the tree view
    Map<String, List<String>> filters;
    filters = symDictionary.getFilters();
    DefaultMutableTreeNode baseNode = new DefaultMutableTreeNode("Filters");
    DefaultMutableTreeNode filterNode;

    // go through all filters
    for (String flt : filters.keySet()) {
      // create filter for the tree
      filterNode = new DefaultMutableTreeNode(flt);

      // get the filter values for each of the filters
      List<String> filterValues;
      filterValues = filters.get(flt);
      for (String val : filterValues) {
        // add the filter value to the tree
        filterNode.add(new DefaultMutableTreeNode(val));
      }

      // add filter section to tree
      baseNode.add(filterNode);
    }

    // apply filters to the tree control
    treeFilters.setModel(new DefaultTreeModel(baseNode));
  }

  /**
   * Adds a selected symbol to the map at the clicked point
   * 
   * @param eventInfo MouseEvent from button click
   */
  private void addSymbolToMap(Feature graphic) {
    // are there any results and is one selected?
    Object selectedSymbol = lstResults.getSelectedValue();

    if (selectedSymbol != null) {
      // get the symbol attributes from the selected symbol name
      String sicCode = "";

      // get the SIC code
      for (SymbolProperties props : symbols) {
        // is this our symbol?
        if (selectedSymbol.toString() == props.getName()) {
          // get the SIC code from the symbol values
          Map<String, String> symbVals = props.getValues();
          sicCode = symbVals.get("SymbolID");

          // break out of loop as we have found the symbol
          break;
        }
      }

      // get the XY coords from the drawn geometry
      String coords = getControlPoints(graphic);

      // create and process message, adding it to the map
      Message message = new Message();
      UUID uuid = UUID.randomUUID();
      message.setID(uuid.toString());
      message.setProperty("_type", "position_report");
      message.setProperty("_action", "update");
      message.setProperty("_control_points", coords);
      message.setProperty("sic", sicCode);
      message.setProperty("_wkid", "3857");
      message.setProperty("uniquedesignation", selectedSymbol);

      msgProcessor.processMessage(message);
    }
  }

  /**
   * Performs the search: creates a query {@link SymbolDictionaryQuery}, adds the keywords and filters to it, then
   * passes it to the symbol dictionary to retrieve the symbol names. Updates the UI with the found symbol names.
   */
  private void doSearch() {
    // get the selected keywords
    List<String> selectedKeywords = new ArrayList<>();

    for (String selectedKeyword : lstKeywords.getSelectedValuesList()) {
      selectedKeywords.add(selectedKeyword);
    }
    
    // get the selected filters
    TreePath[] SelectedTreePath = treeFilters.getSelectionPaths();
    Map<String, List<String>> selectedFilters = new HashMap<>();

    // have we selected anything?
    if (SelectedTreePath != null) {
      for (TreePath selectedPath : SelectedTreePath) {
        // is it a value which is selected (not a filter category)?
        if (selectedPath.getPathCount() == 3) {
          String key = selectedPath.getPathComponent(1).toString();
          String keyVal = selectedPath.getLastPathComponent().toString();

          // add the filter...
          // does the key already exist?
          if (selectedFilters.containsKey(key) == false) {
            // add the key so we can add to the list of filters
            List<String> filterVals = new ArrayList<>();
            filterVals.add(keyVal);
            selectedFilters.put(key, filterVals);
          } else {
            // we are updating an existing entry
            List<String> filterVals = selectedFilters.get(key);
            filterVals.add(keyVal);
            selectedFilters.put(key, filterVals);
          }
        }
      }
    }

    // perform the query
    symbols = new ArrayList<>();

    try {
      symbols = symDictionary.findSymbols(selectedKeywords, selectedFilters);
    } catch (IOException e) {
      e.printStackTrace();
    }

    // get the symbol names and images for the list of results
    symbolNames = new ArrayList<>();
    symbolImages = new ArrayList<>();
    symbolTips = new ArrayList<>();
    for (SymbolProperties symbolProps : symbols) {
      symbolNames.add(symbolProps.getName());
      symbolImages.add(new ImageIcon(symbolProps.getImage(24, 24), symbolProps.getName()));
      symbolTips.add(symbolProps.getGeometryType() + ", " + symbolProps.getGeometryDescription());
    }
  }

  // ------------------------------------------------------------------------
  // Static methods
  // ------------------------------------------------------------------------
  /**
   * Starting point of this application: creates the window, creates an 
   * instance of this application and calls method to create the UI.
   * 
   * @param args any arguments
   */
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        try {
          // instance of this application
          SymbolDictionaryApp mpApp = new SymbolDictionaryApp();

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

  // ------------------------------------------------------------------------
  // Private methods
  // ------------------------------------------------------------------------
  /**
   * Creates and displays the UI, including the map, for this application. Creates 
   * the message processor used to add symbols to the map.
   */
  public JComponent createUI() throws Exception {

    // application content
    final JPanel applicationPanel = new JPanel();
    applicationPanel.setLayout(new BorderLayout(0, 0));

    // create user panel
    final JPanel symbolPanel = createSymbolPanel();

    // create map
    map = new JMap();

    // set up the dialog content
    setupSearchDialog();

    // create and add tiled layer and group layer to map
    ArcGISTiledMapServiceLayer tiledLayer = new ArcGISTiledMapServiceLayer(
        "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer");
    map.getLayers().add(tiledLayer);

    // set default extent to UK
    map.setExtent(new Envelope(-1406416, 6416535, 464994, 7664776));

    // set up a group layer and obtain the message processor
    MessageGroupLayer groupLyr = new MessageGroupLayer(SymbolDictionary.DictionaryType.Mil2525C, 1.0);
    map.getLayers().add(groupLyr);
    msgProcessor = groupLyr.getMessageProcessor();

    // add graphics layer for our drawn graphics
    graphicsLayer = new GraphicsLayer();
    map.getLayers().add(graphicsLayer);

    // create drawing overlay, add to map, and activate
    drawingOverlay = new DrawingOverlay();
    map.addMapOverlay(drawingOverlay);
    drawingOverlay.setActive(true);

    // add a listener to add the drawn graphic into our graphics layer
    drawingOverlay.addDrawingCompleteListener(new DrawingCompleteListener() {

      @Override
      public void drawingCompleted(DrawingCompleteEvent arg0) {
        // gets the military symbol onto the map and clears the graphic from the overlay
        addSymbolToMap(drawingOverlay.getAndClearFeature());
      }
    });

    JPanel mainPanel = new JPanel();
    mainPanel.setLayout(new BorderLayout());
    mainPanel.add(map, BorderLayout.CENTER);

    applicationPanel.add(symbolPanel, BorderLayout.WEST);
    applicationPanel.add(mainPanel, BorderLayout.CENTER);

    return applicationPanel;
  }

  /**
   * Creates the UI element with the description, keywords and 
   * filters, and search result box.
   * 
   * @return panel the symbol panel
   */
  private JPanel createSymbolPanel() {

    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    panel.setSize(300, 575);
    panel.setLocation(10, 10);
    panel.setBorder(new LineBorder(new Color(0, 0, 0, 60), 5, false));

    // Description
    JTextArea description = createDescription();
    panel.add(description);

    // Tabbed pane for keywords and filters
    JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP);
    tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
    tabbedPane.setMinimumSize(new Dimension(300, 250));
    panel.add(tabbedPane);

    // Keywords Panel
    JPanel panKeywords = new JPanel();

    tabbedPane.addTab("Keywords", null, panKeywords, null);
    panKeywords.setLayout(new BorderLayout(0, 0));

    JLabel lblKeywords = new JLabel("Keywords");
    panKeywords.add(lblKeywords, BorderLayout.NORTH);

    lstKeywords = new JList<>();
    Font listFont = new Font("Dialog", 1, 10);
    lstKeywords.setFont(listFont);

    JScrollPane scrKeywords = new JScrollPane(lstKeywords);
    panKeywords.add(scrKeywords, BorderLayout.CENTER);

    // Filters panel
    JPanel panFilters = new JPanel();
    tabbedPane.addTab("Filters", null, panFilters, null);
    panFilters.setLayout(new BorderLayout(0, 0));

    JLabel lblFilters = new JLabel("Filters");
    panFilters.add(lblFilters, BorderLayout.NORTH);

    treeFilters = new JTree();
    treeFilters.setFont(listFont);
    panFilters.add(treeFilters, BorderLayout.CENTER);

    JScrollPane scrFilters = new JScrollPane(treeFilters);
    panFilters.add(scrFilters, BorderLayout.CENTER);

    btnSearch = new JButton("Search");
    btnSearch.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        SearchWorker searchWorker = new SearchWorker();
        // doSearch then update the UI
        searchWorker.execute();
      }
    });
    panel.add(btnSearch);

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

    JPanel panResults = new JPanel();
    panel.add(panResults);

    panResults.setLayout(new BorderLayout(0, 0));

    lblResults = new JLabel("Results");
    panResults.add(lblResults, BorderLayout.NORTH);

    // list of symbol names
    lstResults = new JList<>();
    lstResults.setFont(listFont);
    lstResults.setFixedCellHeight(24);
    lstResults.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    lstResults.addListSelectionListener(new ListSelectionListener() {

      @Override
      public void valueChanged(ListSelectionEvent e) {
        if (lstResults.getSelectedIndex() < 0) {
          return;
        }
        // select the corresponding symbol image
        if (lstResultsImages.getSelectedIndex() != lstResults.getSelectedIndex()) {
          lstResultsImages.setSelectedIndex(lstResults.getSelectedIndex());
        }
        // select a corresponding tool
        setupDrawingOverlayForSymbol(symbols.get(lstResults.getSelectedIndex()));
      }
    });

    lstResults.addMouseMotionListener(new MouseMotionAdapter() {
      @Override
      public void mouseMoved(MouseEvent e) {
        int index = lstResults.locationToIndex(e.getPoint());
        if (index > -1 & index < symbolTips.size()) {
          lstResults.setToolTipText(symbolTips.get(index));
        }
      }
    });

    // list of symbol images
    lstResultsImages = new JList<>();
    lstResultsImages.setFixedCellHeight(24);
    lstResultsImages.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    lstResultsImages.addListSelectionListener(new ListSelectionListener() {

      @Override
      public void valueChanged(ListSelectionEvent e) {
        if (lstResultsImages.getSelectedIndex() < 0) {
          return;
        }
        // select the corresponding symbol name
        if (lstResults.getSelectedIndex() != lstResultsImages.getSelectedIndex()) {
          lstResults.setSelectedIndex(lstResultsImages.getSelectedIndex());
        }
        // select a corresponding tool
        setupDrawingOverlayForSymbol(symbols.get(lstResults.getSelectedIndex()));
      }
    });

    // images and symbol names in results panel
    JPanel lstResultsContainer = new JPanel();
    lstResultsContainer.setBackground(Color.WHITE);
    lstResultsContainer.add(lstResultsImages, BorderLayout.WEST);
    lstResultsContainer.add(lstResults, BorderLayout.CENTER);

    JScrollPane scrResults = new JScrollPane(lstResultsContainer);
    panResults.add(scrResults, BorderLayout.CENTER);
    return panel;
  }

  /**
   * Sets up the drawingOverlay mode from the geometry symbol property.
   * @param symbolProperties the symbol property to get the geometry type 
   */
  private void setupDrawingOverlayForSymbol(SymbolProperties symbolProperties) {
    if (symbolProperties.getGeometryType().contentEquals("Point Symbol")) {
      drawingOverlay.setUp(DrawingMode.POINT, 
          new SimpleMarkerSymbol(Color.BLACK, 10, Style.CIRCLE), null);
    }else if (symbolProperties.getGeometryType().contentEquals("Line Symbol")) {
      drawingOverlay.setUp(DrawingMode.POLYLINE, 
          new SimpleLineSymbol(Color.BLACK, 3), null);
    }else if (symbolProperties.getGeometryType().contentEquals("Polygon Symbol")) {
      drawingOverlay.setUp(DrawingMode.POLYGON, 
          new SimpleFillSymbol(Color.BLACK, new SimpleLineSymbol(Color.BLACK, 2)), null);
    }//else do nothing 
  }

  /**
   * Creates a description for this application.
   * 
   * @return description
   */
  private JTextArea createDescription() {
    JTextArea description = new JTextArea("Use the keywords and filters in the tabs below to search "
        + "for symbols. Keywords and filters work together to create the results. "
        + "Use the <Ctrl> key for multiple selections and to deselect items. "
        + "Select a symbol from the result list, the appropriate drawing tool is automatically selected "
        + "to create a geometry for the symbol.");
    description.setFont(new Font("Verdana", Font.PLAIN, 11));
    description.setForeground(Color.WHITE);
    description.setBackground(new Color(0, 0, 0, 180));
    description.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 5));
    description.setEditable(false);
    description.setWrapStyleWord(true);
    description.setLineWrap(true);
    return description;
  }

  /**
   * Creates a window.
   * 
   * @return a window.
   */
  private JFrame createWindow() {
    JFrame window = new JFrame("Symbol Dictionary 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;
  }

  /**
   * Creates a progress bar.
   * 
   * @return a progress bar.
   */
  private static JProgressBar createProgressBar() {
    final JProgressBar progressBar = new JProgressBar();
    progressBar.setIndeterminate(true);
    progressBar.setVisible(false);
    return progressBar;
  }

  // Get a string of the form x1,y1;x2,y2;x3y3; from the drawn geometry
  private String getControlPoints(Feature graphic) {
    String controlPoints = "";
    if (graphic.getGeometry().getType() == Type.POINT) {
      Point p = ((Point) graphic.getGeometry());
      controlPoints += p.getX() + "," + p.getY();
    } else if (graphic.getGeometry().getType() == Type.POLYLINE) {
      Polyline l = ((Polyline) graphic.getGeometry());
      for (int i = 0; i < l.getPointCount(); i++) {
        controlPoints += l.getPoint(i).getX() + "," + l.getPoint(i).getY() + ";";
      }
    } else if (graphic.getGeometry().getType() == Type.POLYGON) {
      Polygon poly = ((Polygon) graphic.getGeometry());
      for (int i = 0; i < poly.getPointCount(); i++) {
        controlPoints += poly.getPoint(i).getX() + "," + poly.getPoint(i).getY() + ";";
      }
    }
    return controlPoints;
  }

  class SearchWorker extends SwingWorker<Integer, Integer> {
    public SearchWorker() {
    }

    @Override
    protected Integer doInBackground() throws Exception {
      // disable any further searches till we are done
      SwingUtilities.invokeAndWait(new Runnable() {
        @Override
        public void run() {
          btnSearch.setEnabled(false);
          progressBar.setVisible(true);
        }
      });

      // search for the symbols and get their names and images
      doSearch();
      return null;
    }

    @Override
    protected void done() {
      // pop the results in the results list
      lstResults.setListData(symbolNames.toArray(new String[symbolNames.size()]));
      lstResultsImages.setListData(symbolImages.toArray(new ImageIcon[symbolImages.size()]));

      // update the label with the number of results
      lblResults.setText("Results (" + symbolNames.size() + " symbols found)");

      // enable searching
      progressBar.setVisible(false);
      btnSearch.setEnabled(true);
    }
  }
}
Feedback on this topic?