arcgissamples\geoprocessing\customtool\DeleteFeatures.java—ArcObjects 10.4 Help for Java | ArcGIS for Desktop
Custom Java Geoprocessing Tool - Delete Features
arcgissamples\geoprocessing\customtool\DeleteFeatures.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.geoprocessing.customtool;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import com.esri.arcgis.datasourcesGDB.FileGDBWorkspaceFactory;
import com.esri.arcgis.datasourcesfile.DELayerType;
import com.esri.arcgis.datasourcesfile.ShapefileWorkspaceFactory;
import com.esri.arcgis.geodatabase.DEFeatureClass;
import com.esri.arcgis.geodatabase.DEFeatureClassType;
import com.esri.arcgis.geodatabase.Feature;
import com.esri.arcgis.geodatabase.FeatureClass;
import com.esri.arcgis.geodatabase.IFeature;
import com.esri.arcgis.geodatabase.IFeatureClass;
import com.esri.arcgis.geodatabase.IFeatureClassProxy;
import com.esri.arcgis.geodatabase.IFeatureCursor;
import com.esri.arcgis.geodatabase.IFields;
import com.esri.arcgis.geodatabase.IGPMessages;
import com.esri.arcgis.geodatabase.IGPValue;
import com.esri.arcgis.geodatabase.IWorkspaceFactory;
import com.esri.arcgis.geodatabase.QueryFilter;
import com.esri.arcgis.geodatabase.Workspace;
import com.esri.arcgis.geodatabase.esriFeatureType;
import com.esri.arcgis.geodatabase.esriGPMessageSeverity;
import com.esri.arcgis.geoprocessing.BaseGeoprocessingTool;
import com.esri.arcgis.geoprocessing.GPCompositeDataType;
import com.esri.arcgis.geoprocessing.GPFeatureLayer;
import com.esri.arcgis.geoprocessing.GPFeatureLayerType;
import com.esri.arcgis.geoprocessing.GPFeatureRecordSetLayerType;
import com.esri.arcgis.geoprocessing.GPFeatureSchema;
import com.esri.arcgis.geoprocessing.GPLayerType;
import com.esri.arcgis.geoprocessing.GPParameter;
import com.esri.arcgis.geoprocessing.GPSQLExpression;
import com.esri.arcgis.geoprocessing.GPSQLExpressionType;
import com.esri.arcgis.geoprocessing.IGPEnvironmentManager;
import com.esri.arcgis.geoprocessing.IGPParameter;
import com.esri.arcgis.geoprocessing.esriGPParameterDirection;
import com.esri.arcgis.geoprocessing.esriGPParameterType;
import com.esri.arcgis.interop.AutomationException;
import com.esri.arcgis.system.Array;
import com.esri.arcgis.system.IArray;
import com.esri.arcgis.system.IName;
import com.esri.arcgis.system.ITrackCancel;

public class DeleteFeatures extends BaseGeoprocessingTool {

  private static final long serialVersionUID = 1L;
  private String toolName = "JavaDeleteFeatures";
  private String displayName = "Java DeleteFeatures Tool";
  private String metadataFileName = toolName + ".xml";
  private static String sw = null;

  /**
   * Empty Constructor
   */
  public DeleteFeatures() {
  }

  /**
   * Returns the name (string) of the tool. Should be unique. Used by the GP
   * framework. This name appears when executing the tool at the command line or
   * in scripting. This name should be unique to each toolbox and must not
   * contain spaces.
   */
  public String getName() throws IOException, AutomationException {
    return toolName;
  }

  /**
   * Returns Display Name (string) of the tool, as seen in ArcToolbox.
   */
  public String getDisplayName() throws IOException, AutomationException {
    return displayName;
  }

  /**
   * Returns the full name (IName) of the tool
   */
  public IName getFullName() throws IOException, AutomationException {
    return (IName) new DFFunctionFactory().getFunctionName(toolName);
  }

