Select to view content in your preferred language

Attribute Query Distinct values

5256
15
08-03-2011 08:44 AM
gabrielvazquez
Deactivated User
I am running a simple Attribute query, nearly identical to the below link.
http://help.arcgis.com/en/webapi/silverlight/samples/start.htm#AttributeQuery

However, I running the query against an incident layer that uses a statefield. So we may have multiple incidents for each state and as a result the initial combobox dropdown may have multiple states. So if there are 10 incidents in Maryland, there are 10 marylands in the combo box. Which leads me to my question.

I am trying to see if anyone else has run into this issue, and has figured out a way to provide distinct results. So instead of seeing 10 marylands in the combobox, you see only one.

Again, not sure if this has to do with the
query.Outfields.AddRange(new string[] { "(STATENAME)" });
But if anyone has anyone recommendations on using Attribute QueryTask and definining distinct feature it would be much appreciated.
0 Kudos
15 Replies
ChristopherHill
Deactivated User
If its telling you that Distinct is not there then it is probabbly the namespace needs to just be added like Jay said.


Also i though i might include the linq to microsoft linq site. I use this a lot when i want to quickly filter, sort or seach a list.
http://msdn.microsoft.com/en-us/vcsharp/aa336746
0 Kudos
JMcNeil
Deactivated User
Gabriel,
Step away from lunch I think darinatch probably solve you probable and I think he might have pointed out the flaw in my code.  Instead of .Items.Add I think you might need .ItemsSource

so it would look like:


  foreach (Graphic graphic in args.FeatureSet.Features)
                {
                    //Set Distinct
                    QueryComboBox.ItemsSource = (from g in args.FeatureSet.Features
                                                   orderby g.Attributes["STATE_NAME"].ToString()
                                                   select g.Attributes["STATE_NAME"]).Distinct();
                   
         
                }
0 Kudos
gabrielvazquez
Deactivated User
Thanks for the example and help guys. I am still having issues, but I will continue to work on it. Gonna take a step back and try to understand linq a bit more. Found a good esri blog that also helps with what I am trying to do.

http://blogs.esri.com/Dev/blogs/silverlightwpf/archive/2010/01/20/Sorting-query-results-using-LINQ.a...
0 Kudos
gabrielvazquez
Deactivated User
Thanks again for the help all. I ended going with the below example darina provided. Have the selected item then feeding into a TextFind search, and its working great. Thanks again for the help.

var states = (from g in args.FeatureSet.Features
                   select g.Attributes["STATE_NAME"]).Distinct().OrderBy(s => s);
QueryComboBox.ItemsSource = states;

Now I need to add LINQ to the list of things I need to learn.
0 Kudos
GaryBushek
Deactivated User
how are you supposed to get unique values this way (using the linq/combobox) if you can't get all the results back in the first place?  If the rest query can't implement "Distinct" then I could have 40,000 results coming back from a query.  The purpose of Distinct would be to filter this from 40,000 down to 10 for my purposes anyway.  Currently im seeing a limit of 1000 results for the query which is obviously for performance reasons.  Any help is appreciated.

Thanks
0 Kudos
TerryGiles
Frequent Contributor
While there is no direct way to do it in the Silverlight API or in REST, it is possible even if the layer you're querying has more features than the max # Features set on the service.  Starting in version 10 you can query with the option to just retreive the ObjectIDs of the features that match your query.  Since you're not returning features this way, the maximum # of features limitation is bypassed.  Below is some simple example XAML and C# code to do exactly this in a 2 step approach -

1.  Get all the object IDs from the layer.  Can be done using a query that is true for all features such as query.where = "1=1"

