Choice of esriSpatialRelEnum can slow IFeatureCursor NextFeature performance

1264
5
09-13-2011 01:53 PM
TimWhiteaker
Occasional Contributor II
Environment: Programming add-ins for ArcGIS 10.0.

Has anyone noticed performance differences when looping through features returned from a search, depending on which esriSpatialRelEnum you used?  esriSpatialRelIntersects seems to be much faster than esriSpatialRelContains.  Note that the search itself is fast, but what is slow is looping through features in the cursor returned from the search.  I didn't think esriSpatialRelEnum had anything to do with IFeatureCursor.NextFeature.

Am I doing something wrong with my setup of the spatial filter?  Is there a workaround?

To test, create an add-in with a button and attach this code to it.  Then add a polygon layer to ArcMap.  I used the example Globe data on my computer at C:\Program Files (x86)\ArcGIS\Desktop10.0\ArcGlobeData\continent.shp. Click the button and see how long it takes.

The code uses ESRI.ArcGIS.ADF, ESRI.ArcGIS.Carto, and ESRI.ArcGIS.Geodatabase.


// Get a polygon layer, the first layer in the map
IFeatureLayer layer = ArcMap.Document.FocusMap.Layer[0] as IFeatureLayer;
IFeatureClass featureClass = layer.FeatureClass;
// Use an ObjectID from your feature class to get a feature
IFeature feature = featureClass.GetFeature(1);

// Create a filter using geometry from the feature
ISpatialFilter filter = new SpatialFilterClass();
filter.Geometry = feature.ShapeCopy;
// esriSpatialRelIntersects is fast. esriSpatialRelContains is slow.
filter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;

using (ComReleaser comReleaser = new ComReleaser())
{
    IFeatureCursor cursor = featureClass.Search(filter, true);
    comReleaser.ManageLifetime(cursor);

    // Time the loop
    int counter = 0;
    DateTime d = DateTime.Now;
    while ((feature = cursor.NextFeature()) != null) // The slow part
    {
        counter++;
    }

    // Report the result
    string time = DateTime.Now.Subtract(d).TotalMilliseconds.ToString();
    string msg = counter.ToString() + " feature(s) found in " + time + " ms";
    MessageBox.Show(msg);
}
0 Kudos
5 Replies
AlexanderGray
Occasional Contributor III
Is the slow part the First call to nextfeature or every call to next feature?  My experience, after running rdbms traces on feature cursors using only attribute clauses, the database (oracle in my case) parses the query when search is called.  The query is actually executed when the first nextfeature is called.  The search was fast but it wasn't really doing the query, just preparing the cursor with the right fields and parsing the query to make sure it was valid.  The first call to nextfeature was very slow and the trace showed that is where the query was done. 
I would expect different spatial queries to have different performance since the nature of geometric operation varies.  One trick to speed things up is to look at your spatial index on the featureclass, it may need some adjustments.  It also depends on the geometry type SDE and Oracle will have SDO geometry and ST geometry, spatial queries don't perform the same for both.
0 Kudos
TimWhiteaker
Occasional Contributor II
Is the slow part the First call to nextfeature or every call to next feature? 


You're right.  The slow part is the first call to next feature. 

I tried creating new feature classes in my personal geodatabase to play with spatial indexes, appending my existing data to the new feature classes for each test.  The data are in geographic coordinates and cover the Gulf of Mexico.  I tried grid sizes of 18, 1, and 0.1, and got roughly the same poor performance each time.  Hmmmm.
0 Kudos
AlexanderGray
Occasional Contributor III
the grid size depends on the size of your features.  The spatial query typically uses the grids as a first cut to narrow down the candidate features.  A grid size too big will result in too many features being evaluated.  A grid size too small will result in too many grid cells that need to be pre-evaluated.  There are guidlines for grid sizes in the help document and ways to evaluate them.

Have you tried a file geodatabase rather than a a personal geodatabase.  Also you should compact the geodatabase if there has been a lot of editing or if the data was loaded without placing the database in load only mode.  Personal and file geodatabases have adds and deletes tables internally too.

Other factors are at play such as the complexity of the search feature.  Having a very complex feature can slow things down too.
0 Kudos
DuncanHornby
MVP Notable Contributor
Tim,

I don't program in c# so when I looked at your code I noticed something that I would not do. So this could be a red herring and may be what you are doing is simply slicker than how I program? Anyway the line that I thought unusual was:

while ((feature = cursor.NextFeature()) != null)

I don't know if this is a preference thing but I would have done (in VB):

pFeature = pCursor.NextFeature
do while not pFeature is nothing
counter = counter +1
pFeature = pCursor.NextFeature
loop

So the retrieving of the feature that you are testing is done in the loop and not on the line that is evaluating if it is nothing.

Duncan
0 Kudos
TimWhiteaker
Occasional Contributor II
agray1,

Thanks for all the guidance!  I found some tips for tuning spatial indexes at:
http://webhelp.esri.com/arcgisserver/9.3/java/index.htm#geodatabases/tuning_-450163403.htm

I followed the tips and tried the query both with and without multiple indexes, since some features are thousands of times larger than others in this dataset.  No real improvement.  File GDB didn't significantly help either.  Also tried compacting the database.

The features are mostly rectangles, and I'm searching using a rectangle from one of the feature's shapes for testing purposes.  They're in a geographic coordinate system, but projecting them didn't help.

I did find two things that helped significantly.  Exploding multipart features to single features (about 2% of the 628 features are multipart) improved performance by a factor of 4.  Opening an edit session and moving features around so that fewer of them overlapped, improved performance by a factor of 100.  Pretty much every feature overlaps another feature, like leaves in a pile.  That seems to be my biggest problem, but that's the nature of this dataset.

I probably should have included a better description of this dataset in my original post.  I didn't realize the overlapping features would be the culprit, so shame on my intuition.

Duncan,

Thanks for keeping an eye out for red herrings.  I took a double take at that C# code pattern the first time I saw it as well.  The portion in parentheses where the feature variable is being assigned the next object in the cursor, is being evaluated before comparing the result of that operation to null.  So the code runs fine, and in a test just now, it took the same time to complete as when I edited the code to match the style you use in your VB.NET programming.

For an example of ESRI using the same practice, see:
http://help.arcgis.com/en/sdk/10.0/arcobjects_net/conceptualhelp/index.html#//0001000002m1000000

Ah, the nuances of programming.  I'm still learning!
0 Kudos