  /**
   * Returns an array of parameters. This is where the tool's input and output
   * parameters are defined. This property returns an IArray of parameter
   * objects (IGPParameter). These objects define the characteristics of the
   * input and output parameters.
   */
  public IArray getParameterInfo() throws IOException, AutomationException {
    IArray parameters = new Array();

    // a composite data type for input param, so that fc, layer file and map
    // layer can be used as input
    GPCompositeDataType compositeDataType = new GPCompositeDataType();
    compositeDataType.addDataType(new GPFeatureLayerType());
    compositeDataType.addDataType(new DEFeatureClassType());
    compositeDataType.addDataType(new GPLayerType());
    compositeDataType.addDataType(new DELayerType());
    compositeDataType.addDataType(new GPFeatureRecordSetLayerType());

    // Input Features - the fc/layer from which features will be deleted
    GPParameter inFeaturesParameter1 = new GPParameter();
    inFeaturesParameter1.setDataTypeByRef(compositeDataType);
    inFeaturesParameter1.setValueByRef(new GPFeatureLayer());
    inFeaturesParameter1.setDirection(esriGPParameterDirection.esriGPParameterDirectionInput);
    inFeaturesParameter1.setDisplayName("Input Features");
    inFeaturesParameter1.setName("in_features");
    inFeaturesParameter1.setParameterType(esriGPParameterType.esriGPParameterTypeRequired);
    parameters.add(inFeaturesParameter1);

    // where clause - to determine which features to delete
    GPParameter inexpressionParameter2 = new GPParameter();
    inexpressionParameter2.setDataTypeByRef(new GPSQLExpressionType());
    inexpressionParameter2.setValueByRef(new GPSQLExpression());
    inexpressionParameter2.setDirection(esriGPParameterDirection.esriGPParameterDirectionInput);
    inexpressionParameter2.setDisplayName("Expression");
    inexpressionParameter2.setName("in_expression");
    inexpressionParameter2.setParameterType(esriGPParameterType.esriGPParameterTypeRequired);
    inexpressionParameter2.addDependency("in_features");
    parameters.add(inexpressionParameter2);

    // Derived Output Features
    GPParameter outFeaturesParameter = new GPParameter();
    outFeaturesParameter.setDataTypeByRef(new DEFeatureClassType());
    outFeaturesParameter.setValueByRef(new DEFeatureClass());
    outFeaturesParameter.setDirection(esriGPParameterDirection.esriGPParameterDirectionOutput);
    outFeaturesParameter.setDisplayName("Output Features");
    outFeaturesParameter.setName("out_features");
    outFeaturesParameter.setParameterType(esriGPParameterType.esriGPParameterTypeRequired);

    GPFeatureSchema featureSchema = new GPFeatureSchema();
    featureSchema.setCloneDependency(true);

    outFeaturesParameter.setSchemaByRef(featureSchema);

    parameters.add(outFeaturesParameter);

    return parameters;
  }
  
  /**
   * Called each time the user changes a parameter in the tool dialog or Command
   * Line. This updates the output data of the tool, which extremely useful for
   * building models. After returning from UpdateParameters(), the GP framework
   * calls its internal validation routine to check that a given set of
   * parameter values are of the appropriate number, DataType, and value.
   */
  public void updateParameters(IArray paramvalues, IGPEnvironmentManager envMgr) {
    try {
      // Retrieve the Input Feature Set parameter
      IGPParameter inputFeatureSetParameter = (IGPParameter) paramvalues.getElement(0);
      IGPValue inputFeatureSetValue = gpUtilities.unpackGPValue(inputFeatureSetParameter);
      String inputFS = inputFeatureSetValue.getAsText();
      
      // Retrieve Output Feature Class Parameter
      IGPParameter outputParameter = (IGPParameter) paramvalues.getElement(2);
      IGPValue outputFCValue = gpUtilities.unpackGPValue(outputParameter);
      String outputString = outputFCValue.getAsText();

      //force assign a value to output parameter only if its not already specified 
      String outputFC = "", fileName = "";
      if(outputString == null || outputString.isEmpty()){
        // retrieve the environment value for scratch workspace
        sw = envMgr.findEnvironment("scratchWorkspace").getValue().getAsText();
        if(sw != null && !sw.isEmpty() && inputFS != null){
          fileName = new File(inputFS).getName();
          if (fileName.contains(".")) {
            fileName = fileName.substring(0, fileName.lastIndexOf("."));
          }
          
          File swFile = new File(sw);
          if(swFile.isDirectory() && !swFile.getName().endsWith(".gdb") && 
              !swFile.getName().endsWith(".mdb")){
            outputFC = fileName + "_lessFeatures.shp";
          }
          else{
            outputFC = fileName + "_lessFeatures";
          }
        }
        
        outputFCValue.setAsText(sw + File.separator + outputFC);
      }

    } catch (Exception e) {
      e.printStackTrace();
    }
  }    

