Using a related table for rendering

887
9
Jump to solution
02-08-2012 01:53 AM
samulineuvonen
New Contributor
Hello,

I've been trying to find a sample of how to use attributes from another table related to a feature class to render the features. We have the following scenario:

We have (permanent) features for which observation data is collected repeatedly. We wish provide the user with the option to view the status at a certain point in time. So, we would like to be able to visualize the features based on the observations, collected in another table and linked to the features by their OBJECTID.

I have considered using a LINQ-join and then refreshing the GraphicsSource-property of the FeatureLayer based on this query, but I do not know how to construct the query so that it results in a proper IEnumerable<Graphic>. LINQ-join seems to always need the "select new{ ... }"-statement in the end and I am not sure how to formulate this properly (or whether it can be done at all). What I've got is

FeatureLayer featLyr = mp.MainMap.Layers["Layer1"] as FeatureLayer;             var flSource = featLyr.GraphicsSource;             var newSource = from fl in flSource                             join db in this.elementListItems on (int)fl.Attributes["OBJECTID"] equals (int)db.FID                             select new { db.FID, ... };             featLyr.GraphicsSource = (IEnumerable<Graphic>)newSource;


So:
1. How should this be done?
2. Is this at all the right way to go? If not, what would be?

Any help would be greatly appreciated,
Samuli N.

ps. The table for observation data is located in a completely different database as the feature class...
0 Kudos
1 Solution

Accepted Solutions
DominiqueBroux
Esri Frequent Contributor
You can't mix the usage of GraphicsSource and of Graphics in the same GraphicsLayer. It's either GraphicsSource or Graphics but not both.
That explain's your issues 1 and 2.

Issue 3 is because you can't cast an IEnumerable to an IList (a list is enumerable but an enumerable is not always a list).

As I said in my first post instead of creating new graphics you could just add the attributes to the existing graphics.

Something like:
foreach(var pair in featLyr.Graphics.Join(this.elementListItems, fl => (int) fl.Attributes["OBJECTID"], db => (int) db.FID, (fl,db) => new {fl, db})) {     pair.fl.Attributes["Aloitusajankohta"] = pair.db.Aloitusajankohta;     pair.fl.Attributes["Nimi"] = pair.db.Nimi; }

View solution in original post

0 Kudos
9 Replies
samulineuvonen
New Contributor
To update on my futile efforts on a solution: I tried to create an enumeration of Graphics within the query

var newSource = from fl in flSource
                        join db in this.elementListItems on (int)fl.Attributes["OBJECTID"] equals (int)db.FID
                        select new Graphic()
                        { 
                             Geometry=fl.Geometry,
                             Attributes=fl.Attributes.Concat(new Dictionary<string,object>() 
                                 {
                                     {"StartDate", db.StartDate},
                                     {"ValueA", db.ValueA}
                                 }
                             )
                         };


only to have Visual Studio tell me that Attributes is read-only. Why? It shouldn't be, should it? I guess I'm pretty much stuck.

Heeelp,
Samuli
0 Kudos
JenniferNery
Esri Regular Contributor
How about this? I think you need to use l.Graphics after FeatureLayer.UpdateCompleted, where ids is List<int> that contains id's from the related table.

var graphics = from g in l.Graphics
    where ids.Contains((int)g.Attributes["OBJECTID"])
    select g;
0 Kudos
samulineuvonen
New Contributor
Thanks for your response!

However, that is not really what I was trying to ask. I probably haven't been clear enough about my problem. Let's try again:

