Rotate graphics

Download Sample Viewer

Description

Shows how to rotate graphics.

Code snippet


    Point geometry = new Point(13, 55.59);
    SimpleMarkerSymbol symbol = new SimpleMarkerSymbol(Color.BLUE, 24, Style.CROSS);
    // angle is absolute, positive for clockwise, negative for anti-clockwise
    symbol.setAngle(30);
    graphicsLayer.addGraphic(new Graphic(geometry, symbol));
  

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

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
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.JRadioButton;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import com.esri.toolkit.overlays.HitTestEvent;
import com.esri.toolkit.overlays.HitTestListener;
import com.esri.toolkit.overlays.HitTestOverlay;
import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.Geometry;
import com.esri.core.geometry.MultiPoint;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.Polygon;
import com.esri.core.geometry.Polyline;
import com.esri.core.geometry.Transformation2D;
import com.esri.core.map.Feature;
import com.esri.core.map.Graphic;
import com.esri.core.symbol.PictureMarkerSymbol;
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.Symbol;
import com.esri.core.symbol.TextSymbol;
import com.esri.core.symbol.TextSymbol.HorizontalAlignment;
import com.esri.core.symbol.TextSymbol.VerticalAlignment;
import com.esri.map.ArcGISTiledMapServiceLayer;
import com.esri.map.GraphicsLayer;
import com.esri.map.JMap;
import com.esri.map.LayerList;

/***
 * This sample shows how to rotate graphics in a mixed geometry graphics layer.
 */
public class RotateGraphics {

  private JMap jMap;
  private GraphicsLayer graphicsLayer;
  private static final int COMPONENT_WIDTH = 200;
  private int lineAngle = 30;

  // ------------------------------------------------------------------------
  // Constructor
  // ------------------------------------------------------------------------
  public RotateGraphics() {
  }

  // ------------------------------------------------------------------------
  // Core functionality
  // ------------------------------------------------------------------------
  private void rotateGraphics(boolean selectAll, boolean clockwise) {
    // get the graphics to rotate
    int[] graphicIds = selectAll ? graphicsLayer.getGraphicIDs() : graphicsLayer.getSelectionIDs();
    if (graphicIds == null) {
      return;
    }

    // set the angle on the graphics' symbol
    for (int i = 0; i < graphicIds.length; i++) {
      Graphic graphic = graphicsLayer.getGraphic(graphicIds[i]);
      Symbol symbol = graphic.getSymbol();
      if (symbol instanceof SimpleMarkerSymbol) {
        SimpleMarkerSymbol currSymbol = (SimpleMarkerSymbol) symbol;
        currSymbol.setAngle(clockwise ? currSymbol.getAngle() + 30 : currSymbol.getAngle() - 30);
      } else if (symbol instanceof PictureMarkerSymbol) {
        PictureMarkerSymbol currSymbol = (PictureMarkerSymbol) symbol;
        currSymbol.setAngle(clockwise ? currSymbol.getAngle() + 30 : currSymbol.getAngle() - 30);
      } else if (symbol instanceof TextSymbol) {
        TextSymbol currSymbol = (TextSymbol) symbol;
        currSymbol.setAngle(clockwise ? currSymbol.getAngle() + 30 : currSymbol.getAngle() - 30);
      } else if (symbol instanceof SimpleLineSymbol) {
        // lines are a special case.
        // apply rotate transformation to every point
        Geometry geometry = graphic.getGeometry();
        if (geometry instanceof Polyline) {
          Polyline currPolyline = (Polyline) graphic.getGeometry();
          Polyline newPolyline = new Polyline();
          boolean isNewPolyline = true;
          for (int j = 0; j < currPolyline.getPointCount(); j++) {
            Point p = currPolyline.getPoint(j);
            rotatePointOfLine(p, clockwise ? 30 : -30, jMap.getFullExtent().getCenter());
            if (isNewPolyline) {
              newPolyline.startPath(p);
              isNewPolyline = false;
            } else {
              newPolyline.lineTo(p);
            }
          }
          graphicsLayer.updateGraphic(graphicIds[i], newPolyline);
        }
      }

      // update graphics layer
      graphicsLayer.updateGraphic(graphicIds[i], symbol);
    }
  }

  private void createLine(Point fromPoint, float angleDeg) {
    // create a to-point relative center and the North
    double lineLength = jMap.getFullExtent().getHeight() * 0.10;
    Point toPoint = new Point(0, lineLength);

    // rotate the to-point relative to the North
    rotatePointOfLine(toPoint, angleDeg, jMap.getFullExtent().getCenter());

    // shift the to-point relative to the from-point
    Transformation2D transShift = new Transformation2D();
    transShift.setShift(fromPoint.getX(), fromPoint.getY());
    toPoint.applyTransformation(transShift);

    // create line using from and to points
    Polyline line = new Polyline();
    line.startPath(fromPoint);
    line.lineTo(toPoint);
    line.closeAllPaths();

    // add line as graphic
    graphicsLayer.addGraphic(new Graphic(line, new SimpleLineSymbol(Color.BLUE, 2)));
  }