  /**
   * Updates GPMessages after GP framework validates tool's parameters
   * internally. User can specify custom messages. Called from the internal
   * validation routine. You can examine the messages created from internal
   * validation and change them if desired.
   */
  public void updateMessages(IArray paramvalues, IGPEnvironmentManager envMgr, IGPMessages gpMessages) {
    try {
      if (gpMessages.getMaxSeverity() == esriGPMessageSeverity.esriGPMessageSeverityError) {
        System.out.println("updateMessages in " + toolName + ": ");
        for (int i = 0; i < gpMessages.getCount(); i++) {
          System.out.println(gpMessages.getMessage(i).getDescription());
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * Uses input parameters and executes business logic to produce output
   * parameters(s)
   */
  public void execute(IArray paramvalues, ITrackCancel trackcancel, IGPEnvironmentManager envMgr, IGPMessages messages)
    throws IOException, AutomationException {
    // retrieve Input Features parameter
    IGPParameter inputFeaturesParameter = (IGPParameter) paramvalues.getElement(0);
    IGPValue inputFeaturesValue = gpUtilities.unpackGPValue(inputFeaturesParameter);
    String inputFeatures = inputFeaturesValue.getAsText();
    messages.addMessage("InputFeatures: " + inputFeatures);
    
    // retrieve input parameter's FeatureClass
    IFeatureClass[] ifc = new IFeatureClass[1];
    ifc[0] = new IFeatureClassProxy();
    gpUtilities.decodeFeatureLayer(inputFeaturesValue, ifc, null);
    FeatureClass inputFC = new FeatureClass(ifc[0]);

    // retrieve Expression parameter
    IGPParameter expressionParameter = (IGPParameter) paramvalues.getElement(1);
    IGPValue expressionValue = gpUtilities.unpackGPValue(expressionParameter);
    String expression = expressionValue.getAsText();
    messages.addMessage("Expression: " + expression);

    // retrieve Output Features
    IGPParameter outputFeaturesParameter = (IGPParameter) paramvalues.getElement(2);
    IGPValue outputFeaturesValue = gpUtilities.unpackGPValue(outputFeaturesParameter);
    if (gpUtilities.exists(outputFeaturesValue)) {
      messages.addMessage("\nOutput already exists. Overwriting it...");
      gpUtilities.delete(outputFeaturesValue);
    }
    String outputFC = outputFeaturesValue.getAsText();    
    messages.addMessage("Output Feature Class: " + outputFC);

    // Check if expression returns any features
    messages.addMessage("\tTesting expression " + expression + "...");
    QueryFilter deleteFilter = new QueryFilter();
    deleteFilter.setWhereClause(expression);
    int numFeaturesMatchingQuery = inputFC.rowCount(deleteFilter); 
    if(numFeaturesMatchingQuery <= 0){
      messages.addWarning("Expression " + expression
          + " did not return any features in " + inputFeatures
          + ".\n" + "No output will be generated.");      
    }
    else{
      messages.addMessage("Expression " + expression + " returned "
          + numFeaturesMatchingQuery + " feature(s).\n" + "Beginning delete operation now...");
      Workspace wsEdit = null;
      try {
        messages.addMessage("Number of features in input feature class before delete operation: "
            + inputFC.featureCount(null));
        
        // create a list of features returned by expression
        ArrayList<Integer> deleteFeaturesList = new ArrayList<Integer>(
            10);
        IFeatureCursor inputFCFeatureCursor = inputFC.search(
            deleteFilter, false);
        IFeature feature = inputFCFeatureCursor.nextFeature();
        while (feature != null) {
          deleteFeaturesList.add(feature.getOID());
          feature = inputFCFeatureCursor.nextFeature();
        }
        inputFCFeatureCursor.flush();
        inputFCFeatureCursor = null;
        feature = null;

        // create output feature class in location provided by 3rd
        // parameter
        messages.addMessage("Creating output feature class...");
        FeatureClass outputFeatureClass = createOutputFC(outputFC,
            inputFC.getFields(), inputFC.getShapeFieldName(), messages, envMgr);
        messages.addMessage("Done.");

        // start an edit session
        wsEdit = new Workspace(outputFeatureClass.getWorkspace());
        wsEdit.startEditing(false);
        wsEdit.startEditOperation();

        /*
         * Adding all features from inputFC to outputFC, except ones in the deleteFeaturesList.
         * Another way to implement this delete operation is to use the IGeoDBDataTansfer interface
         * to transfer all features from input to output and then carry a delete op on the output FC.          
         */
        inputFCFeatureCursor = inputFC.search(null, false);
        Feature inputFeature = (Feature) inputFCFeatureCursor
            .nextFeature();
        IFeatureCursor outputFCFeatureCursor = outputFeatureClass
            .IFeatureClass_insert(true);
        while (inputFeature != null) {
          if (!deleteFeaturesList.contains(inputFeature.getOID())) {
            outputFCFeatureCursor.insertFeature(inputFeature);
          }

          inputFeature = (Feature) inputFCFeatureCursor.nextFeature();
        }
        outputFCFeatureCursor.flush();

        //stop the edit session
        wsEdit.stopEditOperation();
        wsEdit.stopEditing(true);

        messages.addMessage("Number of features in output feature class - " + outputFeatureClass.getName() + ", after delete operation: "
            + outputFeatureClass.featureCount(null));

      } catch (Exception e) {
        if (wsEdit != null && wsEdit.isBeingEdited()) {
          wsEdit.stopEditing(false);
        }

        messages.addError(500, e.getMessage());
      }
    }
    
    messages.addMessage("Done.");
  }
  
  /***************************************************************************************************************
   * Util methods
   ****************************************************************************************************************/
  private FeatureClass createOutputFC(String outputFCString, IFields fields, String shapeFieldName, 
      IGPMessages messages, IGPEnvironmentManager envMgr) throws Exception {

    //extract output feature class name
    String outputFCName = outputFCString.substring(outputFCString.lastIndexOf(File.separator) + 1, outputFCString.length());
    
    //ensure output feature class is indeed provided - redundant check
    Workspace outputWS = null;
    if(outputFCString == null || outputFCString.isEmpty()){
      messages.addError(500, "No output feature class specified.");
    }
    else{
      //extract parent folder of output feature class or shapefile provided as output parameter
      File outputFCStringFile = new File(outputFCString);
      File outputWSFile = outputFCStringFile.getParentFile();
      
      //if only a file name is provided without parent folder or abolute path, use the scratch workspace as output folder
      if (outputWSFile == null) {
        String sw = envMgr.findEnvironment("scratchWorkspace").getValue().getAsText();
        if (sw != null && !sw.isEmpty()) {
          outputWSFile = new File(sw);
        } else {
          messages.addError(500, "No output folder or scratch workspace specified.");
        }
      } 

      //Now that output location is known, determine type of workspace, FileGDB or shapefile. SDE is not supported.
      String outputWSName = null;
      IWorkspaceFactory wsFactory = null;
      if (outputWSFile.exists() && outputWSFile.isDirectory()) {
        String outputWSFileName = outputWSFile.getName();
        if (outputWSFileName.endsWith(".gdb")) {
          wsFactory = new FileGDBWorkspaceFactory();
          outputWSName = outputWSFileName.substring(0, outputWSFileName.indexOf(".gdb"));
        } else {
          wsFactory = new ShapefileWorkspaceFactory();
          outputWSName = outputWSFile.getName();
        }
      }
      else{
        messages.addError(500, "Output or scratch workspace directory does not exist.");
      }
      
      //create the output workspace if it's not created yet. If the output file gdb exists, then workspace exists too. 
      if(!wsFactory.isWorkspace(outputWSFile.getAbsolutePath())){
        wsFactory.create(outputWSFile.getParentFile()
            .getAbsolutePath(), outputWSName, null, 0);  
      }      

      //retrieve the output workspace
      outputWS = new Workspace(wsFactory.openFromFile(
          outputWSFile.getAbsolutePath(), 0));
    }
    
    //create a fc with input FC's schema in output workspace
    FeatureClass outputFeatureClass = new FeatureClass(outputWS.createFeatureClass(outputFCName, fields, null, null, 
        esriFeatureType.esriFTSimple, shapeFieldName, "default"));
    
    return outputFeatureClass;
  }

  /**
   * Returns metadata file. This contains metadata for the tool and its
   * parameters
   */
  public String getMetadataFile() throws IOException, AutomationException {
    return metadataFileName;
  }

  /**
   * Returns status of license
   */
  public boolean isLicensed() throws IOException, AutomationException {
    return true;
  }
}