Obtain attribute values for a selected feature

2090
5
Jump to solution
01-18-2019 02:35 PM
KarenRobbins
New Contributor III

There are several examples of clicking on a map and obtaining the layer name and it's feature count ... 

var lyrs = mv.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>();
foreach (var lyr in lyrs)
{
var fCnt = features.ContainsKey(lyr) ? features[lyr].Count : 0;
sb.AppendLine($@"{fCnt} {(fCnt == 1 ? "record" : "records")} for {lyr.Name}");

}

or using kvp.Key.Name, kvp.Value.Count

... but I have not found any examples of obtaining the attributes themselves ...

Maybe something similar to the old school way of ...

lfieldindex = pfeature.Fields.FindField("XYZfield")

pfeature.value(lfieldindex

I'm new programming in Pro and having a hard time finding out how to access the data for my selected feature.  In my instance it would be a single feature based off a point sketch.  Does "MapMember" play a role here?

Tags (1)
0 Kudos
1 Solution

Accepted Solutions
CharlesMacleod
Esri Regular Contributor

Hi Karen,

So somewhere along the line I am thinking you saw "Map.GetSelection()" - perhaps in a sample - and decided on that as "the" way to go. In your case, I would suggest selecting directly off the layer - "parcels" - instead. Similar to arcobjects, feature layers support both a Select and a Search method - the select will highlight the features on the map whereas search does not and applies the selection to the underlying feature class (with no visible highlight).

To retrieve the list of layers from a Pro map or scene use the method "GetLayersAsFlattenedList" which I show below.

I also encourage you to google "LINQ expressions" and read a few tutorials on it - the syntax can be a little intimidating at first - a lot of the Pro SDK samples use LINQ as it is the preferred technique for selecting items from a .NET collection. The most common LINQ methods are "Where", "FirstOrDefault", and "OfType". In the code below, I use the Pro SDK method "GetlayersAsFlattenedList()" on the Map, which I mentioned above, to return the collection of the map's layers. I then use a LINQ expression "FirstOrDefault" and that funky "lambda" syntax "lyr => lyr.Name ..." to select, from the returned list, either the first layer with a matching name of "parcels" or null (that is the purpose of "FirstOrDefault").

The final code looks like this:

  var parcels = MapView.Active.Map.GetLayersAsFlattenedList().FirstOrDefault(lyr => lyr.Name == "parcels") as FeatureLayer;
  if (parcels == null)
        return; //parcels not found

  await QueuedTask.Run(() =>
  {
    var sf = new SpatialQueryFilter()
    {
      FilterGeometry = geometry,
      SpatialRelationship = SpatialRelationship.Intersects,
      SubFields = "*"
    };

    var select = parcels.Select(sf);
    var rc = ((FeatureLayer)parcels).GetSelection().Search();
    while(rc.MoveNext())
    {
      var feature = rc.Current as Feature;
      var pin = feature["PIN"];
      //etc
      ...

To help you answer these questions:

//NEED: Populate form with "InVillage" value from Village Boundary layer
...
 //NEED: Populate form with "StreetID" from closest street segement from Streets layer

I suggest the following sample to help you with understanding how to populate a form:

DockpaneSimple sample

It will introduce you to something called MVVM and how to add properties to your dock pane view model that get displayed on your dock pane view.

Finding the nearest feature can be quite complicated. Geoprocessing has Proximity tools and spatial joins are also possible. You could also use a brute force approach progressively selecting on the street layer with an incrementally increased buffer applied to your selection geometry and then filter the "hits" for the closest feature (see ProSnippets Geometry#nearest-point-versus-nearest-vertex)

View solution in original post

5 Replies
RichRuh
Esri Regular Contributor

There is actually an indexer built-in to the Feature (and Row) class.  

If you have a Feature variable named feature, and want to read the value of the XYZfield field you can just do the following:

feature["XYZField"]

More info is in this section of the geodatabase conceptual doc.

0 Kudos
KarenRobbins
New Contributor III

Right now I only have results from a mouse click (I do not have a variable feature) ...

Can I obtain the field info via results or do I need feature?  I am a newb and not 100% sure how to instantiate feature.  Here is what I have ...

using ...
...
namespace AddMgtPro
{
 internal class ClickToolLyr : MapTool
 {
 public ClickToolLyr()
 {
 IsSketchTool = true;
 SketchType = SketchGeometryType.Point; //Sketch Geometry Type

 //To perform a interactive selection or identify in 3D or 2D, sketch must be created in screen coordinates.
 SketchOutputMode = SketchOutputMode.Map; // Map or Screen
 }

 public class Globals
 {
 public static Int32 px;
 public static Int32 py;
 UserForm1 frm = new UserForm1();
 }

 protected override Task<bool> OnSketchCompleteAsync(Geometry geometry) //Sketch Callback
 {
 return QueuedTask.Run(() =>
 {
 var mapView = MapView.Active;
 //Get all the features that intersect the sketch geometry (point). 
 var results = mapView.GetFeatures(geometry);

 MapPoint mp = (MapPoint)geometry;
 Globals.px = Convert.ToInt32(mp.X);
 Globals.py = Convert.ToInt32(mp.Y);

 //Populate form with XY values from the sketch geometry (point).
 UserForm1 frm = new UserForm1();
 frm.Tbx_XFromMap.Text = Globals.px.ToString();
 frm.Tbx_YFromMap.Text = Globals.py.ToString();

 //NEED: Populate form with "PIN" value from Parcels layer

 //NEED: Populate form with "InVillage" value from Village Boundary layer

 //NEED: Search spatially from sketch geometery to find the closest street segment
 //NEED: Populate form with "StreetID" from closest street segement from Streets layer
 
 //Show a message box reporting each layer the number of the features. (From sample script)
 MessageBox.Show(
 String.Join("\n", results.Select(kvp => String.Format("{0}: {1}", kvp.Key.Name, kvp.Value.Count()))),
 "Identify Result");

 //Show the interactive form with populated data.
 frm.ShowDialog();

 return true;
 });
 }
 }
}
0 Kudos
KarenRobbins
New Contributor III

I was able to do this:

// get the currently selected features in the map

                var selectedFeatures = mapView.Map.GetSelection();

 

                // get the first layer and its corresponding selected feature OIDs

                var firstSelectionSet = selectedFeatures.First();

 

                // create an instance of the inspector class

                var inspector = new ArcGIS.Desktop.Editing.Attributes.Inspector();

 

                // load the selected features into the inspector using a list of object IDs

                inspector.Load(firstSelectionSet.Key, firstSelectionSet.Value);

 

                //get the attribute value of the PIN

                var pscode = inspector["PIN"];

                frm.Tbx_PINfromMap.Text = pscode.ToString();

And it works because the Parcel layer/feature happens to be the first one.  But ... I will also need to do this with another feature layer, so I need to be able to tell it which feature layer I want the info from ... any help?  

0 Kudos
CharlesMacleod
Esri Regular Contributor

Hi Karen,

So somewhere along the line I am thinking you saw "Map.GetSelection()" - perhaps in a sample - and decided on that as "the" way to go. In your case, I would suggest selecting directly off the layer - "parcels" - instead. Similar to arcobjects, feature layers support both a Select and a Search method - the select will highlight the features on the map whereas search does not and applies the selection to the underlying feature class (with no visible highlight).

To retrieve the list of layers from a Pro map or scene use the method "GetLayersAsFlattenedList" which I show below.

I also encourage you to google "LINQ expressions" and read a few tutorials on it - the syntax can be a little intimidating at first - a lot of the Pro SDK samples use LINQ as it is the preferred technique for selecting items from a .NET collection. The most common LINQ methods are "Where", "FirstOrDefault", and "OfType". In the code below, I use the Pro SDK method "GetlayersAsFlattenedList()" on the Map, which I mentioned above, to return the collection of the map's layers. I then use a LINQ expression "FirstOrDefault" and that funky "lambda" syntax "lyr => lyr.Name ..." to select, from the returned list, either the first layer with a matching name of "parcels" or null (that is the purpose of "FirstOrDefault").

The final code looks like this:

  var parcels = MapView.Active.Map.GetLayersAsFlattenedList().FirstOrDefault(lyr => lyr.Name == "parcels") as FeatureLayer;
  if (parcels == null)
        return; //parcels not found

  await QueuedTask.Run(() =>
  {
    var sf = new SpatialQueryFilter()
    {
      FilterGeometry = geometry,
      SpatialRelationship = SpatialRelationship.Intersects,
      SubFields = "*"
    };

    var select = parcels.Select(sf);
    var rc = ((FeatureLayer)parcels).GetSelection().Search();
    while(rc.MoveNext())
    {
      var feature = rc.Current as Feature;
      var pin = feature["PIN"];
      //etc
      ...

To help you answer these questions:

//NEED: Populate form with "InVillage" value from Village Boundary layer
...
 //NEED: Populate form with "StreetID" from closest street segement from Streets layer

I suggest the following sample to help you with understanding how to populate a form:

DockpaneSimple sample

It will introduce you to something called MVVM and how to add properties to your dock pane view model that get displayed on your dock pane view.

Finding the nearest feature can be quite complicated. Geoprocessing has Proximity tools and spatial joins are also possible. You could also use a brute force approach progressively selecting on the street layer with an incrementally increased buffer applied to your selection geometry and then filter the "hits" for the closest feature (see ProSnippets Geometry#nearest-point-versus-nearest-vertex)

KarenRobbins
New Contributor III

Thank you so much for putting me on the right path.  (FYI - Jeremy Clark has some great resources on Lambda and LINQ that anyone needing to understand the syntax should check out!)

Also, of note, I was able to pretty easily do the Street search ... I just added a buffer to the map point and pull the first one it finds.  It is not the end of the world if it's a corner lot and it chooses the wrong one.  I account for that in my form.

var poly = GeometryEngine.Instance.Buffer(mp, 300);

I also am not using a Dockpane.  I am using a Windows Form and I've got that already put together, so no worries there.

Thanks again!

0 Kudos