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