arcgissamples\cartography\CustomCircleCallout.java—ArcObjects 10.4 Help for Java | ArcGIS for Desktop
Circle callout
arcgissamples\cartography\CustomCircleCallout.java
/* Copyright 2015 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 at <your ArcGIS install location>/DeveloperKit10.4/userestrictions.txt.
* 
*/
package arcgissamples.cartography;

import java.io.IOException;

import com.esri.arcgis.display.ICallout;
import com.esri.arcgis.display.IColor;
import com.esri.arcgis.display.IDisplayName;
import com.esri.arcgis.display.IDisplayTransformation;
import com.esri.arcgis.display.IQueryGeometry;
import com.esri.arcgis.display.ITextBackground;
import com.esri.arcgis.display.ITextSymbol;
import com.esri.arcgis.display.RgbColor;
import com.esri.arcgis.display.SimpleFillSymbol;
import com.esri.arcgis.display.SimpleLineSymbol;
import com.esri.arcgis.geometry.CircularArc;
import com.esri.arcgis.geometry.Envelope;
import com.esri.arcgis.geometry.IEnvelope;
import com.esri.arcgis.geometry.IGeometry;
import com.esri.arcgis.geometry.IPoint;
import com.esri.arcgis.geometry.IPolygon;
import com.esri.arcgis.geometry.ITransformation;
import com.esri.arcgis.geometry.Point;
import com.esri.arcgis.geometry.Polygon;
import com.esri.arcgis.geometry.Polyline;
import com.esri.arcgis.interop.AutomationException;
import com.esri.arcgis.system.IClone;

/**
 * This sample shows how you can extend ArcObjects to build a new custom textbackground.
 * ArcMap ships with several textbackgrounds including: Balloon Callout, Line Callout,
 * Marker Text Background, and Simple Line Callout.
 * This new textbackground is a Circle Line Callout and it is very similar to
 * the Balloon callout except, like its name suggests, it is circle and for simplicity sake,
 * its leader line is just a straight line.
 * The class implements IClone, ICallout, ITextBackground, IQueryGeometry, IDisplayName interfaces.
 */

