arcgissamples\cartography\FilledLeaderCallout.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.cartography; import java.io.IOException; import com.esri.arcgis.display.DisplayTransformation; import com.esri.arcgis.display.ICallout; import com.esri.arcgis.display.IDisplayName; import com.esri.arcgis.display.IDisplayTransformation; import com.esri.arcgis.display.IFillSymbol; import com.esri.arcgis.display.IQueryGeometry; import com.esri.arcgis.display.ISymbol; import com.esri.arcgis.display.ITextBackground; import com.esri.arcgis.display.ITextBackground2; 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.display.esriSimpleFillStyle; 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.ISegmentCollection; import com.esri.arcgis.geometry.ITransformation; import com.esri.arcgis.geometry.Line; import com.esri.arcgis.geometry.Point; import com.esri.arcgis.geometry.Polygon; 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 Filled Leader Callout and it is very similar to the Balloon * callout except, like its name suggests, the leader can be filled with a symbol * that differs from the balloon. */ public class FilledLeaderCallout implements IClone, ICallout, ITextBackground, IQueryGeometry, IDisplayName { private static final long serialVersionUID = 1L; double right; double left; double top; double bottom; IFillSymbol leaderSym; IFillSymbol backSym; double leaderTolerance; Envelope textBox; IPoint anchorPoint; IGeometry geometry; Envelope callOutBox; Polygon callOutPoly; IPolygon toundRect; double ldrDist; /** * Default Constructor */ FilledLeaderCallout() { try { this.top = 5; this.bottom = 5; this.left = 5; this.right = 5; this.leaderTolerance = 15; // SimpleLineSymbol outLine = new SimpleLineSymbol(); outLine.setWidth(0.4); // default Leader Symbol SimpleFillSymbol leaderSymbol = new SimpleFillSymbol(); RgbColor leaderColor = new RgbColor(); leaderColor.setRGB(0x000000FF); leaderSymbol.setColor(leaderColor); leaderSymbol.setStyle(esriSimpleFillStyle.esriSFSSolid); leaderSymbol.setOutline(outLine); this.backSym = leaderSymbol; // default Back Symbol SimpleFillSymbol backSymbol = new SimpleFillSymbol(); RgbColor backColor = new RgbColor(); backColor.setRGB(0x00FF0000); backSymbol.setColor(backColor); backSymbol.setStyle(esriSimpleFillStyle.esriSFSSolid); backSymbol.setOutline(outLine); this.leaderSym = backSymbol; } catch (java.lang.Exception e) { e.printStackTrace(); // never happened } } // IClone interface /** * @see IClone#esri_clone * @return IClone */ public IClone esri_clone() { FilledLeaderCallout filledLeaderCallout = new FilledLeaderCallout(); filledLeaderCallout.bottom = this.bottom; filledLeaderCallout.left = this.left; filledLeaderCallout.right = this.right; filledLeaderCallout.top = this.top; filledLeaderCallout.backSym = this.backSym; filledLeaderCallout.leaderSym = this.leaderSym; filledLeaderCallout.anchorPoint = this.anchorPoint; filledLeaderCallout.leaderTolerance = this.leaderTolerance; return filledLeaderCallout; } /** * @see IClone#assign * @param clone */ public void assign(IClone clone) { // no implemented } /** * @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 IPoint */ 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() { return null; // no implemented } /** * @see ITextBackground#setTextSymbolByRef * @param textSymbol */ public void setTextSymbolByRef(ITextSymbol textSymbol) { // no implemented } /** * @see ITextBackground#setTextBoxByRef * @param envelope * @throws IOException * @throws AutomationException */ @SuppressWarnings("deprecation") public void setTextBoxByRef(IEnvelope envelope) throws IOException, AutomationException { this.textBox = (Envelope) envelope; this.callOutPoly = new Polygon(); this.callOutPoly.setRectangle(this.textBox); this.callOutBox = (Envelope) this.callOutPoly.getEnvelope(); } /** * @see ITextBackground#queryBoundary */ public void queryBoundary(int hDC, ITransformation transformation, IPolygon iPolygonBoundary) { Polygon boundary = (Polygon) iPolygonBoundary; try { IDisplayTransformation displayTransformation = (IDisplayTransformation) transformation; // double dLeft = displayTransformation.fromPoints(this.left); double dRight = displayTransformation.fromPoints(this.right); double dTop = displayTransformation.fromPoints(this.top); double dBottom = displayTransformation.fromPoints(this.bottom); // Point centerPt = new Point(); centerPt.setX(this.callOutBox.getXMin() + this.callOutBox.getWidth() / 2); centerPt.setY(this.callOutBox.getYMin() + this.callOutBox.getHeight() / 2); // Envelope newEnv = (Envelope) this.callOutPoly.getEnvelope(); newEnv.setXMin(newEnv.getXMin() - dLeft); newEnv.setXMax(newEnv.getXMax() + dRight); newEnv.setYMin(newEnv.getYMin() - dBottom); newEnv.setYMax(newEnv.getYMax() + dTop); // // This will populate Boundary with a polygon based on the envelope SimpleFillSymbol simpleFillSymbol = null; if (this.backSym instanceof SimpleFillSymbol) { simpleFillSymbol = (SimpleFillSymbol) this.backSym; simpleFillSymbol.queryBoundary(hDC, transformation, createCallout(newEnv, 0.125, displayTransformation).getEnvelope(), boundary); } boundary.simplify(); Polygon leaderBound = createLeader(newEnv, transformation); leaderBound.simplify(); IGeometry geom = boundary.union(leaderBound); // Don't want to pass back a different Boundary reference // Set our new geometry into the passed in Boundary reference - performance! boundary.assign((IClone) geom); } catch(IOException e) { // never happens } } /** * @see ITextBackground#draw */ public void draw(int hDC, ITransformation transformation) throws IOException, AutomationException { // Create the callout DisplayTransformation displayTransformation = (DisplayTransformation) transformation; double lleft = displayTransformation.fromPoints(this.left); double lright = displayTransformation.fromPoints(this.right); double ltop = displayTransformation.fromPoints(this.top); double lbottom = displayTransformation.fromPoints(this.bottom); Point centerPt = new Point(); centerPt.setX(this.callOutBox.getXMin() + this.callOutBox.getWidth() / 2); centerPt.setY(this.callOutBox.getYMin() + this.callOutBox.getHeight() / 2); Envelope newEnv = (Envelope) this.callOutPoly.getEnvelope(); newEnv.setXMin(newEnv.getXMin() - lleft); newEnv.setXMax(newEnv.getXMax() + lright); newEnv.setYMin(newEnv.getYMin() - lbottom); newEnv.setYMax(newEnv.getYMax() + ltop); this.geometry = createCallout(newEnv, 0.125, displayTransformation); Polygon leaderPoly = createLeader(this.callOutBox, transformation); // Draw the leader ISymbol symbol2 =(ISymbol) this.leaderSym; symbol2.setupDC(hDC, transformation); symbol2.draw(leaderPoly); symbol2.resetDC(); // Draw the callout ISymbol symbol = (ISymbol) this.backSym; symbol.setupDC(hDC, transformation); symbol.draw(this.geometry); symbol.resetDC(); } /** * @see ITextBackground2#setTextBoundaryByRef */ public void setTextBoundaryByRef(Polygon polygon) throws IOException, AutomationException { // If pTextBox Is RHS Then Exit Property 'same, do nothing this.textBox = (Envelope) polygon.getEnvelope(); Polygon plgl = new Polygon(); this.callOutPoly = plgl; ISegmentCollection segColl = plgl; segColl.setRectangle(this.textBox); // When they set the text box, let's create the geometry for out symbol used by Draw this.callOutBox = (Envelope) this.callOutPoly.getEnvelope(); } /** * @see IQueryGeometry#getGeometry */ public IGeometry getGeometry(int i, ITransformation transformation, IGeometry geom) throws IOException, AutomationException { return createLeader(this.callOutBox, transformation); } /** * @see IQueryGeometry#queryEnvelope */ @SuppressWarnings("deprecation") public void queryEnvelope(int i, ITransformation transformation, IGeometry geom, IEnvelope envelope) { // no implemented } // IDisplayName interface /** * @see IDisplayName#getNameString */ public String getNameString() { return "Filled Leader Callout"; } // own public method /** * set left margin */ public void setLeft(double v) { this.left = v; } /** * @return left margin as double */ public double getLeft() { return this.left; } /** * set right margin * @param v double */ public void setRight(double v) { this.right = v; } /** * return right margin * @return as double */ public double getRight() { return this.right; } /** * set top margin * @param v double */ public void setTop(double v) { this.top = v; } /** * return top margin * @return as double */ public double getTop() { return this.top; } /** * set bottom margin * @param v double */ public void setBottom(double v) { this.bottom = v; } /** * return bottom margin * @return as double */ public double getBottom() { return this.bottom; } /** * set leader symbol * @param v is FillSymbol object */ public void setLeaderSym(IFillSymbol v) { this.leaderSym = v; } /** * return leader sybmol * @return as FillSymbol object */ public IFillSymbol getLeaderSym() { return this.leaderSym; } /** * set background symbol * @param v is FillSymbol object */ public void setBackSym(IFillSymbol v) { this.backSym = v; } /** * return background symbol * @return as FillSymbol object */ public IFillSymbol getBackSym() { return this.backSym; } // private methods /** * create balloon geometry */ private Polygon createCallout(Envelope env, double roundRatio, IDisplayTransformation displayTransformation) throws IOException, AutomationException { this.ldrDist = displayTransformation.fromPoints(this.leaderTolerance); double offset; if (env.getHeight() > env.getWidth()) offset = env.getWidth() * roundRatio; else offset = env.getHeight() * roundRatio; double xmin = env.getXMin(); double ymin = env.getYMin(); double xmax = env.getXMax(); double ymax = env.getYMax(); Polygon polygon = new Polygon(); // Point p1 = new Point(); p1.setX(xmin + offset); p1.setY(ymax); polygon.addPoint(p1, null, null); // Point p2 = new Point(); p2.setX(xmax - offset); p2.setY(ymax); polygon.addPoint(p2, null, null); // Point p3 = new Point(); p3.setX(xmax); p3.setY(ymax - offset); polygon.addPoint(p3, null, null); // Point p4 = new Point(); p4.setX(xmax); p4.setY(ymin + offset); polygon.addPoint(p4, null, null); // Point p5 = new Point(); p5.setX(xmax - offset); p5.setY(ymin); polygon.addPoint(p5, null, null); // Point p6 = new Point(); p6.setX(xmin + offset); p6.setY(ymin); polygon.addPoint(p6, null, null); // Point p7 = new Point(); p7.setX(xmin); p7.setY(ymin + offset); polygon.addPoint(p7, null, null); // Point p8 = new Point(); p8.setX(xmin); p8.setY(ymax - offset); polygon.addPoint(p8, null, null); // Point p9 = new Point(); p9.setX(xmin + offset); p9.setY(ymax); polygon.addPoint(p9, null, null); // return polygon; } /** * create leader geometry */ private Polygon createLeader(Envelope env, ITransformation transformation) throws IOException, AutomationException { DisplayTransformation displayTransformation = (DisplayTransformation) transformation; if (this.anchorPoint == null || this.callOutBox == null) return null; double lleft = displayTransformation.fromPoints(this.left); double lright = displayTransformation.fromPoints(this.right); double ltop = displayTransformation.fromPoints(this.top); double lbottom = displayTransformation.fromPoints(this.bottom); env.setXMin(env.getXMin() - lleft); env.setXMax(env.getXMax() + lright); env.setYMin(env.getYMin() - lbottom); env.setYMax(env.getYMax() + ltop); Point centerPt = new Point(); centerPt.setX(env.getXMin() + env.getWidth() / 2); centerPt.setY(env.getYMin() + env.getHeight() / 2); Line newLine = new Line(); newLine.putCoords(centerPt, this.anchorPoint); double angle = newLine.getAngle() * 180.0 / Math.PI; // Create a new point angled from the center point Point p0 = new Point(); Point p1 = new Point(); double d = (env.getYMin() - env.getXMax()) / 8; if ((angle > 45 && angle < 135) || (angle > -135 && angle < -45)) { // top || bottom p0.setX(centerPt.getX() - d); p0.setY(centerPt.getY() + 0); p1.setX(centerPt.getX() + d); p1.setY(centerPt.getY() + 0); } else // left || right { p0.setX(centerPt.getX() + 0); p0.setY(centerPt.getY() - d); p1.setX(centerPt.getX() + 0); p1.setY(centerPt.getY() + d); } Polygon polygon = new Polygon(); polygon.addPoint(this.anchorPoint, null, null); polygon.addPoint(p0, null, null); polygon.addPoint(p1, null, null); return polygon; } }