Pro SDK: How to get features after GetFeatures

4793
7
Jump to solution
05-10-2019 08:02 PM
TimWhiteaker
Occasional Contributor II

After I execute MapView.Active.GetFeatures(someGeometry), I get a dictionary of feature layers and OIDs in those feature layers that match the search geometry. From there, how do I get the actual features so I can read their attributes?  Do I have to assemble the OIDs into a where clause for a query filter search, or is there a more direct way such as the IFeatureClass.GetFeature method from the ArcGIS for Desktop SDK?

0 Kudos
1 Solution

Accepted Solutions
GintautasKmieliauskas
Regular Contributor

Hi Tim,

You can use Inspector (for one by one feature)

          // for each feature
          foreach (var oid in featOids)
          {
            // load the inspector with the feature
            var insp = new Inspector();
            insp.Load(faetLayer, oid);
              ....
           }

or use query like this:

               QueryFilter queryFilter = new QueryFilter { ObjectIDs = oids };

                using (RowCursor rowCursor = featLayer.Search(queryFilter))
                {
                    while (rowCursor.MoveNext())
                    {
                        using (Row row = rowCursor.Current)
                        {
                            Feature feat = row as Feature;

 

                             ....
                       }

                    }

               }

View solution in original post

7 Replies
GintautasKmieliauskas
Regular Contributor

Hi Tim,

You can use Inspector (for one by one feature)

          // for each feature
          foreach (var oid in featOids)
          {
            // load the inspector with the feature
            var insp = new Inspector();
            insp.Load(faetLayer, oid);
              ....
           }

or use query like this:

               QueryFilter queryFilter = new QueryFilter { ObjectIDs = oids };

                using (RowCursor rowCursor = featLayer.Search(queryFilter))
                {
                    while (rowCursor.MoveNext())
                    {
                        using (Row row = rowCursor.Current)
                        {
                            Feature feat = row as Feature;

 

                             ....
                       }

                    }

               }

DylanHarwell
Occasional Contributor

I am having a similar issue.. I'd like the user to draw a box with a map tool, and from there parse the attribute table for a specific field. How exactly do you use the inspector or query filter with another field along with the oids returned from GetFeatures(geometry)?

I started developing this from an identify example where it loops through the features in the TOC and determines which are selected from the user input geometry. I have a set of 5 specific layers of interest and each has a specific field of interest I'd like to be able to pull attribute values from. Here is a snippet.. I'd like to insert the logic right under sb.ApendLine, and build an array of the attribute values from the selection.

// Create dictionary of feature classes and as-built field names
List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
list.Add(new KeyValuePair<string, string>("gjgislucity.DBO.SWNETG", "NTG_USER15"));             
list.Add(new KeyValuePair<string, string>("gjgislucity.DBO.SWNETMHG", "MAG_USER15"));           
list.Add(new KeyValuePair<string, string>("gjgislucity.DBO.SMVCONDTG", "CNG_USER4"));           
list.Add(new KeyValuePair<string, string>("gjgislucity.DBO.SMVSTRUCG", "SNG_USER5"));           
list.Add(new KeyValuePair<string, string>("gjgislucity.DBO.WTPIPEG", "PPG_MAP1_NO PPG_USER6"));

