ThinkGeo.com    |     Documentation    |     Premium Support

GetFeaturesByColumnValue

Hello,


I need to be able to highlight features based on a particular attribute matching various different values. For instance, I may need to highlight 300+ parcels and only have a set of ID's to highlight them by. The way I am currently doing this is by looping through my string array of ID's and having to do a GetFeaturesByColumnValue with just that one match then add that feature to a collection to highlight and then iterate to my next ID. The overhead is insane.


I am running a Core 2 Quad @ 2.66Ghz and 4GB of ram and lighting up 300 parcels in this manner takes 2 minutes +/- . To do something with a 1990's era Map Objects took about 15 seconds on a machine of that era. I am not doing much in the way of elaborate syntax.


The below represents a simple excerpt of what I am trying to do. I am imagining that behind the scenes, a connection is established to the DBF, the features are filtered down to, and then the connection is closed with the collection returned. This whole process is killing the retrieval times. How can I get around this? An overload accepting an array or collection of values to match up to would be ideal.


 


 


        For idCount As Integer = 0 To UIDs.Count - 1
            Dim valProgress As Integer = ((idCount + 1) / UIDs.Count) * 100
            backWorker.ReportProgress(valProgress)

            If backWorker.CancellationPending Then
                If layerToSearchSHP.IsOpen Then layerToSearchSHP.Close()
                Exit For
            End If

            Dim fCollection As Collection(Of Feature) = layerToSearchSHP.FeatureSource.GetFeaturesByColumnValue(UID_Field, UIDs(idCount))

            'Make sure somehow we don't have a duplicate or cross match and then add to our output collection
            For Each feat As Feature In fCollection
                If Not fCollectionToHighlight.Contains(feat) Then
                    fCollectionToHighlight.Add(feat)
                End If
            Next
        Next



Nelson,


For the API GetFeaturesByCoummnValue, it exactly did what you are thinking! For better performance, I suggest you using the SqlStatement instead of loop each feature within the GetFeaturesByCoummnValue API.
 
Following 2 ways get the same result:
 

Using the GetFeaturesByColumnValue way:


shapeFileLayer.Open();
Collection<Feature> capticalFeatures = shapeFileLayer.FeatureSource.GetFeaturesByColumnValue("TYPE", "DR");
shapeFileLayer.Close();

 

Using the SqlStatement way:


string sqlStatement = "Select * From CanadaStreets where TYPE='DR'";
shapeFileLayer.Open();
DataTable dataTable = shapeFileLayer.FeatureSource.ExecuteQuery(sqlStatement);
shapeFileLayer.Close();

 

After this, you can get any information from the returned DataTable.
 
Any more quetions just let me know.
 
Thanks.
 
Yale


Yale,  
  
 I’m confused. I want to filter for the feature, not necessarily the attribute data. The intention is to highlight all of the parcels in a given array of values that match a particular column. So, if my column is LOTS_ and I have an array of 3 values of {23, 56, 40} I want to highlight them all. Obviously this is a very small example and with only 3 parcels the performance is not a huge issue. 
  
 So, my problem I think is that I have to use GetFeaturesByColumnValue as it will return the feature object to me and then must move to my next value from the array and use GetFeaturesByColumnValue again and again and again until I have processed my entire array. In itself this might not be so bad except I am opening and closing a connection for every executiong of GetFeatureByColumnValue. This is killing my performance, if that is indeed true.  
  
 So, I would like a way to establish the connection once, then retrieve features for every value in the array that matches against the LOTS_ and put them in the collection and finally close the connection.  
  
 Does this make sense? Is this possible?

Nelson,


Thanks for your post!
 
I am sorry I did not make myself clear enough for you! Your requirement is very clear and makes sense.
 
 
Unfortunately, the component did not have this API directly did what you are trying to achieve, following provided 2 ways will work for you, one way is still using SqlStatement way, the other is to read all the data into memory and filter it out one by one:
 
Using SqlStatement way:

private DataTable UsingSqlStatement(Collection<string> columns, Collection<string> matchValues)
        {
            string shapeFile = @"C:\temp\LOTS\LOTS.shp";

            ShapeFileFeatureLayer shapeFileLayer = new ShapeFileFeatureLayer(shapeFile);
            string sqlStatement = "Select * From LOTS";

            StringBuilder sb = new StringBuilder();
            sb.Append(sqlStatement);
            if (columns.Count > 0 && columns.Count == matchValues.Count)
            {
                sb.Append(" where ");
            }

            for (int i = 0; i < columns.Count; i++)
            {
                sb.Append(string.Format("[{0}]='{1}'",columns[i],matchValues[i]));

                if (i != columns.Count - 1)
                {
                    sb.Append(" OR ");
                }
            }

            string finalSqlStatement = sb.ToString();

            shapeFileLayer.Open();
            DataTable dataTable = shapeFileLayer.FeatureSource.ExecuteQuery(finalSqlStatement);
            int rows = dataTable.Rows.Count;
            shapeFileLayer.Close();

            return dataTable;
        }

 

Using Filter way:


