Custom vertex editing tools
arcgissamples\editing\CustomVertexTools.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.editing;

import java.io.IOException;

import com.esri.arcgis.carto.IFeatureLayer;
import com.esri.arcgis.carto.IFeatureSelection;
import com.esri.arcgis.controls.BaseTool;
import com.esri.arcgis.controls.EngineEditor;
import com.esri.arcgis.controls.EngineSketchOperation;
import com.esri.arcgis.controls.HookHelper;
import com.esri.arcgis.controls.IEngineEditTask;
import com.esri.arcgis.controls.esriEngineEditState;
import com.esri.arcgis.controls.esriEngineSketchOperationType;
import com.esri.arcgis.geodatabase.ISelectionSet;
import com.esri.arcgis.geometry.IGeometry;
import com.esri.arcgis.geometry.IGeometryCollection;
import com.esri.arcgis.geometry.IHitTest;
import com.esri.arcgis.geometry.IPoint;
import com.esri.arcgis.geometry.IPointCollection;
import com.esri.arcgis.geometry.Point;
import com.esri.arcgis.geometry.esriGeometryHitPartType;
import com.esri.arcgis.geometry.esriGeometryType;
import com.esri.arcgis.interop.AutomationException;
import com.esri.arcgis.systemUI.ICommandSubType;

class CustomVertexTools extends BaseTool implements ICommandSubType {
  private static final long serialVersionUID = -418758068047848768L;

  private EngineEditor editor;

  private HookHelper hookHelper;

  private IEngineEditTask modifyFeatureTask;

  private int type;

  @Override
  public void onCreate(Object hook) {
  super.onCreate(hook);
  try {

    this.hookHelper = new HookHelper();
    this.hookHelper.setHookByRef(hook);
    this.editor = new EngineEditor();
  } catch (Exception e) {
    e.printStackTrace();
  }
  }

  @Override
  public void onClick() {
  try {

    // find the Modify Feature task and set it as the current task
    // so the vertices will be automatically highlighted when tool is
    // clicked
    this.modifyFeatureTask = editor
      .getTaskByUniqueName("ControlToolsEditing_ModifyFeatureTask");
    editor.setCurrentTaskByRef(this.modifyFeatureTask);
  } catch (Exception e) {
    e.printStackTrace();
  }
  }

  @Override
  public void onMouseUp(int button, int shift, int x, int y) {
  try {

    IGeometry editShape = editor.getGeometry();

    // location clicked as a point object
    IPoint clickedPt = hookHelper.getActiveView().getScreenDisplay()
      .getDisplayTransformation().toMapPoint(x, y);

    IHitTest hitShape = (IHitTest) editShape;
    IPoint hitPoint = new Point();
    double[] hitDistance = { -1 };
    int[] hitPartIndex = { -1 };
    int[] hitSegmentIndex = { -1 };
    boolean[] bRightSide = { false };
    int hitPartType = esriGeometryHitPartType.esriGeometryPartNone;

    // the searchRadius is the maximum distance away, in map units, from
    // the shape that will be used
    // for the test - change to an appropriate value.
    double searchRadius = 1;
    switch (type) {
    case 1: // Insert Vertex
    hitPartType = esriGeometryHitPartType.esriGeometryPartBoundary;
    break;

    case 2: // Delete Vertex

    hitPartType = esriGeometryHitPartType.esriGeometryPartVertex;
    break;
    }

    hitShape.hitTest(clickedPt, searchRadius, hitPartType, hitPoint,
      hitDistance, hitPartIndex, hitSegmentIndex, bRightSide);

    // check whether the HitTest was successful (i.e within the search
    // radius)
    if (!hitPoint.isEmpty()) {

    EngineSketchOperation sketchOp = new EngineSketchOperation();
    sketchOp.start(editor);

    int opType = esriEngineSketchOperationType.esriEngineSketchOperationGeneral;

    // Get the PointCollection for a specific path or ring by
    // hitPartIndex to handle multi-part features
    IGeometryCollection geometryCol = (IGeometryCollection) editShape;
    IPointCollection pathOrRingPointCollection = (IPointCollection) geometryCol
      .getGeometry(hitPartIndex[0]);

    switch (this.type) {
    case 1: // Insert Vertex

      // add new vertex to the path or ring PointCollection
      pathOrRingPointCollection.addPoint(clickedPt, null,
        hitSegmentIndex[0]);
      sketchOp.setMenuString("Insert Vertex (Custom)");
      opType = esriEngineSketchOperationType.esriEngineSketchOperationVertexAdded;

      break;

    case 2: // Delete Vertex.

      // delete a vertex from the path or ring PointCollection
      pathOrRingPointCollection.removePoints(hitSegmentIndex[0], 1);
      sketchOp.setMenuString("Delete Vertex (Custom)");
      opType = esriEngineSketchOperationType.esriEngineSketchOperationVertexDeleted;

      break;
    }

    // remove the old PointCollection from the GeometryCollection
    // and replace with the new one
    geometryCol.removeGeometries(hitPartIndex[0], 1);
    geometryCol.addGeometry((IGeometry) pathOrRingPointCollection,
      hitPartIndex[0], null);

    sketchOp.finish(null, opType, clickedPt);
    }
  } catch (Exception ex) {
    ex.printStackTrace();
  }
  }