public class CustomCircleCallout implements
IClone,
ICallout,
ITextBackground,
IQueryGeometry,
IDisplayName {

  private static final long serialVersionUID = 1L;
  // These members are controlled by properties
  double size;
  IColor color;
  double leaderTolerance;
  Envelope textBox;
  IPoint anchorPoint;
  IGeometry geometry;
  SimpleFillSymbol fillSymbol;
  IPoint textBoxCenterPt;
  SimpleLineSymbol leaderSymbol;

  /**
   * Default constructor.
   */
  CustomCircleCallout() {
    try {
      this.fillSymbol = new SimpleFillSymbol();
      this.leaderSymbol = new SimpleLineSymbol();
      this.size = 20; // default size
      // default color
      RgbColor rgbColor = new RgbColor();
      rgbColor.setRed(255);
      rgbColor.setGreen(255);
      this.color = rgbColor;
      this.fillSymbol.setColor(rgbColor);
    } catch( IOException e) {
      // never happened
    }
  }

  // IClone interface

  /**
   * @see IClone#esri_clone
   * @return IClone
   */
  public IClone esri_clone() {
    CustomCircleCallout textCallout = new CustomCircleCallout();
    textCallout.setSize(this.size);
    textCallout.setColor(this.color);
    textCallout.setAnchorPoint(this.anchorPoint);
    textCallout.setLeaderTolerance(this.leaderTolerance);
    return textCallout;
  }

  /**
   * @see IClone#assign
   * @param clone
   */
  public void assign(IClone clone) {
    // no implement
  }

  /**
   * @see IClone#isEqual
   * @param clone
   * @return boolean
   */
  public boolean isEqual(IClone clone) {
    return false;
  }

  /**
   * @see IClone#isIdentical
   * @param clone
   * @return boolean
   */
  public boolean isIdentical(IClone clone) {
    return false;
  }

  // ICallout interface

  /**
   * @see ICallout#getAnchorPoint
   * @return boolean
   */
  public IPoint getAnchorPoint() {
    return this.anchorPoint;
  }

  /**
   * @see ICallout#setAnchorPoint
   * @param point
   */
  public void setAnchorPoint(IPoint point) {
    this.anchorPoint = point;
  }

  /**
   * @see ICallout#getLeaderTolerance
   * @return double
   */
  public double getLeaderTolerance() {
    return this.leaderTolerance;
  }

  /**
   * @see ICallout#setLeaderTolerance
   * @param v
   */
  public void setLeaderTolerance(double v) {
    this.leaderTolerance = v;
  }

  // ITextBackground interface

  /**
   * @see ITextBackground#getTextSymbol
   * @return ITextSymbol
   */
  public ITextSymbol getTextSymbol() {
    // no implement
    return null;
  }

  /**
   * @see ITextBackground#setTextSymbolByRef
   * @param textSymbol
   */
  public void setTextSymbolByRef(ITextSymbol textSymbol) {
    // no implement
  }

  /**
   * @see ITextBackground#setTextBoxByRef
   * @param envelope
   * @throws IOException
   * @throws AutomationException
   */
  @SuppressWarnings("deprecation")
  public void setTextBoxByRef(IEnvelope envelope) throws IOException, AutomationException {
    this.textBox = (Envelope) envelope;
    this.textBoxCenterPt = new Point();
    this.textBoxCenterPt.setX((this.textBox.getXMin() + this.textBox.getXMax()) / 2);
    this.textBoxCenterPt.setY((this.textBox.getYMin() + this.textBox.getYMax()) / 2);
  }

  /**
   * @see ITextBackground#queryBoundary
   * @param hDC
   * @param transformation
   * @param boundary
   * @throws IOException
   * @throws AutomationException
   */
  public void queryBoundary(int hDC, ITransformation transformation, IPolygon boundary) throws IOException, AutomationException {
    //Forward the call down do the symbol
    //This will populate Boundary with a polygon based on the circle's envelope
    IDisplayTransformation displayTransformation = (IDisplayTransformation)(transformation);
    this.fillSymbol.queryBoundary(hDC, transformation, CreateGeometry(this.textBoxCenterPt, displayTransformation).getEnvelope(), boundary);
    Polygon polygon = (Polygon) boundary;
    polygon.simplify(); //Make sure it's simple

    //Create a polygon buffer around the leader line
    Polyline polyline = CreateLeader();
    double dBufferSize = displayTransformation.fromPoints(1);
    Polygon bufferedLeader = (Polygon) polyline.buffer(dBufferSize);
    bufferedLeader.simplify();

    //Union the buffered leader with the circle geometry
    //to create the final shape that needs refreshing
    bufferedLeader = (Polygon) polygon.union(bufferedLeader);

    //Don't want to pass back a different Boundary reference
    //Set our new geometry into the passed in Boundary reference - performance!
    IClone clone = (polygon);
    IClone bufLead = (bufferedLeader);
    clone.assign(bufLead);
  }

  /**
   * @see ITextBackground#draw
   * @param hDC
   * @param transformation
   * @throws IOException
   * @throws AutomationException
   */
  public void draw(int hDC, ITransformation transformation) throws IOException, AutomationException {
    //Draw the leader
    Polyline line = CreateLeader();
    IDisplayTransformation displayTransform = (IDisplayTransformation)(transformation);
    //Only draw the leader if it is longer than the tolerance
    if (line != null) {
      if (line.getLength() > displayTransform.fromPoints(this.leaderTolerance)) {
        this.leaderSymbol.setupDC(hDC, transformation);
        this.leaderSymbol.draw(line);
        this.leaderSymbol.resetDC();
      }
    }
    IDisplayTransformation displayTransformation = (IDisplayTransformation)(transformation);
    Polygon polygon = CreateGeometry(this.textBoxCenterPt, displayTransformation);
    this.geometry = polygon;
    //Draw the circle
    if (this.geometry == null)
      return;
    this.fillSymbol.setColor(this.color); //use the color property
    this.fillSymbol.setupDC(hDC, transformation);
    this.fillSymbol.draw(this.geometry);
    this.fillSymbol.resetDC();
  }

  // IQueryGeometry interface

  /**
   * @see IQueryGeometry#getGeometry
   * @param i
   * @param transformation
   * @param geom
   * @throws IOException
   * @throws AutomationException
   * @return IGeometry
   */
  public IGeometry getGeometry( int i, ITransformation transformation, IGeometry geom) throws IOException, AutomationException {
    return CreateLeader();
  }

  /**
   * @see IQueryGeometry#queryEnvelope
   * @param i
   * @param transformation
   * @param geom
   * @param envelope
   */
  @SuppressWarnings("deprecation")
  public void queryEnvelope(int i, ITransformation transformation, IGeometry geom, IEnvelope envelope) {
    // no implement
  }

  // IDisplayName interface

  /**
   * @see IDisplayName#getNameString
   * @return String
   */
  public String getNameString() {
    return "Circle Line Callout";
  }

  // own public methods

  /**
   * Set symbool size.
   * @param sz double
   */
  public void setSize(double sz) {
    this.size = sz;
  }

  /**
   * Return simbol size.
   * @return double
   */
  public double getSize() {
    return this.size;
  }

  /**
   * Set symbol color.
   * @param clr is IColor object
   */
  public void setColor(IColor clr) {
    this.color = clr;
  }

  /**
   * Return symbol color.
   * @return IColor object
   */
  public IColor getColor() {
    return this.color;
  }

  // private methods

  private Polygon CreateGeometry(IPoint point, IDisplayTransformation displayTransformation) throws IOException, AutomationException {
    CircularArc circularArc = new CircularArc();
    //Access radius from property
    circularArc.constructCircle(point, displayTransformation.fromPoints(this.size), true);

    Polygon polygon = new Polygon();
    polygon.addSegment(circularArc,null,null);
    polygon.simplify();
    return polygon;
  }

  private Polyline CreateLeader() throws IOException, AutomationException {
    if (this.anchorPoint == null || this.textBoxCenterPt == null) {
      return null;
    }
    Polyline polyline = new Polyline();
    polyline.addPoint(this.anchorPoint, null, null);
    polyline.addPoint(this.textBoxCenterPt, null, null);
    return polyline;
  }
}