arcgissamples\cartography\MZRenderer.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.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.text.NumberFormat; import com.esri.arcgis.carto.IFeatureIDSet; import com.esri.arcgis.carto.IFeatureRenderer; import com.esri.arcgis.carto.ILegendGroup; import com.esri.arcgis.carto.ILegendInfo; import com.esri.arcgis.carto.ILegendItem; import com.esri.arcgis.carto.LegendClass; import com.esri.arcgis.carto.LegendGroup; import com.esri.arcgis.carto.SimpleRenderer; import com.esri.arcgis.display.IDisplay; import com.esri.arcgis.display.ISimpleMarkerSymbol; import com.esri.arcgis.display.ISimpleTextSymbol; import com.esri.arcgis.display.ISymbol; import com.esri.arcgis.display.RgbColor; import com.esri.arcgis.display.SimpleLineSymbol; import com.esri.arcgis.display.SimpleMarkerSymbol; import com.esri.arcgis.display.TextSymbol; import com.esri.arcgis.display.esriSimpleLineStyle; import com.esri.arcgis.display.esriSimpleMarkerStyle; import com.esri.arcgis.display.esriTextHorizontalAlignment; import com.esri.arcgis.display.esriTextVerticalAlignment; import com.esri.arcgis.geodatabase.IFeature; import com.esri.arcgis.geodatabase.IFeatureClass; import com.esri.arcgis.geodatabase.IFeatureCursor; import com.esri.arcgis.geodatabase.IFeatureDraw; import com.esri.arcgis.geodatabase.IQueryFilter; import com.esri.arcgis.geodatabase.esriDrawStyle; import com.esri.arcgis.geometry.IPoint; import com.esri.arcgis.geometry.IPointCollection; import com.esri.arcgis.geometry.esriGeometryType; import com.esri.arcgis.interop.AutomationException; import com.esri.arcgis.interop.extn.ArcGISExtension; import com.esri.arcgis.system.IDocumentVersionSupportGEN; import com.esri.arcgis.system.ITrackCancel; import com.esri.arcgis.system.esriArcGISVersion; import com.esri.arcgis.system.esriDrawPhase; @ArcGISExtension public class MZRenderer implements IFeatureRenderer, ILegendInfo, Externalizable, IDocumentVersionSupportGEN{ /** * MODETYPE_MEASURES constant */ public static final int MODETYPE_MEASURES = 0; /** * MODETYPE_ZVALUES constant */ public static final int MODETYPE_ZVALUES = 1; /** * ORIENTATIONTYPE_ALONG constant */ public static final int ORIENTATIONTYPE_ALONG = 0; /** * ORIENTATIONTYPE_HORIZONTAL constant */ public static final int ORIENTATIONTYPE_HORIZONTAL = 1; /** * ORIENTATIONTYPE_PERPENDICULAR constant */ public static final int ORIENTATIONTYPE_PERPENDICULAR = 2; private ISimpleTextSymbol textSymbol; private ILegendGroup legendGroup; private int renderMode; private int labelInterval; private int markerOrientation; private int labelOrientation; public MZRenderer() { try { initialize(); } catch (Exception e) { e.printStackTrace(); } } public void initialize() throws Exception { textSymbol = new TextSymbol(); // create data structure to store symbols in legendGroup legendGroup = new LegendGroup(); legendGroup.setHeading("MZ Line Renderer"); // set renderer defaults renderMode = MODETYPE_MEASURES; labelInterval = 5; markerOrientation = ORIENTATIONTYPE_HORIZONTAL; labelOrientation = ORIENTATIONTYPE_HORIZONTAL; // build 3 legend classes, two for markers and one for lines. // for each, create and assign symbol, and add to pLegendGroup. // (0) marker symbol for vertices LegendClass legendClass0 = new LegendClass(); RgbColor colorRed = new RgbColor(); colorRed.setRGB(0xFF); // red SimpleMarkerSymbol simpleMSym = new SimpleMarkerSymbol(); simpleMSym.setColor(colorRed); simpleMSym.setStyle(esriSimpleMarkerStyle.esriSMSSquare); simpleMSym.setSize(6.0); legendClass0.setSymbolByRef(simpleMSym); legendClass0.setLabel("M value at vertex"); legendGroup.addClass(legendClass0); // (1) marker symbol for end vertices (endpoints) LegendClass legendClass1 = new LegendClass(); RgbColor colorGreen = new RgbColor(); colorGreen.setRGB(0xFF00); // green SimpleMarkerSymbol simpleMSym1 = new SimpleMarkerSymbol(); simpleMSym1.setColor(colorGreen); simpleMSym1.setStyle(esriSimpleMarkerStyle.esriSMSSquare); simpleMSym1.setSize(6.0); legendClass1.setSymbolByRef(simpleMSym1); legendClass1.setLabel("M value at endpoint"); legendGroup.addClass(legendClass1); // (2) line symbol LegendClass legendClass2 = new LegendClass(); RgbColor colorBlue = new RgbColor(); colorBlue.setRGB(0xFF0000); // blue SimpleLineSymbol simpleLSym = new SimpleLineSymbol(); simpleLSym.setColor(colorBlue); simpleLSym.setStyle(esriSimpleLineStyle.esriSLSSolid); simpleLSym.setWidth(0.5); legendClass2.setSymbolByRef(simpleLSym); legendClass2.setLabel("Line"); legendGroup.addClass(legendClass2); legendGroup.setVisible(true); } public void terminate() { legendGroup = null; } public boolean canRender(IFeatureClass featClass, IDisplay display) throws IOException, AutomationException { return (featClass.getShapeType() == esriGeometryType.esriGeometryPolyline); } public void draw(IFeatureCursor featureCursor, int drawPhase,IDisplay display, ITrackCancel trackCancel) throws IOException,AutomationException { // do not draw features if no display if (display == null) return; if (drawPhase == esriDrawPhase.esriDPGeography) { ISymbol blueSymbol = legendGroup.esri_getClass(2).getSymbol(); // for line ISymbol iRedSymbol = legendGroup.esri_getClass(0).getSymbol(); ISimpleMarkerSymbol redSymbol = (ISimpleMarkerSymbol) (iRedSymbol); ISymbol iGreenSymbol = legendGroup.esri_getClass(1).getSymbol(); ISimpleMarkerSymbol greenSymbol = (ISimpleMarkerSymbol) (iGreenSymbol); double dblVOffset = (redSymbol.getSize() / 2) + 2; // offset for vertices double dblEOffset = (greenSymbol.getSize() / 2) + 2; // offset for endpoints NumberFormat numberFormat = NumberFormat.getNumberInstance(); numberFormat.setMaximumFractionDigits(2); // while there are still more polyline features and drawing has not been canceled for (IFeature feat = featureCursor.nextFeature(); feat != null; feat = featureCursor.nextFeature()) { // draw the line feature IFeatureDraw featDraw = (IFeatureDraw) feat; display.setSymbol(blueSymbol); featDraw.draw(drawPhase, display, blueSymbol, true, null,esriDrawStyle.esriDSNormal); // using IPointCollection, loop through and draw the vertices // for the feature IPointCollection pointCollect = (IPointCollection) feat.getShape(); int nPoints = pointCollect.getPointCount(); for (int i = 0; i < nPoints; i++) { IPoint point = pointCollect.getPoint(i); IPoint nextPoint = null; if (i < nPoints - 1) nextPoint = pointCollect.getPoint(i + 1); // for calculation of angles get next vertex else nextPoint = pointCollect.getPoint(i - 1); // for calculation of angles get previous vertext double angle = arithAngle(point, nextPoint); double markAngle = orientAngle(angle, markerOrientation); // angle for marker double labAngle = orientAngle(angle, labelOrientation); // angle for label redSymbol.setAngle(markAngle); greenSymbol.setAngle(markAngle); if ((i != 0) && (i != (nPoints - 1))) {// no first and no last vertex display.setSymbol((ISymbol) redSymbol); display.drawPoint(point); if ((i % this.labelInterval) == 0) drawMyText(point, display, labAngle, dblVOffset,numberFormat); } else { // others display.setSymbol((ISymbol) greenSymbol); display.drawPoint(point); drawMyText(point, display, labAngle, dblEOffset,numberFormat); } } if (trackCancel != null) if (!trackCancel.esri_continue()) break; } } } public ISymbol getSymbolByFeature(IFeature arg0) throws IOException, AutomationException { ISymbol symbol = legendGroup.esri_getClass(2).getSymbol(); // return the line symbol only return symbol; } public boolean isRenderPhase(int drawPhase) throws IOException,AutomationException { return (drawPhase == esriDrawPhase.esriDPGeography); } public void prepareFilter(IFeatureClass arg0, IQueryFilter arg1)throws IOException, AutomationException { } public void setExclusionSetByRef(IFeatureIDSet arg0) throws IOException,AutomationException { } public ILegendGroup getLegendGroup(int arg0) throws IOException,AutomationException { return legendGroup; } public int getLegendGroupCount() throws IOException, AutomationException { if (legendGroup == null) return 0; else return 1; } public ILegendItem getLegendItem() throws IOException, AutomationException { return null; } public boolean isSymbolsAreGraduated() throws IOException,AutomationException { return false; } public void setSymbolsAreGraduated(boolean arg0) throws IOException,AutomationException { } public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException { legendGroup = (ILegendGroup) in.readObject(); renderMode = Integer.parseInt((String) in.readObject()); labelInterval = Integer.parseInt((String) in.readObject()); markerOrientation = Integer.parseInt((String) in.readObject()); labelOrientation = Integer.parseInt((String) in.readObject()); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(legendGroup); out.writeObject(Integer.toString(renderMode)); out.writeObject(Integer.toString(labelInterval)); out.writeObject(Integer.toString(markerOrientation)); out.writeObject(Integer.toString(labelOrientation)); } // ********** Utility Methods /** * Method returns the direction of vector from pPoint to pNextPoint in * degrees arithmetic coordinates used (East = 0, North = 90, West = 180, * South = 270). * @param point first point * @param nextPoint second point */ private double arithAngle(IPoint point, IPoint nextPoint) throws IOException, AutomationException { double slope; double angleRadians; double xDiff = nextPoint.getX() - point.getX(); double yDiff = nextPoint.getY() - point.getY(); if (xDiff > 0) { // quadrants I or IV slope = yDiff / xDiff; angleRadians = Math.atan(slope); } else if (xDiff < 0) { // quadrants II or III slope = yDiff / xDiff; // rotate 180 degrees to get left hand quadrants angleRadians = Math.atan(slope) - Math.PI; } else { // dblXDiff = 0 ' special case of perfectly vertical line if (yDiff >= 0) angleRadians = Math.PI / 2; else angleRadians = -1 * (Math.PI / 2); } // convert radians to degrees and return return angleRadians * 180 / Math.PI; } /** * Method calculates new angle depend on the orientation. * * @param angle in degrees * @param orientation (ORIENTATIONTYPE_ALONG, ORIENTATIONTYPE_HORIZONTAL or ORIENTATIONTYPE_PERPENDICULAR) * @return new angle in degrees */ private double orientAngle(double angle, int orientation) { switch (orientation) { case ORIENTATIONTYPE_ALONG: return angle; case ORIENTATIONTYPE_HORIZONTAL: return 0; case ORIENTATIONTYPE_PERPENDICULAR: return angle - 90; } return 0; } /** * Method draws text label on map. */ private void drawMyText(IPoint point, IDisplay display, double angle,double offset, NumberFormat numberFormat) throws IOException,AutomationException { textSymbol.setHorizontalAlignment(esriTextHorizontalAlignment.esriTHALeft); // left side of label closest to point textSymbol.setXOffset(offset);// apply offset along x-axis if other textSymbol.setVerticalAlignment(esriTextVerticalAlignment.esriTVABottom); // bottom of label closest to point textSymbol.setAngle(angle); display.setSymbol((ISymbol) this.textSymbol); // get value from pPoint, and draw it. // Note that most sample data have no M or Z values. // We provide fake values if necessary (if Not a Number) if (this.renderMode == MODETYPE_MEASURES) { if (Double.isNaN(point.getM())) { point.setM(point.getX()); } display.drawText(point, numberFormat.format(point.getM())); } if (this.renderMode == MODETYPE_ZVALUES) { if (Double.isNaN(point.getZ())) { point.setZ(point.getY()); } display.drawText(point, numberFormat.format(point.getZ())); } } // *********************** Public Methods /** * The method returns rederer mode. * @return renderer mode (MODETYPE_MEASURES or MODETYPE_ZVALUES) */ public int getRenderMode() { return renderMode; } /** * Method sets renderer mode. * @param inMode (MODETYPE_MEASURES or MODETYPE_ZVALUES) */ public void setRenderMode(int inMode) throws IOException,AutomationException { renderMode = inMode; // renderMode changes so change labels if (renderMode == MODETYPE_MEASURES) { // M values legendGroup.esri_getClass(0).setLabel("M value at vertex"); legendGroup.esri_getClass(1).setLabel("M value at endpoint"); } else { // Z values legendGroup.esri_getClass(0).setLabel("Z value at vertex"); legendGroup.esri_getClass(1).setLabel("Z value at endpoint"); } } /** * The method sets label inteval. */ public void setLabelInterval(int intInInterval) { labelInterval = intInInterval; } public int getLabelInterval() { return labelOrientation; } /** * The method sets label orientation. * @param orientation(ORIENTATIONTYPE_ALONG, ORIENTATIONTYPE_HORIZONTAL or ORIENTATIONTYPE_PERPENDICULAR) */ public void setMarkerOrientation(int orientation) { markerOrientation = orientation; } public int getMarkerOrientation() { return markerOrientation; } /** * The method sets label orientation. * @param orientation (ORIENTATIONTYPE_ALONG, ORIENTATIONTYPE_HORIZONTAL or ORIENTATIONTYPE_PERPENDICULAR) */ public void setLabelOrientation(int orientation) { labelOrientation = orientation; } public int getLabelOrientation() { return labelOrientation; } public Object convertToSupportedObject(int arg0) throws IOException,AutomationException { SimpleRenderer simpleRend = new SimpleRenderer(); SimpleLineSymbol simpleLineSymbol = new SimpleLineSymbol(); simpleLineSymbol.setStyle(esriSimpleLineStyle.esriSLSDash); simpleRend.setSymbolByRef(simpleLineSymbol); return simpleRend; } public boolean isSupportedAtVersion(int arg0) throws IOException,AutomationException { // Support all versions above or equal 9.3.1. if (arg0 >= esriArcGISVersion.esriArcGISVersion93) return true; else return false; } }