MOLE basic symbols
arcgissamples\defensesolutions\MoleBasicSymbols.java
/* Copyright 2010 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 arcgissamples.defensesolutions;


import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

import com.esri.arcgis.carto.*;
import com.esri.arcgis.controls.*;
import com.esri.arcgis.defensesolutions.*;
import com.esri.arcgis.display.*;
import com.esri.arcgis.geometry.*;
import com.esri.arcgis.system.*;

import static com.esri.arcgis.carto.esriViewDrawPhase.*;


/** This class is the "controller" part of the model-view-controller.
 *  To the greatest extent possible, MapTocToolbar only has code generator output.
 *  This file is reserved for code written by humans. */
public class MoleBasicSymbols extends IMapControlEvents2Adapter{
  private static final long serialVersionUID       = -1;
  private static Random     s_random               = new Random();
  private IPoint            m_currentMouseLocation = createPoint (0, 0);
  private long              m_dragStartTime        = 0;
  private boolean           m_firstTime            = true;
  private IPoint            m_lastMouseClick       = createPoint (0, 0);
  private MapTocToolbar     m_mapWindow            = new MapTocToolbar();
  private IDisplayFeedback  m_moveFeedback         = null;
  private Envelope          m_selectedBounds       = null;
  private int               m_unitCount            = 0;


  private MoleBasicSymbols()
  {
    try
    {
      // some additional configuration
      m_mapWindow.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
      updateTitle();
      m_mapWindow.getMapBean().addIMapControlEvents2Listener (this);
      Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
      m_mapWindow.setLocation (
          (screenSize.width - m_mapWindow.getWidth()) >> 1,
          (screenSize.height - m_mapWindow.getHeight()) >> 1
      );
      m_mapWindow.getTxtSIC().setText (DemoSymbols.getSIC(s_random.nextInt()));
      m_selectedBounds = new Envelope();

      // connect ActionListeners to the Swing toolbar
      m_mapWindow.getBtnAdd200().addActionListener (new ActionListener() {
        public void actionPerformed (ActionEvent e) {
          MoleBasicSymbols.this.actionAdd200();
        }
      });
      m_mapWindow.getBtnAddArea().addActionListener (new ActionListener() {
        public void actionPerformed (ActionEvent e) {
          MoleBasicSymbols.this.actionAddArea();
        }
      });
      m_mapWindow.getBtnAddLine().addActionListener (new ActionListener() {
        public void actionPerformed (ActionEvent e) {
          MoleBasicSymbols.this.actionAddLine();
        }
      });
      m_mapWindow.getBtnAddMoleSymbol().addActionListener (new ActionListener() {
        public void actionPerformed (ActionEvent e) {
          MoleBasicSymbols.this.actionAddMoleSymbol();
        }
      });
      m_mapWindow.getBtnMoveUnits().addActionListener (new ActionListener() {
        public void actionPerformed (ActionEvent e) {
          MoleBasicSymbols.this.actionMoveUnits();
        }
      });
      m_mapWindow.getBtnSelect().addActionListener (new ActionListener() {
        public void actionPerformed (ActionEvent e) {
          MoleBasicSymbols.this.actionSelect();
        }
      });
      m_mapWindow.addWindowListener (new WindowAdapter() {
        public void windowClosing (WindowEvent e) {
          try
          {
            // tell MOLE to save its settings and release its resources
            System.out.println ("releasing MOLE resources");
            (new MoleCoreHelper()).releaseForceElementRenderer();
          }
          catch ( Exception x ) { x.printStackTrace(); }
        }
      });
      // show the completed user interface
      m_mapWindow.setVisible (true);
    }
    catch ( Exception e ) { e.printStackTrace(); }
  }


  public void onMapReplaced (IMapControlEvents2OnMapReplacedEvent e)
  {
    try
    {
      // initialize the last mouse click point to the center of the map's extent
      Envelope extent = (Envelope) m_mapWindow.getMapBean().getActiveView().getExtent();
      m_lastMouseClick.putCoords (
          extent.getXMin() + extent.getWidth() * 0.5,
          extent.getYMin() + extent.getHeight() * 0.5
      );
      // update the window title
      m_unitCount = 0;
      updateTitle();
    }
    catch ( Exception x ) { x.printStackTrace(); }
  }


