Editing in a custom application
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.IActiveView;
import com.esri.arcgis.carto.IFeatureLayer;
import com.esri.arcgis.carto.IFeatureSelection;
import com.esri.arcgis.carto.esriViewDrawPhase;
import com.esri.arcgis.controls.BaseTool;
import com.esri.arcgis.controls.EngineEditor;
import com.esri.arcgis.controls.HookHelper;
import com.esri.arcgis.controls.IEngineEditTask;
import com.esri.arcgis.controls.esriEngineEditState;
import com.esri.arcgis.geodatabase.ICursor;
import com.esri.arcgis.geodatabase.IDataset;
import com.esri.arcgis.geodatabase.IFeature;
import com.esri.arcgis.geodatabase.IFeatureCursor;
import com.esri.arcgis.geodatabase.IFeatureCursorProxy;
import com.esri.arcgis.geodatabase.ISelectionSet;
import com.esri.arcgis.geodatabase.esriWorkspaceType;
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.ITopologicalOperator;
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 currentEditTask;

  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
    for (int i = 0; i < editor.getTaskCount() ; i++) {
    IEngineEditTask editTask = editor.getTask(i);

    if (editTask.getName().equalsIgnoreCase("Modify Feature")) {
      this.currentEditTask = editTask;
      editor.setCurrentTaskByRef(editTask);
      break;
    }
    }
  } catch (Exception e) {
    e.printStackTrace();
  }
  }

  @Override
  public void onMouseUp(int button, int shift, int x, int y) {
  try {
    // get layer being edited
    IFeatureLayer featureLayer = (IFeatureLayer) editor.getTargetLayer();

    // modify the geometry appropriately for each sub-typed command
    // get the currently selected feature
    IFeatureSelection featureSelection = (IFeatureSelection) featureLayer;
    ISelectionSet selectionSet = featureSelection.getSelectionSet();
    ICursor cursor[] = new ICursor[1];
    selectionSet.search(null, true, cursor);
    IFeatureCursor featureCursor = new IFeatureCursorProxy(cursor[0]);
    // the enabled property has already checked that only 1 feature is
    // selected
    IFeature feature = featureCursor.nextFeature();
    // Take a copy of geometry for the selected feature
    IGeometry editShape = feature.getShapeCopy();

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

      break;

    case 2: // Delete Vertex.

      // delete a vertex from the path or ring PointCollection
      pathOrRingPointCollection.removePoints(hitSegmentIndex[0], 1);

      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);

    // Ensure that the modified shape is topologically correct
    ITopologicalOperator topoOp = (ITopologicalOperator) editShape;
    topoOp.simplify();

    try {
      editor.startOperation();
      feature.setShapeByRef(editShape);
      feature.store();
      editor.stopOperation("Add Vertex");
    } catch (Exception ex) {
      editor.abortOperation();
      ex.printStackTrace();
    }
    }

    // Due to the behaviour of the Modify Task the following code is
    // required to
    // ensure that any changes are displayed immediately depending on
    // data source type
    IDataset dataset = (IDataset) featureLayer;
    int datasetType = dataset.getWorkspace().getType();

    if ((datasetType == esriWorkspaceType.esriFileSystemWorkspace)
      || (dataset.getCategory()
        .equalsIgnoreCase("File Geodatabase Feature Class"))) {
    this.currentEditTask.onFinishSketch();
    editor.setCurrentTaskByRef(this.currentEditTask);
    } else {
    this.onClick();
    }

    IActiveView activeView = (IActiveView) editor.getMap();
    activeView.partialRefresh(esriViewDrawPhase.esriViewGeography,
      featureLayer, activeView.getExtent());

  } 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;
  }

  }
}