I have no problem in getting the graphics I want (based on certain OBJECTID's). My problem is in joining attributes from another table with the graphics. I have a feature service, from which I get my feature layer. I have a database table (not within the same SDE-database as the feature class provided by the feature service) in which I have observations, each linked to the features based on the OBJECTID. What I would like to do is get certain data from this database table added into the attributes of the graphics. Based on quite a long search I'm beginning to think there are only two ways to do this:

1. Loop through each of the graphics and use Attributes.Add(). I'm only afraid this may be slow. But I guess I will try.

2. Move the table within the SDE-database and create a spatial view.

Unless someone has better ideas?

I can update this thread, if I have any progress. If I remember to...

SN
0 Kudos
DominiqueBroux
Esri Frequent Contributor
only to have Visual Studio tell me that Attributes is read-only. Why? It shouldn't be, should it? I guess I'm pretty much stuck

You can't change the Attributes property itself (read-only) but you can remove or add attributes in the dictionary.


So your code might be like :
var newSource = flSource.Join(this.elementListItems, fl => (int) fl.Attributes["OBJECTID"], db => (int) db.Key,
                                (fl, db) =>
                                {
                                    var g = new Graphic {Geometry = fl.Geometry};
                                    g.Attributes["StartDate"] = db.StartDate;
                                    g.Attributes["ValueA"] = db.ValueA;
                                    return g;
                                });


That being said I am not sure you need to created new graphics, you might just add the attributes to the existing graphics.
0 Kudos
samulineuvonen
New Contributor
Great!

Thanks a million! I will try this out as soon as I can!

Regards,
Samuli
0 Kudos
samulineuvonen
New Contributor
Dear Dominique & others,

I tried the code you provided but could not get it to work. Below is my attempt:


var flSource = featLyr.GraphicsSource;
var newSource = flSource.Join(this.elementListItems, fl => (int) fl.Attributes["OBJECTID"], db => (int) db.FID,
                                (fl, db) =>
                                {
                                    var g = new Graphic {Geometry = fl.Geometry};
                                    g.Attributes["Aloitusajankohta"] = db.Aloitusajankohta;
                                    g.Attributes["Nimi"] = db.Nimi;
                                    return g;
                                });

featLyr.GraphicsSource = (IEnumerable<Graphic>)newSource;



First problem: featLyr.GraphicsSource is null. I haven't yet investigated this further.

I continued by taking the Graphics-collection of the feature layer instead of the GraphicsSource:

var flSource = featLyr.Graphics;



Second problem: Debugger says "Graphics collection must be empty before using GraphicsSource." at

featLyr.GraphicsSource = (IEnumerable<Graphic>)newSource;



Again, I skipped this and tried to simply access the graphic list to see if I got the attributes added. Third problem: cast (IList<Graphic>)newSource failed with the following error:

"Unable to cast object of type '<JoinIterator>d__61`4[ESRI.ArcGIS.Client.Graphic,Boris2Entity.Osa_versio,System.Int32,ESRI.ArcGIS.Client.Graphic]' to type 'System.Collections.Generic.IList`1[ESRI.ArcGIS.Client.Graphic]'."

At this point I gave up for the time being, I have to return to this tomorrow. In the meanwhile if anybody has any tips on this, they will - again - be very welcome...

Regards,
samuli n.
0 Kudos
DominiqueBroux
Esri Frequent Contributor
You can't mix the usage of GraphicsSource and of Graphics in the same GraphicsLayer. It's either GraphicsSource or Graphics but not both.
That explain's your issues 1 and 2.

Issue 3 is because you can't cast an IEnumerable to an IList (a list is enumerable but an enumerable is not always a list).

As I said in my first post instead of creating new graphics you could just add the attributes to the existing graphics.

Something like:
foreach(var pair in featLyr.Graphics.Join(this.elementListItems, fl => (int) fl.Attributes["OBJECTID"], db => (int) db.FID, (fl,db) => new {fl, db})) {     pair.fl.Attributes["Aloitusajankohta"] = pair.db.Aloitusajankohta;     pair.fl.Attributes["Nimi"] = pair.db.Nimi; }
0 Kudos
samulineuvonen
New Contributor
You can't mix the usage of GraphicsSource and of Graphics in the same GraphicsLayer. It's either GraphicsSource or Graphics but not both.
That explain's your issues 1 and 2.


OK, thanks! Does it matter which I use (for rendering)? I would seem that by default Graphics is used for feature layers as it contains a collection of graphics automatically when I retrieve it.


Issue 3 is because you can't cast an IEnumerable to an IList (a list is enumerable but an enumerable is not always a list).


OK, I was a bit hasty with this. However, not a very informative error message by VS...

Thanks for all your help, I will continue my efforts as soon as I can (I have some other stuff to take care of for a couple of days). However, as I looked at one of ESRIs samples concerning rendering, I'm beginning to wonder whether I could achieve this functionality simply by looping through the features and setting the values for each one individually...

Kind regards,
samuli n.
0 Kudos
DominiqueBroux
Esri Frequent Contributor
I would seem that by default Graphics is used for feature layers as it contains a collection of graphics automatically when I retrieve it.

Right. FeatureLayer populates the Graphics collection from the result of the queries.


I'm beginning to wonder whether I could achieve this functionality simply by looping through the features and setting the values for each one individually...

It's basically what the code I gave is doing.

foreach(var pair in featLyr.Graphics.Join(this.elementListItems, fl => (int) fl.Attributes["OBJECTID"], db => (int) db.FID, (fl,db) => new {fl, db}))
{
    pair.fl.Attributes["Aloitusajankohta"] = pair.db.Aloitusajankohta;
    pair.fl.Attributes["Nimi"] = pair.db.Nimi;
}

Just using Join to hopefully enhance performances (not measured tho)
0 Kudos