  public void onMouseDown (IMapControlEvents2OnMouseDownEvent e)
  {
    try
    {
      m_lastMouseClick.putCoords (e.getMapX(), e.getMapY());
      if ( m_mapWindow.getBtnAddMoleSymbol().isSelected() )
      {
        // "Add MOLE Symbol" command:  draw a symbol at the click point
        drawSymbol (m_lastMouseClick, m_mapWindow.getTxtSIC().getText(), false);
        m_mapWindow.getTxtSIC().setText (DemoSymbols.getSIC(s_random.nextInt()));
      }
      else if (
          m_mapWindow.getBtnSelect().isSelected() &&
          selectElements(m_lastMouseClick, m_mapWindow.getMapBean().getActiveView(), m_selectedBounds)
      )
      {
        // "Select & Drag Graphics" command:  initialize mouse tracking to move the selected elements
        System.out.println ("start tracking at (" + m_lastMouseClick.getX() + ", " + m_lastMouseClick.getY() + ")");

        // the envelope feedback draws a rectangle of the selected elements' extent following the mouse
        IMoveEnvelopeFeedback moveEnvelopeFeedback = new MoveEnvelopeFeedback();
        moveEnvelopeFeedback.start (m_selectedBounds, m_lastMouseClick);
        m_moveFeedback = (IDisplayFeedback) moveEnvelopeFeedback;
        m_moveFeedback.setDisplayByRef (m_mapWindow.getMapBean().getActiveView().getScreenDisplay());

        // the tick count is used to filter out short, unintentional mouse drags
        m_dragStartTime = System.currentTimeMillis();
      }
    }
    catch ( Exception x ) { x.printStackTrace(); }
  }


  public void onMouseMove (IMapControlEvents2OnMouseMoveEvent e)
  {
    try
    {
      // update the current map location of the mouse
      m_currentMouseLocation.putCoords (e.getMapX(), e.getMapY());

      // "Select & Drag Graphics" command:  move the feedback graphic
      if ( m_mapWindow.getBtnSelect().isSelected() && e.getButton() == 1 && m_moveFeedback != null )
        m_moveFeedback.moveTo (m_currentMouseLocation);
    }
    catch ( Exception x ) { x.printStackTrace(); }
  }


  public void onMouseUp (IMapControlEvents2OnMouseUpEvent e)
  {
    try
    {
      if ( m_moveFeedback != null )
      {
        // stop the feedback graphic and save its geometry for future use
        m_selectedBounds = (Envelope) ((IMoveEnvelopeFeedback)m_moveFeedback).stop();
        m_moveFeedback = null;
        long endTicks = System.currentTimeMillis() - m_dragStartTime;
        if ( endTicks > 250 )
        {
          // only update the graphics if the minimum move time has elapsed
          System.out.println ("drag start = (" + m_lastMouseClick.getX() + ", " + m_lastMouseClick.getY() + ")");
          System.out.println ("drag end = (" + m_currentMouseLocation.getX() + ", " + m_currentMouseLocation.getY() + ")");
          moveGraphics (
              m_currentMouseLocation.getX() - m_lastMouseClick.getX(),
              m_currentMouseLocation.getY() - m_lastMouseClick.getY()
          );
        }
      }
    }
    catch ( Exception x ) { x.printStackTrace(); }
  }