  @Override
  public boolean isEnabled() {
  try {
    if (editor.getEditState() == esriEngineEditState.esriEngineStateNotEditing) {
    return false;
    }

    // check for appropriate geometry types
    int geomType = ((IFeatureLayer) editor.getTargetLayer())
      .getFeatureClass().getShapeType();
    if ((geomType != esriGeometryType.esriGeometryPolygon)
      && (geomType != esriGeometryType.esriGeometryPolyline)) {
    return false;
    }

    // check that only one feature is currently selected
    IFeatureSelection featureSelection = (IFeatureSelection) editor
      .getTargetLayer();
    ISelectionSet selectionSet = featureSelection.getSelectionSet();
    if (selectionSet.getCount() != 1) {
    return false;
    }

    return true;
  } catch (Exception e) {
    e.printStackTrace();
    return false;
  }
  }

  public int getCount() throws IOException, AutomationException {
  // The number of subtypes in this tool
  return 2;
  }

  public void setSubType(int index) throws IOException, AutomationException {

  type = index;

  this.category = "Vertex Cmds (Java)";

  switch (type) {
  case 1:
    // Insert Vertex using the custom command
    this.caption = "Insert vertex (custom)";
    this.message = "Insert vertex at clicked location using custom command";
    this.toolTip = "Insert vertex at clicked location using custom command";
    this.name = "VertexCommands_CustomInsertVertex";
    this.cursorPath = this.getClass().getClassLoader().getResource(
      "InsertVertexCursor.cur").getPath();
    this.bitmapPath = this.getClass().getClassLoader().getResource(
      "InsertVertex.bmp").getPath();
    if(System.getProperty("os.name").toLowerCase().indexOf("win")>-1){
    this.cursorPath =this.cursorPath.substring(1).replaceAll("%20"," ");
    this.bitmapPath =this.bitmapPath.substring(1).replaceAll("%20"," ");
    }
    break;

  case 2:
    // Delete vertex at clicked location using the custom command
    this.caption = "Delete vertex (custom)";
    this.message = "Delete vertex at clicked location using custom command";
    this.toolTip = "Delete vertex at clicked location using custom command";
    this.name = "VertexCommands_CustomDeleteVertex";
    this.cursorPath = this.getClass().getClassLoader().getResource(
      "DeleteVertexCursor.cur").getPath();
    ;
    this.bitmapPath = this.getClass().getClassLoader().getResource(
      "DeleteVertex.bmp").getPath();
    if(System.getProperty("os.name").toLowerCase().indexOf("win")>-1){
    this.cursorPath =this.cursorPath.substring(1).replaceAll("%20"," ");
    this.bitmapPath =this.bitmapPath.substring(1).replaceAll("%20"," ");
    }
    break;
  }

  }
}