Deleting Selected Features

4227
5
01-19-2012 12:50 PM
LeoDonahue
Occasional Contributor III
What is the best option to delete simple features from a shapefile progammatically after a user selects them randomly from the map?

If I use a QueryFilter and IFeatureCursor to delete the selected features, then I would need to have a where clause that knows the OBJECTID of all the selected features.  No problem creating a where clause but this would seem like a rather long where clause at times.

I can also assign the feature layer selection set IDs to an IEnumIDs and then iterate over that and simply do something like:
featureClass.getFeature(featureLayer.getSelectionSet().getIDs().next()).delete();

Is there a better alternative?
0 Kudos
5 Replies
TimWhiteaker
Occasional Contributor II
There's a GP tool called DeleteRows that should only delete the selected rows.  Can you programmatically call that?
http://help.arcgis.com/en/sdk/10.0/java_ao_adf/conceptualHelp/engine/index.html#//0001000004q4000000

Or use ISelectionSet.search to get a cursor over which you can iterate and call delete().
0 Kudos
LeoDonahue
Occasional Contributor III
Sure, I can call the Geoprocessing Delete Features tool.

There are at least three different ways I've identified to delete a feature.  Which one is preferred?  Geoprocessing seems to be the most straight forward.

If you have queryFilter that can find features based on something besides the OID field, this works nice.  But if you wanted the user to interactively select features from the map, then you would have to build a where clause of all of those OIDs.

Use a queryFilter to get a specific feature ID.
                     // Create a queryFilter with where clause = to the OID of the selected feature in the map
                     queryFilter = new QueryFilter();
 
                     // ex: OBJECTID = 10
                     String strWhereClause = fc.getOIDFieldName() + " = " + featLayer.getSelectionSet().getIDs().next();
                    
                     queryFilter.setWhereClause(strWhereClause);
 
                     // Use IFeatureClass.Update to populate IFeatureCursor.
                     IFeatureCursor updateCursor = fc.IFeatureClass_update(queryFilter, false);
                     IFeature feature = null;
                     while ((feature = updateCursor.nextFeature()) != null) {
                           updateCursor.deleteFeature();
                     }
                    
                     // If the cursor is no longer needed, release it.
                     Cleaner.release(updateCursor);
                    


Iterate over the feature layer selection set.   This seems like a hack. Maybe just use a for loop on the selection set count?
                     IEnumIDs oids = (IEnumIDs) featLayer.getSelectionSet().getIDs();
                     IEnumIDs oids2 = (IEnumIDs) featLayer.getSelectionSet().getIDs();
                    
                     while((oids.next()) != -1){
                           fc.getFeature(oids2.next()).delete();
                           oids.reset();
                     }
 

Use Geoprocessing.  Selected features in a feature layer will delete, or if you pass a feature class, then this deletes all of the features in the feature class.
                     GeoProcessor gp = new GeoProcessor();
                     DeleteFeatures delete = new DeleteFeatures();
                     delete.setInFeatures(featLayer);
                     gp.execute(delete, null);
0 Kudos
TimWhiteaker
Occasional Contributor II
Instead of querying for the OIDs, you could iterate directly on the features.  Are you working in Java?  I don't work in Java, but I think the code would be something like

// Get a cursor on selected features
IFeatureCursor cursor = null;
featLayer.getSelectionSet().search(null, false, cursor);

// Delete all features in the cursor
IFeature feature = null;
while ((feature = cursor.nextFeature()) != null) {
      cursor.deleteFeature(); // Or maybe try feature.delete()
}


As for which is better, you're probably the best judge of that.  Generally I find running GP tools to be a little slower than writing my own code to do the same work.  However, using GP tools means I let ESRI handle their end of code development and maintenance, which usually makes my life a little easier.
0 Kudos
LeoDonahue
Occasional Contributor III
I am working with the Java API.

When you select features from the active view, or from the table, I'm guessing that somewhere in the background a selection is made based on ObjectID, because there isn't an acutal attribute or spatial query query being made. That is what I'm trying to reproduce (select random features, either from the feature table or from the map, and delete them).

FeatureLayer and FeatureClass have methods for deleting searched rows, which both take an IQueryFilter which means I need a "where clause".
I almost think this is the best solution so far.

   fc = (IFeatureClass) featLayer.getFeatureClass();
   
   // Create a queryFilter with where clause 
   queryFilter = new QueryFilter();
   
   // Create two enums.  One to iterate and one to get the IDs without a complex for statement
   IEnumIDs oids = (IEnumIDs) featLayer.getSelectionSet().getIDs();
   IEnumIDs oids2 = (IEnumIDs) featLayer.getSelectionSet().getIDs();
   
   int count = featLayer.getSelectionSet().getCount();
   int x = 0;  
 
   // Build a long WhereClause from all of the OBJECTIDs concatenated together.
   StringBuilder sb = new StringBuilder();
   sb.append(fc.getOIDFieldName() + " = ");
   
   while((oids.next()) != -1){
    if(x < ((count) - 1)){
     sb.append(oids2.next());
     sb.append(" OR " + fc.getOIDFieldName() + " = ");
     x++;
    } 
    else {
     sb.append(oids2.next());
    }
   }
   
   queryFilter.setWhereClause(sb.toString());
   // Use IFeatureClass.Update to populate IFeatureCursor.
   IFeatureCursor updateCursor = fc.IFeatureClass_update(queryFilter, false);
   IFeature feature = null;
   while ((feature = updateCursor.nextFeature()) != null) {
    updateCursor.deleteFeature();
   }
   
   // Clear the selection set, because deleting this way doesn't notify the table or the TOC to udpate.
   featLayer.clear();

   // optionally call updateContents on map document
   
   // If the cursor is no longer needed, release it.
   Cleaner.release(updateCursor);



FeatureClass has a getFeature method which from there I can delete the feature, if I know the ObjectID - my second code example of the three from the previous post.
This option seems to have side effects, such as:

  • if you select and delete from the active view, all selected features will delete.

  • if you select and delete from the feature layer table, not all of the features will delete.

Geoprocessing has side effects when you delete, such as:

  • the active view is auto refreshed after the delete.

  • if you pass a FeatureClass to Geoprocessor, all of the records are deleted.

  • if you pass a FeatureLayer to Geoprocessor, all of the selected feature are deleted, or all of them are deleted if you have no selection.

0 Kudos
TimWhiteaker
Occasional Contributor II
Did you try this line?
featLayer.getSelectionSet().search(null, false, cursor);

If it works, it would be fewer lines of code and might be more efficient since you aren't looping through OIDs to create a whereclause.  Your code would become:

   // Get a cursor on selected features
   IFeatureCursor cursor = null;
   featLayer.getSelectionSet().search(null, false, cursor);

   // Delete all features in the cursor
   IFeature feature = null;
   while ((feature = cursor.nextFeature()) != null) {
      cursor.deleteFeature(); // Or maybe try feature.delete()
   }
   
   // Clear the selection set, because deleting this way doesn't notify the table or the TOC to udpate.
   featLayer.clear();

   // optionally call updateContents on map document
   
   // If the cursor is no longer needed, release it.
   Cleaner.release(cursor);
0 Kudos