  private void actionAdd200()
  {
    try
    {
      // this one takes a little while, especially when it's the first one chosen
      Cursor previousCursor = m_mapWindow.getCursor();
      m_mapWindow.setCursor (Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

      // create concentric rings of units centered around where the user last clicked
      double centerLon = m_lastMouseClick.getX();
      double centerLat = m_lastMouseClick.getY();
      final double circleRadiusInRad = 1.0;
      final int numberPerCircle = 10;
      for ( int i = 0; i < 200; ++i )
      {
        // draw a random symbol at the next position in the pattern
        double currentRadius = (i / numberPerCircle) * circleRadiusInRad + circleRadiusInRad;
        double currentAngle = (i % numberPerCircle) *  2.0 * 3.1415926536 / (double)numberPerCircle;
        drawSymbol (
            createPoint(
                centerLon + (currentRadius * Math.sin(currentAngle)),
                centerLat + (currentRadius * Math.cos(currentAngle))
            ),
            DemoSymbols.getSIC(s_random.nextInt()),
            true
        );
      }
      //refresh the display and restore the cursor
      m_mapWindow.getMapBean().getActiveView().partialRefresh (esriViewGraphics, null, null);
      m_mapWindow.setCursor (previousCursor);
    }
    catch ( Exception e ) { e.printStackTrace(); }
  }


  private void actionAddArea()
  {
    try
    {
      // create the symbol using a default symbol ID code (obstacle restricted area)
      IMoleSymbol moleSymbol = new MoleFillSymbol();
      moleSymbol.setSymbolID ("GUMPOGR-------X");
      moleSymbol.setTextLabels (getLabels());
      moleSymbol.setTextSize (3.0);

      // override the default fill color and outline symbol - these settings are optional
      //ILineSymbol lineSymbol = new SimpleLineSymbol();
      //lineSymbol.setColor (getRandomColor());
      //lineSymbol.setWidth (dRandom(1, 5));
      //IFillSymbol fillSymbol = (IFillSymbol) moleSymbol;
      //fillSymbol.setOutline (lineSymbol);
      //fillSymbol.setColor (getRandomColor());

      // create a new polygon geometry for this symbol (four points in this example)
      IPointCollection pointCollection = new com.esri.arcgis.geometry.Polygon();

      // center the polygon somewhere inside the current map extent
      IActiveView activeView = m_mapWindow.getMapBean().getActiveView();
      Envelope extent = (Envelope) activeView.getExtent();
      double lat = dRandom (extent.getYMin(), extent.getYMax());
      double lon = dRandom (extent.getXMin(), extent.getXMax());

      // place the four corners somewhere within a specified threshold of the center
      final double threshold = 20;
      pointCollection.addPoint (createPoint(lon, dRandom(lat, lat + threshold)), null, null);
      pointCollection.addPoint (createPoint(dRandom(lon, lon + threshold), lat), null, null);
      pointCollection.addPoint (createPoint(lon, dRandom(lat - threshold, lat)), null, null);
      pointCollection.addPoint (createPoint(dRandom(lon - threshold, lon), lat), null, null);

      // set up the graphic element with the random geometry
      IFillShapeElement fillShapeElement = new PolygonElement();
      fillShapeElement.setSymbol ((IFillSymbol)moleSymbol);
      IElement element = (IElement) fillShapeElement;
      element.setGeometry ((IGeometry)pointCollection);

      // add the new element to the map and update the user interface
      activeView.getGraphicsContainer().addElement (element, 0);
      activeView.partialRefresh (esriViewGraphics, null, null);
      ++m_unitCount;
      updateTitle();
    }
    catch ( Exception e ) { e.printStackTrace(); }
  }


  private void actionAddLine()
  {
    try
    {
      // create the symbol using a default symbol ID code (fix task line)
      IMoleSymbol moleSymbol = new MoleLineSymbol();
      moleSymbol.setSymbolID ("GUTPF---------X");
      moleSymbol.setTextLabels (getLabels());
      moleSymbol.setTextSize (2.0);

      // override the default line color and width - these settings are optional
      //ILineSymbol lineSymbol = (ILineSymbol) moleSymbol;
      //lineSymbol.setColor (getRandomColor());
      //lineSymbol.setWidth (dRandom(1, 5));

      // create a new line geometry for the symbol - this symbol requires two points
      IPointCollection pointCollection = new Polyline();

      // place the first endpoint of the line somewhere inside the current map extent
      IActiveView activeView = m_mapWindow.getMapBean().getActiveView();
      Envelope ext = (Envelope) activeView.getExtent();
      double lat = dRandom (ext.getYMin(), ext.getYMax());
      double lon = dRandom (ext.getXMin(), ext.getXMax());
      pointCollection.addPoint (createPoint(lon, lat), null, null);

      // place the second endpoint somewhere within a specified threshold of the first
      final double threshold = 20;
      pointCollection.addPoint (
          createPoint(
              dRandom(lon - threshold, lon + threshold),
              dRandom(lat - threshold, lat + threshold)
          ),
          null,
          null
      );
      // set up the graphic element with the random geometry
      ILineElement lineElement = new LineElement();
      lineElement.setSymbol ((ILineSymbol)moleSymbol);
      IElement element = (IElement) lineElement;
      element.setGeometry ((IGeometry)pointCollection);

      // add the new element to the map and update the user interface
      activeView.getGraphicsContainer().addElement (element, 0);
      activeView.partialRefresh (esriViewGraphics, null, null);
      ++m_unitCount;
      updateTitle();
    }
    catch ( Exception e ) { e.printStackTrace(); }
  }


  private void actionAddMoleSymbol()
  {
    try
    {
      // make this button exclusive both in the Swing toolbar and in the Toolbar bean
      // the primary logic is in onMouseDown
      boolean selected = m_mapWindow.getBtnAddMoleSymbol().isSelected();
      System.out.println ("add MOLE symbol " + (selected ? "selected" : "deselected"));
      if ( selected )
      {
        m_mapWindow.getToolbarBean().setBuddyControl (null);
        m_mapWindow.getToolbarBean().esri_setEnabled (false);
        m_mapWindow.getBtnSelect().setSelected (false);
      }
      else
      {
        m_mapWindow.getToolbarBean().setBuddyControl (m_mapWindow.getMapBean());
        m_mapWindow.getToolbarBean().esri_setEnabled (true);
      }
    }
    catch ( Exception e ) { e.printStackTrace(); }
  }


  private void actionMoveUnits()
  {
    try
    {
      // MoveGraphics only applies to units in the selection - this will erase any previous selection
      IActiveView activeView = m_mapWindow.getMapBean().getActiveView();
      IGraphicsContainerSelect graphicsContainerSelect = (IGraphicsContainerSelect) activeView.getGraphicsContainer();
      graphicsContainerSelect.selectAllElements();
      moveGraphics (0.75, 0.75);
      graphicsContainerSelect.unselectAllElements();
      activeView.partialRefresh (esriViewGraphics, null, null);
      m_selectedBounds.setEmpty();
    }
    catch ( Exception e ) { e.printStackTrace(); }
  }


  private void actionSelect()
  {
    try
    {
      // make this button exclusive both in the Swing toolbar and in the Toolbar bean
      // the primary logic is in onMouseDown and onMouseMove
      boolean selected = m_mapWindow.getBtnSelect().isSelected();
      System.out.println ("select " + (selected ? "selected" : "deselected"));
      if ( selected )
      {
        m_mapWindow.getToolbarBean().setBuddyControl (null);
        m_mapWindow.getToolbarBean().esri_setEnabled (false);
        m_mapWindow.getBtnAddMoleSymbol().setSelected (false);
      }
      else
      {
        m_mapWindow.getToolbarBean().setBuddyControl (m_mapWindow.getMapBean());
        m_mapWindow.getToolbarBean().esri_setEnabled (true);
      }
    }
    catch ( Exception e ) { e.printStackTrace(); }
  }


  private void drawSymbol (IPoint location, String sic, boolean suppressRefresh)
  {
    try
    {
      // the first time we create a symbol, display the wait cursor while MOLE loads up
      Cursor previousCursor = m_mapWindow.getCursor();
      if ( m_firstTime )
        m_mapWindow.setCursor (Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

      // set up a MOLE symbol for the new graphic; minimally validate the symbol ID code
      IMoleSymbol moleSymbol = new MoleMarkerSymbol();
      if ( sic.length() == 15 )
        moleSymbol.setSymbolID (sic);
      moleSymbol.setTextLabels (getLabels());

      // to remove the symbol's fill, uncomment this code
      //IMoleMarkerSymbol moleMarkerSymbol = (IMoleMarkerSymbol) moleSymbol;
      //moleMarkerSymbol.setShowFill (false);

      // initialize the marker symbol size property
      double size = 36;
      try { size = Double.valueOf (m_mapWindow.getTxtSize().getText()); }
      catch ( Exception e ) {}
      IMarkerSymbol markerSymbol = (IMarkerSymbol) moleSymbol;
      markerSymbol.setSize (size);

      // create the graphic element for the marker symbol and add it to the map
      IMarkerElement markerElement = new MarkerElement();
      markerElement.setSymbol (markerSymbol);
      IElement element = (IElement) markerElement;
      element.setGeometry ((IGeometry)location);
      IActiveView activeView = m_mapWindow.getMapBean().getActiveView();
      activeView.getGraphicsContainer().addElement (element, 0);
      if ( ! suppressRefresh )
        activeView.partialRefresh (esriViewGraphics, null, null);

      // update the user interface
      if ( m_firstTime )
      {
        m_mapWindow.setCursor (previousCursor);
        m_firstTime = false;
      }
      ++m_unitCount;
      updateTitle();
    }
    catch ( Exception e ) { e.printStackTrace(); }
  }


  private void moveGraphics (double deltaX, double deltaY)
  {
    try
    {
      // move all selected graphics along a delta (change) vector
      System.out.println ("moving delta = (" + deltaX + ", " + deltaY + ")");

      // get reference to graphics container and its selected elements
      IActiveView              activeView              = m_mapWindow.getMapBean().getActiveView();
      IGraphicsContainer       graphicsContainer       = activeView.getGraphicsContainer();
      IGraphicsContainerSelect graphicsContainerSelect = (IGraphicsContainerSelect) graphicsContainer;
      IEnumElement             enumElement             = graphicsContainerSelect.getSelectedElements();

      // iterate through the selected elements
      enumElement.reset();
      IElement element = enumElement.next();
      while (element != null)
      {
        // apply the delta vector to each element's geometry and update it the container
        IGeometry geometry = element.getGeometry();
        ((ITransform2D)geometry).move (deltaX, deltaY);
        element.setGeometry (geometry);
        graphicsContainer.updateElement (element);
        element = enumElement.next();
      }
      // refresh the active view
      activeView.partialRefresh (esriViewGraphics, null, null);
    }
    catch ( Exception e ) { e.printStackTrace(); }
  }


  private void updateTitle()
  {
    // put the number of symbols in the title bar, when there are any
    String title = "MOLE Symbols";
    if ( m_unitCount == 1 )
      title += " (" + m_unitCount + " unit)";
    else if ( m_unitCount > 1 )
      title += " (" + m_unitCount + " units)";
    m_mapWindow.setTitle (title);
  }


  private static IPoint createPoint (double x, double y)
  {
    IPoint point = null;
    try
    {
      // create a new point instance and initialize its coordinates
      point = new com.esri.arcgis.geometry.Point();
      point.putCoords (x, y);
    }
    catch ( Exception e ) { e.printStackTrace(); }
    return point;
  }


  private double dRandom (double low, double high)
  {
    // generate a random floating-point number within the indicated range [low, high)
    return low + s_random.nextDouble() * (high - low);
  }


  private static IPropertySet getLabels()
  {
    IPropertySet labelSet = null;
    try
    {
      labelSet = new PropertySet();

      // all of the below are supported - comment and uncomment to experiment
      labelSet.setProperty ("Name", "Name");
      labelSet.setProperty ("Comment", "Comment");
      //labelSet.setProperty ("Parent", "Parent");
      //labelSet.setProperty ("Info", "Info");
      //labelSet.setProperty ("Strength", "Strength");
      //labelSet.setProperty ("EvalRating", "EvalRating");
      //labelSet.setProperty ("Location", "Location");
      //labelSet.setProperty ("Alt_Depth", "Alt_Depth");
      //labelSet.setProperty ("Speed", "Speed");
      //labelSet.setProperty ("DTG", "DTG");
      //labelSet.setProperty ("HQ", "HQ");
      //labelSet.setProperty ("Quantity", "Quantity");
      //labelSet.setProperty ("EType", "EType");
      //labelSet.setProperty ("Effective", "Effective");
      //labelSet.setProperty ("Signature", "Signature");
      //labelSet.setProperty ("IFFSIF", "IFFSIF");
    }
    catch ( Exception e ) { e.printStackTrace(); }
    return labelSet;
  }

  private static boolean selectElements (IPoint point, IActiveView activeView, Envelope selectedBounds)
  {
    try
    {
      // this function is written in such a way that it should be pastable
      System.out.println ("selecting graphics near (" + point.getX() + ", " + point.getY() + ")");

      IGraphicsContainer       graphicsContainer       = activeView.getGraphicsContainer();
      IGraphicsContainerSelect graphicsContainerSelect = (IGraphicsContainerSelect) graphicsContainer;
      IScreenDisplay           screenDisplay           = activeView.getScreenDisplay();
      boolean                  selected                = false;
      boolean                  refreshRequired         = false;

      // start with a precise search, and then widen the tolerance if nothing is found
      // (you may need to change these tolerances if using this code in your own application)
      IEnumElement enumElement = graphicsContainer.locateElements (point, 0.0000001);
      if ( enumElement == null )
        enumElement = graphicsContainer.locateElements (point, 0.5);

      // if no elements were selected
      if ( enumElement == null )
      {
        // if the previous selection is nonempty
        if ( graphicsContainerSelect.getElementSelectionCount() > 0 )
        {
          // clear the selection and refresh the display
          System.out.println ("clearing selection");
          graphicsContainerSelect.unselectAllElements();
          selectedBounds.setEmpty();
          refreshRequired = true;
        }
        // else do nothing
      }
      else
      {
        // get the extent of the selected elements
        Envelope envelope = new Envelope();
        enumElement.reset();
        IElement element = enumElement.next();
        while ( element != null )
        {
          // establish selectedBounds as the extent of all selected elements
          element.queryBounds (screenDisplay, envelope);
          selectedBounds.union (envelope);
          element = enumElement.next();
        }
        // add all the newly selected elements to the graphics container's selection
        enumElement.reset();
        graphicsContainerSelect.selectElements (enumElement);
        refreshRequired = selected = true;
      }
      // refresh the display if anything has changed
      if ( refreshRequired )
        activeView.partialRefresh (esriViewGraphics, null, null);

      // return true if any elements on the display are currently selected; selectedBounds has their extent
      return selected;
    }
    catch ( Exception e ) { e.printStackTrace(); }
    return false;
  }


  public static void main (String[] args)
  {
    EngineInitializer.initializeVisualBeans();
    ToolTipManager.sharedInstance().setLightWeightPopupEnabled (false);
    new MoleBasicSymbols();
  }
}