Select/Loop through features, one at a time

5887
10
09-06-2011 03:54 PM
MikeLouwrens
Occasional Contributor III
I am wanting to create a tool where I can click a button and it will either select the first record of a specific featurelayer, or if there is already a feature selected it will unselect that feature and select the next one.

How do I tell it to select very first record (ie lowest objectid) in the featurelayer?  Or if there is already a feature selected, how do I tell it to select the next record?

I don't think this should be too hard, I've just come up blank when trying to figure it out this morning.
Really all I'm doing is:
If nothing selected, select Row 1
else if something selected, select Row(selected)+1

I'm trying this currently in VBA, but will be moving to .Net once I've got something working.

Thanks,
Mike.
0 Kudos
10 Replies
MikeLouwrens
Occasional Contributor III
this code (from http://forums.esri.com/thread.asp?c=93&f=992&t=289076) almost does what I need, except that it seems to ignore my Definition Query (one feature class has 168 feature total, but with the definition query in place there are only 5 records I want to look at, but this code still selects from all 168 features).

EDIT: Actually it doesn't quite do what I want, as it doesn't start again if the selection clears. /EDIT

Private pFCursor As IFeatureCursor

Private Sub UIButtonControl1_Click()

      Dim pMxDoc As IMxDocument
      Dim pMap As IMap
      Dim pLayer As ILayer
      Dim pFLayer As IFeatureLayer
      Dim pFeature As IFeature

      Set pMxDoc = ThisDocument
      Set pMap = pMxDoc.FocusMap
      Set pLayer = pMap.Layer(0)
      Set pFLayer = pLayer

      If pFCursor Is Nothing Then
          Set pFCursor = pFLayer.FeatureClass.Search(Nothing, False)
      End If

      Set pFeature = pFCursor.NextFeature
      pMap.ClearSelection
     pMap.SelectFeature pLayer, pFeature

     pMxDoc.ActiveView.Refresh

End Sub
Mike.
0 Kudos
JeffreyHamblin
New Contributor III
To respect the definition query for a layer, use IFeatureLayer.Search.

Replace:
Set pFCursor = pFLayer.FeatureClass.Search(Nothing, False)

With:
Set pFCursor = pFLayer.Search(Nothing, False)
0 Kudos
NeilClemmons
Regular Contributor III
The thing to keep in mind is that the ObjectIds are not necessarily in sequential order.  If features have been deleted, then there will be gaps in the ids.  So, you can't simply +1 the OID to query for the next feature.  I would suggest keeping an array of OIDs instead of trying to query for the next feature.  The first time the button is clicked, it will initialize the array by querying for all features in the layer and adding their OIDs to the array.  Use IQueryFilterDefinition to specify an ORDER BY clause to sort on the OID field (or you can just sort the array after you've added all of the OIDs).  The first time through you will query for and select the feature whose OID is at index 0 in the array.  After that, you get the OID of the selected feature, find the index position in the array that contains that OID and then get the OID at the next index position.  Also, use IFeatureClass.GetFeature instead of using a query filter to select the individual features.  It's faster.
0 Kudos
AlexanderGray
Regular Contributor II
Hi there, Neil's method of getting the ObjectId is probably optimal if the featureclass is not being edited at the time.  If it is you can probably do a search on the IfeatureLayer with a QueryFilter where objectid > theSelectedObjectID, get the first feature in the cursor and use that id.

As far as making the selection, I suggest you use IfeatureSelection.Clear, IFeatureSelection.SelectionSet.Add(ObjectId) and Call IfeatureSelection.ChangeSelection.

if you have no query definition and you have a very large featureclass (hundreds of thousands of records), it might actually be more efficient, to find the objectID from the selection (ISelectionSet.Ids), increment it can call IfeatureClass.GetFeature, trap the exception and keep going until you find a valid feature.  Keep in mind error trapping in .Net is usually not efficient and to be avoided.  However, this might be faster than running queries on very large tables.
0 Kudos
MikeLouwrens
Occasional Contributor III
To respect the definition query for a layer, use IFeatureLayer.Search.

Replace:
Set pFCursor = pFLayer.FeatureClass.Search(Nothing, False)

With:
Set pFCursor = pFLayer.Search(Nothing, False)


I got an "Automation Error" when I tried changing this.  If I remove the definition query this works fine, but put it back and I get Automation Error again.  Leaving the 'FeatureClass' part in I don't get the Automation Error (but doesn't follow the definition query)

Mike.
0 Kudos
MikeLouwrens
Occasional Contributor III
Hi there, Neil's method of getting the ObjectId is probably optimal if the featureclass is not being edited at the time.  If it is you can probably do a search on the IfeatureLayer with a QueryFilter where objectid > theSelectedObjectID, get the first feature in the cursor and use that id.

As far as making the selection, I suggest you use IfeatureSelection.Clear, IFeatureSelection.SelectionSet.Add(ObjectId) and Call IfeatureSelection.ChangeSelection.

if you have no query definition and you have a very large featureclass (hundreds of thousands of records), it might actually be more efficient, to find the objectID from the selection (ISelectionSet.Ids), increment it can call IfeatureClass.GetFeature, trap the exception and keep going until you find a valid feature.  Keep in mind error trapping in .Net is usually not efficient and to be avoided.  However, this might be faster than running queries on very large tables.

I'm not editing the data, but I do always have a definition query on (most feature classes have 10000+ features (one has 50000), but are filtered down to approximately 200 with the definition query.  it is these remaining records that I want to step through.

Mike.
0 Kudos
JeffreyHamblin
New Contributor III
I got an "Automation Error" when I tried changing this.  If I remove the definition query this works fine, but put it back and I get Automation Error again.  Leaving the 'FeatureClass' part in I don't get the Automation Error (but doesn't follow the definition query)

Mike.



Interesting. What type of datasource are you working with? And what version of ArcMap?

Another option is to grab the definition query from the feature layer and use that to construct the QueryFilter for the Search method on the feature class. The FeatureLayer class implements ITableDefinition which has a DefinitionExpression property.

So something like this in VBA (untested, but I've used the concept in C# add-ins):


Dim pTableDefinition As ITableDefinition
Set pTableDefinition = pFLayer
Dim pQueryFilter As IQueryFilter
Set pQueryFilter = New QueryFilter
pQueryFilter.WhereClause = pTableDefinition.DefinitionExpression
pQueryFilter.SubFields = "*"
...
Set pFCursor = pFLayer.FeatureClass.Search(pQueryFilter, False)

0 Kudos
MikeLouwrens
Occasional Contributor III
Interesting. What type of datasource are you working with? And what version of ArcMap?
SDE point or line feature class, joined to SQL table, definition query on SQL table.  ArcMap 10.0 (fully patched)

I'm on leave for the weekend, so I'll have more of a play when I return on Monday.  To me the whole idea doesn't seem very complex, but in practice it's getting quite complicated.  Maybe I'm over thinking it? 🙂

Cheers,
Mike.
0 Kudos
SusanJones
Occasional Contributor II
kia Ora Mike

create yourself a selection set from the feature Layer. from here you'll be able to get a cursor of just the selection set features.


Susan

--
Susan Jones
GIS Consultant
Spatial Logic Ltd
Auckland, NEW ZEALAND

Email: sjones@spatiallogic.co.nz
http://www.spatiallogic.co.nz
0 Kudos