var mv = MapView.Active;
var selectionResult = await QueuedTask.Run(() =>
{
    // Initiate string builder for message window
    var sb = new System.Text.StringBuilder();

    // Get the features that intersect the sketch geometry
    var features = mv.GetFeatures(geometry);

    // Get all layer definitions, loop through and determine which are selected
    var lyrs = mv.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>();               
    foreach (var lyr in lyrs)
    {
        var fCnt = features.ContainsKey(lyr) ? features[lyr].Count : 0;
        if (fCnt != 0)
        {
            // If layer is selected, get it's name and decide if in list
            var fcName = lyr.GetFeatureClass().GetName();
            foreach (KeyValuePair<string, string> item in list)
            {
                if (item.Key == fcName) 
                {                               
                    total = total + fCnt;
                    sb.AppendLine($@"{fCnt} {(fCnt == 1 ? "record" : "records")} selected for {lyr.Name}");                               
                }                               
            }
        }
    }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
GintautasKmieliauskas
Regular Contributor

Hi Dylan,

At first instead of List kvp in features)

{

string fieldName;

if(list.TryGetValue(kvp.Key.Name, out fieldName))

{

long fCnt = kvp.Value.Count;

total = total + fCnt;

sb.AppendLine($@"{fCnt} {(fCnt == 1 ? "record" : "records")} selected for {kvp.Key.Name}");

QueryFilter queryFilter = new QueryFilter { ObjectIDs = kvp.Value };

using (RowCursor rowCursor = kvp.Key.Search(queryFilter))

{

while (rowCursor.MoveNext())

{

using (Row row = rowCursor.Current)

{

string attributeValue = Convert.ToString(row[fieldName]);

// Do something with attribute value

}

}

}

}

}

DylanHarwell
Occasional Contributor

Thanks for the reply, I am somewhat understanding the cursor, but I am not sure what you mean when you said 'at first instead of list kvp in features'. I am not following where this would be inserted, and if I'd still be looping through lyrs?

0 Kudos
DylanHarwell
Occasional Contributor

Solved it, thanks for your help even though I was confused about what you said initially. The cursor code was really what I needed and I ended up restructuring and looping through the GetFeatures rather than the lyrs in the TOC as the ESRI example I started off with did. Here are some snippets.

// Get the features that intersect the sketch geometry and loop through
var features = mv.GetFeatures(geometry);
foreach (KeyValuePair<BasicFeatureLayer, List<long>> fL in features)
{
	// Loop through feature class list and match to user selection
	featureName = fL.Key.GetTable().GetName();
	foreach (KeyValuePair<string, string> item in list)
	{
		if (item.Key == featureName)
		{
			// Get selection information for message box
			selected = features[fL.Key].Count;
			total = total + selected;
			sb.AppendLine($@"{selected} {(selected == 1 ? "record" : "records")} selected in {fL.Key.Name}");
			// Get as-built field(s) and selected object ids
			abFields = item.Value.Split(' ');
			var oids = fL.Value;
			// If water pipes are selected loop through both as-built fields and generate file paths
			if (abFields.Count() > 1)
			{
				foreach (var ab in abFields)
				{
					asbuilt = GetAttributeValues(fL.Key, ab, fL.Value);
					if (asbuilt != "")
					{
						if (ab.Contains("MAP1"))
						{
							path = docPath + asbuilt;
						}
						else
							path = notesPath + asbuilt;
						paths.Add(path);
					}
				}
			}
			// If other features are selected use single as-built field and generate file paths
			else
			{
				asbuilt = GetAttributeValues(fL.Key, abFields[0], fL.Value);     
				if (asbuilt != "")
				{
					path = docPath + asbuilt;
					paths.Add(path);
				}
			}
		}
	}   
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

// Method to use cursor and retrieve attribute values by field
public static string GetAttributeValues(BasicFeatureLayer inFL, string fieldName, List<long> inOIDs)
{
	string attValue = "";
	QueryFilter queryFilter = new QueryFilter { ObjectIDs = inOIDs };
	using (RowCursor rowCursor = inFL.Search(queryFilter))
	{
		while (rowCursor.MoveNext())
		{
			using (Row row = rowCursor.Current)
			{
				attValue = Convert.ToString(row[fieldName]);
			}
		}
	}
	return attValue; 
}
0 Kudos
GintautasKmieliauskas
Regular Contributor

Hi Dylan,

Sorry. I have replied from the mail and some part of my answer missed. I wanted to write what Dictionary is better than List in your case. For example if  you need few fields from one layer, you can build dictionary like this:
Dictionary<string, List<string>>, where first is layer name, second one is list of field names. So you will need less stuff to get the same results.

I would code in different way case than one layer has few fields. I would read all values in one RowCursor. It will be faster. My GetAttributeValues procedure would have List<string> fieldNames parameter instead of string fieldName and return type will be List<string> for each of attribute fields. Then I don't need to check fields number, have 2 branches in my code and etc.

0 Kudos
DylanHarwell
Occasional Contributor

Thanks Gintautas, I am brand new to C# and of course there will be a more elegant way than my hacked together code 

0 Kudos