  /**
   * Rotate a point relative to the North. Angle is measured clockwise, 0 being North,
   * 90 being East.
   * @param p point to rotate.
   * @param angleDeg angle relative to the North axis, in degrees.
   * @param rotationCenter center of rotation.
   */
  private void rotatePointOfLine(Point p, float angleDeg, Point rotationCenter) {
    // create rotate transformation
    double angleRad = Math.toRadians(-angleDeg);
    Transformation2D rotateTx = new Transformation2D();
    rotateTx.rotate(Math.cos(angleRad), Math.sin(angleRad), rotationCenter);

    // apply transformation on the point
    p.applyTransformation(rotateTx);
  }

  // ------------------------------------------------------------------------
  // Static methods
  // ------------------------------------------------------------------------
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        try {
          // instance of this application
          RotateGraphics app = new RotateGraphics();

          // 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();
        }
      }
    });
  }

  // ------------------------------------------------------------------------
  // Public methods
  // ------------------------------------------------------------------------
  public JComponent createUI() throws Exception {
    // application content
    JComponent contentPane = createContentPane();

    // selection color panel
    JPanel userPanel = createUserPanel();
    userPanel.setVisible(true);
    userPanel.setLocation(10, 10);

    // create map
    jMap = createMap();

    contentPane.add(userPanel);
    contentPane.add(jMap);

    return contentPane;
  }

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

    final JMap map = new JMap();

    // add the base layer
    ArcGISTiledMapServiceLayer worldLayer = new ArcGISTiledMapServiceLayer(
        "http://services.arcgisonline.com/ArcGIS/rest/services/NGS_Topo_US_2D/MapServer");

    map.setExtent(new Envelope(-140, -45, 50, 45));

    map.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent arg0) {
        if (arg0.getButton() != MouseEvent.BUTTON1) {
          Point clickedPoint = map.toMapPoint(arg0.getX(), arg0.getY());
          createLine(clickedPoint, lineAngle);
        }
      }
    });

    LayerList layers = map.getLayers();
    layers.add(worldLayer);

    graphicsLayer = new GraphicsLayer();
    layers.add(graphicsLayer);
    addGraphics(graphicsLayer);

    // create the overlay and the listener
    final HitTestOverlay overlay = new HitTestOverlay(graphicsLayer);
    overlay.addHitTestListener(new GraphicSelectedListener());

    // add the overlay to the jMap
    map.addMapOverlay(overlay);

    return map;
  }

  private void addGraphics(GraphicsLayer gLayer) {
    Point point = new Point(0, 0);
    gLayer.addGraphic(new Graphic(point, new SimpleMarkerSymbol(Color.MAGENTA, 25, Style.CROSS)));

    Polyline line = new Polyline();
    line.startPath(-180, 0);
    line.lineTo(180, 0);
    gLayer.addGraphic(new Graphic(line,
        new SimpleLineSymbol(Color.BLACK, 2, com.esri.core.symbol.SimpleLineSymbol.Style.DASH)));

    Polygon polygon = new Polygon();
    polygon.startPath(-80, 25);
    polygon.lineTo(-64, 32);
    polygon.lineTo(-66, 18);
    polygon.closeAllPaths();
    gLayer.addGraphic(new Graphic(polygon, 
        new SimpleFillSymbol(Color.RED, new SimpleLineSymbol(Color.WHITE, 1.0f))));

    MultiPoint mp = new MultiPoint();
    PictureMarkerSymbol planeSymbol = new PictureMarkerSymbol(
        "http://static.arcgis.com/images/Symbols/Transportation/Airplane.png");
    planeSymbol.setSize(50, 50);
    Point plane1 = new Point(-40, 25);
    mp.add(plane1);
    Point plane2 = new Point(-45, 35);
    mp.add(plane2);
    Point plane3 = new Point(-30, 35);
    mp.add(plane3);
    Graphic gPlanes = new Graphic(mp, planeSymbol);
    gLayer.addGraphic(gPlanes);

    TextSymbol textSymbol = new TextSymbol(14, "Equator", Color.BLACK);
    textSymbol.setHorizontalAlignment(HorizontalAlignment.RIGHT);
    textSymbol.setVerticalAlignment(VerticalAlignment.BOTTOM);
    textSymbol.setOffsetX(-30.0f);
    gLayer.addGraphic(new Graphic(point, textSymbol));
  }

  /**
   * Creates the user panel to change the selection color.
   */
  private JPanel createUserPanel() {
    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    panel.setSize(COMPONENT_WIDTH, 200);
    panel.setLocation(10, 10);

    JTextArea description = new JTextArea("Rotate all or selected graphics. " +
        "Fill symbols do not support rotation. Line rotation is a special case.");
    description.setFont(new Font("Verdana", Font.PLAIN, 11));
    description.setForeground(Color.WHITE);
    description.setBackground(Color.BLACK);
    description.setEditable(false);
    description.setLineWrap(true);
    description.setWrapStyleWord(true);
    description.setAlignmentX(Component.CENTER_ALIGNMENT);

    final JRadioButton btnRotateAll = new JRadioButton("All", true);
    btnRotateAll.setAlignmentX(Component.CENTER_ALIGNMENT);
    btnRotateAll.setFocusPainted(false);
    btnRotateAll.setForeground(Color.WHITE);
    btnRotateAll.setBackground(Color.BLACK);

    final JRadioButton btnRotateSelected = new JRadioButton("Selected", false);
    btnRotateSelected.setAlignmentX(Component.CENTER_ALIGNMENT);
    btnRotateSelected.setFocusPainted(false);
    btnRotateSelected.setForeground(Color.WHITE);
    btnRotateSelected.setBackground(Color.BLACK);

    ButtonGroup btnGroup = new ButtonGroup();
    btnGroup.add(btnRotateAll);
    btnGroup.add(btnRotateSelected);

    // put the radio buttons and text boxes in a panel.
    JPanel selectionPanel = new JPanel();
    selectionPanel.setLayout(new BoxLayout(selectionPanel, BoxLayout.X_AXIS));
    selectionPanel.setBackground(Color.BLACK);
    selectionPanel.add(btnRotateAll);
    selectionPanel.add(btnRotateSelected);
    selectionPanel.setAlignmentX(Component.CENTER_ALIGNMENT);

    // rotate buttons
    JButton btnRotateClockwise = new JButton("30\u02da\u21b7");
    btnRotateClockwise.setFocusPainted(false);
    btnRotateClockwise.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        try {
          rotateGraphics(btnRotateAll.isSelected(), true);
        } catch (Exception ex) {
          JOptionPane.showMessageDialog(jMap, wrap(ex.getMessage()));
        }
      }
    });

    JButton btnRotateAntiClockwise = new JButton("30\u02da\u21b6");
    btnRotateAntiClockwise.setFocusPainted(false);
    btnRotateAntiClockwise.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        try {
          rotateGraphics(btnRotateAll.isSelected(), false);
        } catch (Exception ex) {
          JOptionPane.showMessageDialog(jMap, wrap(ex.getMessage()));
        }
      }
    });

    // panel for rotate buttons
    final JPanel rotationPanel = new JPanel();
    rotationPanel.setLayout(new BoxLayout(rotationPanel, BoxLayout.X_AXIS));
    rotationPanel.add(btnRotateClockwise);
    rotationPanel.add(btnRotateAntiClockwise);
    rotationPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
    rotationPanel.setBackground(Color.BLACK);

    // option to set angle
    final JLabel lblAngle = new JLabel("<html>Angle for new lines <br> (right-click to create):</html>");
    lblAngle.setBackground(Color.BLACK);
    lblAngle.setForeground(Color.WHITE);
    lblAngle.setAlignmentX(Component.CENTER_ALIGNMENT);


    final JTextField txtAngle = new JTextField();
    txtAngle.setMaximumSize(new Dimension(40, 20));
    txtAngle.setBackground(Color.BLACK);
    txtAngle.setForeground(Color.WHITE);
    txtAngle.setText("" + lineAngle);
    txtAngle.addKeyListener(new KeyListener() {
      @Override
      public void keyTyped(KeyEvent e) {
      }

      @Override
      public void keyReleased(KeyEvent e) {
        try {
          if (!(txtAngle.getText().isEmpty() || txtAngle.getText().equals("-"))) {
            lineAngle = Integer.parseInt(txtAngle.getText());
          }
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }

      @Override
      public void keyPressed(KeyEvent e) {
      }
    });

    // layout all the components together into a panel
    panel.setBackground(Color.BLACK);
    panel.add(description);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(selectionPanel);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(rotationPanel);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(lblAngle);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(txtAngle);
    panel.setBorder(BorderFactory.createEmptyBorder(5,10,5,10));
    return panel;
  }

  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 window.
   * @return a window.
   */
  private JFrame createWindow() {
    JFrame window = new JFrame("Rotate Graphics 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);
        jMap.dispose();
      }
    });
    return window;
  }
  
  private String wrap(String str) {
    // create a HTML string that wraps text when longer
    return "<html><p style='width:200px;'>" + str + "</html>";
  }
}

class GraphicSelectedListener implements HitTestListener {

  @Override
  public void featureHit(HitTestEvent event) {
    // get hit features
    List<Feature> hitFeatures = event.getOverlay().getHitFeatures();
    GraphicsLayer gLayer = (GraphicsLayer) event.getOverlay().getLayer();
    for (Feature feature : hitFeatures) {
      int id = (int) feature.getId();
      if (gLayer.isGraphicSelected(id)) {
        // if graphic is selected in the layer, unselect it
        gLayer.unselect(id);
      } else {
        // otherwise select graphic in the layer
        gLayer.select(id);
      }
    }
  }
}

Feedback on this topic?