private Collection<Feature> UsingGetAllFeatures(Collection<string> columns, Collection<string> matchValues)
        {
            string shapeFile = @"C:\temp\LOTS\LOTS.shp";

            ShapeFileFeatureLayer shapeFileLayer = new ShapeFileFeatureLayer(shapeFile);
            shapeFileLayer.Open();
            Collection<Feature> allFeatures = shapeFileLayer.QueryTools.GetAllFeatures(columns);
            shapeFileLayer.Close();

            Collection<Feature> returnFeatures = new Collection<Feature>();
            foreach (Feature feature in allFeatures)
            {
                for(int i = 0 ; i < columns.Count ; i++)
                {
                    if(string.Equals( feature.ColumnValues[columns<i>],matchValues<i>))
                    {
                        returnFeatures.Add(feature);
                        break;
                    }
                }
            }

            return returnFeatures;
        }

 

Let me know if you still have any more questions.
 
Thanks.
 
Yale 


When altered a bit, the second example provided works nicely. Thanks, Yale.

Nelson, You are always welcome! 
  
 Let me know if any more questions! 
  
 Thanks. 
  
 Yale

If we ran a query over the shape file and it returns for us a DataTable.  
  
 What’s the best way to convert the DataTable to Collection<Feature>? 
  
 the reason that I want to do this conversion is  that I want to show the result of query on the map control (I mean changing the color of the result on the map).

 


Ben,
 
Thanks for your post. 
 
I think the data table and a collection of features are different things. They can be converted to each other only if a data row can represent a feature. Normally, the data table information will not contain the shape information which is very important part of the feature.
 
Following post shows you how to convert Features to DataTable:
gis.thinkgeo.com/Support/DiscussionForums/tabid/143/aff/21/aft/5881/afv/topic/Default.aspx
gis.thinkgeo.com/Support/DiscussionForums/tabid/143/aff/21/aft/5714/afv/topic/Default.aspx
 
Following post shows you how to convert DataTable to Features:
gis.thinkgeo.com/Support/DiscussionForums/tabid/143/aff/21/aft/5411/afv/topic/Default.aspx
 
Any more questions just feel free to let me know.
 
Thanks.
 
Yale

It seems that when we want to convert the DataTable to Feature we need to now the destination layer type (Polygon and …). 
 is there any simple way to convert datatable to feature like the way we convert feature to datatable?

Ben, 
  
 Thanks for your response. 
  
 I am not sure why the destination alyer type should be known when converted. 
  
 A feature should correspond to a row in the DataTable. The WKB or WKT in the row forms the baseshape in the feature, other informations forms the column values in the feature. 
  
 A DataTable should be coresponding to a collecion of features, the number of the features equals to the number of rows in the DataTable. 
  
 Any more questions just feel free to let us know. 
  
 Thanks. 
  
 Yale 


Many thanks for your attention,


I write the below code to convert the DataTable to Feature


 



            DataTable dt =
                ((ShapeFileFeatureLayer)wpfMap1.FindFeatureLayer("Cardiff_Roads_polyline")).
                        QueryTools.ExecuteQuery(" Select top 10 * from Cardiff_Roads_polyline where fcode=3002  ");
            Collection<Feature> returnFeatures = new Collection<Feature>();
            try
            {
                foreach (DataRow dr in dt.Rows)
                {
                    Feature feature = new Feature();

                    foreach (DataColumn dc in dt.Columns)
                    {
                        if (!dr.IsNull(dc))
                        {
                            feature.ColumnValues.Add(dc.ColumnName, Convert.ToString(dr[dc]));
                        }
                    }

                    returnFeatures.Add(feature);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
            return returnFeatures;

but when it come to feature.ColumnValues.Add(dc.ColumnName, Convert.ToString(dr[dc])); it returns "object reference null error ",  what's the problem you think?



Ben, 
  
 Thanks for your post and input. 
  
 I think the problem is probably exactly the same with the problem describled in following post: 
 gis.thinkgeo.com/Support/DiscussionForums/tabid/143/aff/21/aft/7559/afv/topic/Default.aspx#17162 
  
 Try to firgure out the rows in the DataTatable dt is exactly the rows we want before we convert it into features. 
  
 Any more questions just feel free to let me know. 
  
 Thanks. 
  
 Yale 


Yale, Many thanks for your answers,


I think the problem is about     feature.ColumnValues.Add(     


I wrote the below sample:



Feature feature = new Feature();
feature.ColumnValues.Add("Name","12");

but it return error, Would you please descripe for me How I should use  
feature.ColumnValues.Add(
  

many thanks, in advance.

 



Ben,


Thanks for your post and questions.


The thing is that the feature is structure and the shape is a class, so when you create a feature, it is always attached a shape. So, please pass in a vertex if it is point or the WKT or WKB for the shape in the feature. Like following simple way:



Feature feature = new Feature(0.0,0.0);

feature.ColumnValues.Add("TestColumn1", "TestValue1");
feature.ColumnValues.Add("TestColumn2", "TestValue2");
feature.ColumnValues.Add("TestColumn3", "TestValue3");
feature.ColumnValues.Add("TestColumn4", "TestValue4");

Any more questions just feel free to let me know.


Thanks.


Yale