2.  Pull the objectIDs out in chunks of 1000 (the default max # of features for a v10 Server) and run a query that'll actually get the attribute(s) you need back & create a list of unique values from it.

I wrote this up today as it's something I've been needing to do for a user but have been putting off and to hopefuly help gbushek and others.  Also turned out to be a fun learning experience with some LINQ too..

I tested this against a layer with 34,000 + features in it.  Just getting the values from 1 field takes around 10 seconds to complete.  The code below actually grabs 2 fields (county and state) from the data to build the list and takes closer to 15-20 seconds.  Not the usual snappy response I'm used to with Silverlight but it does work.

There are of course other options, depending on your data sources - WCF service calls or using SQL to LINQ and the Entity Framework come to mind.  I'm also sure some of my code below code be more efficient but it's a start.... if anyone can speed it up, I'd love to hear about it.

Terry

XAML
  <UserControl  x:Class="ArcGISSilverlightSDK.RoutingDirections"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
 xmlns:toolkit= "clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"       
    xmlns:esri="http://schemas.esri.com/arcgis/client/2009">
      

    <Grid x:Name="LayoutRoot" Background="White">

                <Grid.Resources>
                    <esri:SimpleMarkerSymbol x:Key="FromSymbol" Size="15" Style="Circle" Color="#9900FF00" />
                    <esri:SimpleMarkerSymbol x:Key="ToSymbol" Size="15" Style="Circle" Color="#99FF0000" />
                    <esri:SimpleLineSymbol x:Key="RouteSymbol" Color="#990000FF" Width="5"/>
                    <esri:SimpleLineSymbol x:Key="SegmentSymbol" Color="#99FF0000" Width="8"/>
                </Grid.Resources>

                <Grid.RowDefinitions>
                    <!--<RowDefinition Height="30" />-->
                    <RowDefinition Height="75" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
             

        <toolkit:BusyIndicator x:Name="busy" IsBusy="False">
            <StackPanel Orientation="Horizontal" >
                <Button x:Name="btnTest" Content="pop cbox" Click="btnTest_Click" Width="75"  Height="50"   />
                <ComboBox x:Name="cbxValues" Grid.Column="2" Grid.Row="1" Width="100"  Height="50"  />
            </StackPanel>
        </toolkit:BusyIndicator>


    </Grid>
</UserControl>


C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Tasks;


namespace ArcGISSilverlightSDK
{
    public partial class RoutingDirections : UserControl
    {

        public RoutingDirections()
        {
            InitializeComponent();
        }

        private int _featcount;
        private int _featcomplete;
        private List<string> uniquevalues;

        private void btnTest_Click(object sender, RoutedEventArgs e)
        {

            System.Diagnostics.Debug.WriteLine("start " + System.DateTime.Now);
            busy.IsBusy = true;

            uniquevalues = new List<string>();

            QueryTask qt = new QueryTask("http://myserver/ArcGIS/rest/services/MyService/MapServer/0");
            qt.ExecuteCompleted += qt_ExecuteCompleted;
            qt.Failed += qt_Failed;

            Query qry = new Query();
            qry.ReturnIdsOnly = true;
            qry.Where = "1=1";  //get them all... ReturnIDsonly is not limited to the max #features set for a sevice

            qt.ExecuteAsync(qry, qt.Url);

        }

        void qt_Failed(object sender, TaskFailedEventArgs e)
        {
            MessageBox.Show(e.Error.Message);
        }

        void qt_ExecuteCompleted(object sender, QueryEventArgs e)
        {

            _featcount += e.FeatureSet.ObjectIDs.Count();

            if (_featcount == 0)
            {
                return;
            }

            int[] oids;

            QueryTask qt = new QueryTask("http://myserver/ArcGIS/rest/services/MyService/MapServer/0");
            Query qry = new Query();


            //assuming max # features returned is stilll the v10.0 default of 1000 here....
            for (int i = 0; i <= Math.Floor(_featcount / 1000); i++)
            {
                oids = e.FeatureSet.ObjectIDs.Skip(i * 1000).Take(1000).Select(x => int.Parse(x.ToString())).ToArray();

                qt = new QueryTask("http://myserver/ArcGIS/rest/services/MyService/MapServer/0");
                qt.ExecuteCompleted += fullqt_ExecuteCompleted;
                qt.Failed += qt_Failed;
                qry = new Query();

                OutFields ofld = new OutFields();
                ofld.Add("COUNTYNAME");
                ofld.Add("STATE");
                qry.OutFields = ofld;
                qry.ObjectIDs = oids;

                qt.ExecuteAsync(qry);
            }

        }

        void fullqt_ExecuteCompleted(object sender, QueryEventArgs e)
        {
            foreach (Graphic g in e.FeatureSet)
            {
                _featcomplete += 1;
                if (!uniquevalues.Contains(g.Attributes["COUNTYNAME"].ToString() + ", " + g.Attributes["STATE"].ToString()))
                {
                    uniquevalues.Add(g.Attributes["COUNTYNAME"].ToString() + ", " + g.Attributes["STATE"].ToString());
                }

            }

            if (_featcomplete == _featcount)
            {
                cbxValues.ItemsSource = uniquevalues;
                System.Diagnostics.Debug.WriteLine("done " + System.DateTime.Now);
                busy.IsBusy = false;
                cbxValues.IsDropDownOpen = true;
            }

        }

 

    }
}


0 Kudos