ThinkGeo.com    |     Documentation    |     Premium Support

How can I avoid going back to the datasource

Hello,


I have tree layers, one for my customer datapoints, one for my sales reps and one for shapes drawing.  Now,  I draw a circle area on the shapelayer using as center a sales rep data point then I issue a spatial query to extract for this area all the customers belonging to this area (customers layer). 


Because of the large number of sales reps data point I have to issue many queries then my question is :  is it possible to avoid to refresh the data from the datasource when not required and just use the data that  has been stored in the layer (customers layer)?


 


thanks a lot!


  


 


 



Hi Jean-marie, 
  
 I think I understand your requirement. 
  
 You need did query many times loop the sales reps, but each time need did spatial query for whole the customers layer, and you want enhancement the performance. 
  
 I think what you are using is ShapeFileFeatureLayer for your customer layer, so my suggestion for that is you can build a InMemoryFeatureLayer and then add all points in your csutomers layer to it, then build index for the InMemoryFeatureLayer and do spatial query in it. This layer won’t read data from disk and should be very fast. 
  
 Any question please let me know. 
  
 Regards, 
  
 Don

Hi Don, 
  
 No I do not use a shapefilefeaturelayer , I am using a datasource FeatureLayer and FeatureSourceLayer. 
  
 Should I use the same approach? 
  
 thanks a lot for your support.

Hi Jean-marie, 



What's the class type of your datasource featurelayer? 



Regards, 



Don



My featureSource: 
  
 ************************************************************** 
 using System; 
 using System.Collections.Generic; 
 using System.Collections.ObjectModel; 
  
 using System.Linq; 
 using System.Text; 
  
 using ThinkGeo.MapSuite.Core; 
 using ThinkGeo.MapSuite.DesktopEdition; 
  
 using Teradata; 
 using Teradata.Client; 
 using Teradata.Client.Provider; 
  
 using System.Data.Common; 
 using System.Data.Odbc; 
 using System.Data.OleDb; 
 using System.Data.ProviderBase; 
 using System.Data.Sql; 
 using System.Data.SqlClient; 
 using System.Data.SqlTypes; 
  
 namespace GisB20 
 { 
     class TDPointFeatureSource : FeatureSource 
     { 
         TdConnection cn; 
         TdConnectionStringBuilder cnsb = new TdConnectionStringBuilder(); 
         //private OleDbConnection connection; 
         private string _tableName; 
         private string _Where_wo; 
         private string _Where_wi; 
  
         // It is important for compatability that you always have a default constructor that 
         // takes no parameters.  This constructor will just chain to the more complex one. 
         public TDPointFeatureSource() 
             : this(string.Empty, string.Empty) 
         { } 
         // We have provided another constructor that has all of the parameters we need for the FeatureSource 
         // to work. 
         public TDPointFeatureSource(string tableName, string where) 
             : base() 
         { 
             _tableName = tableName; 
             if (where != “”) 
             { 
                 _Where_wi = " WHERE " + where; 
                 _Where_wo = " AND " + where; 
             } 
             else 
             { 
                 _Where_wi = “”; 
                 _Where_wo = “”; 
             } 
         } 
         // The next dozen lines are all of the properties we need.  It is good form to always match 
         // the parameters in your constructors with properties of the exact same name. 
  
         // Use the OpenCore to initialize your underlying data source.  The concreat Open method will ensure 
         // that if the user calls the Open method multiple times in a row that you only get one call to  
         // OpenCore. 
         protected override void OpenCore() 
         { 
             System.Diagnostics.Debug.WriteLine(“begin:TDPointFeatureSource.OpenCore”); 
             cnsb.ConnectionTimeout = 1200; 
             cnsb.Database = “DBIPAD02”; 
             cnsb.DataSource = “GEDWI”; 
             cnsb.DataSourceDnsEntries = 8; 
             cnsb.Password = “Predict02”; 
             cnsb.ReadAhead = true; 
             cnsb.ResponseBufferSize = 1024 * 1000; 
             cnsb.UserId = “A09ZMZZ”; 
             cn = new TdConnection(cnsb.ToString()); 
             cn.Open(); 
             System.Diagnostics.Debug.WriteLine(“end:TDPointFeatureSource.OpenCore”); 
  
         } 
  
         // Use the CloseCore to close your underlying data source.  It is important to note that the 
         // CloseCore is not like the Dispose on other objects.  The FeatureSource is meant to be opened 
         // and closed many times durring its lifetime.  Make sure that you clean up any objects that have  
         // unmanaged resources but do not put the object in a state that when Open is called it will fail. 
         // The concreat Close method will ensure that if the user calle the Close multiple times in  
         // a row that you only get one call to CloseCore. 
         protected override void CloseCore() 
         { 
             System.Diagnostics.Debug.WriteLine(“begin:TDPointFeatureSource.CloseCore”); 
             cn.Close(); 
             System.Diagnostics.Debug.WriteLine(“end:TDPointFeatureSource.CloseCore”); 
         } 
  
         // Here you need to query all of the features in your data source.  We use this method 
         // as the basis of nearly all other virtual methods.  For example if you choose not to 
         // override the GetCountCore then we will get all the features and count them as the default. 
         // This is ineffecient however it produces the correct results. 
         protected override Collection<Feature> GetAllFeaturesCore(IEnumerable<string> columnNames) 
         { 
             TdCommand cmd = null; 
             TdDataReader reader = null; 
             Collection<Feature> returnFeatures = new Collection<Feature>(); 
  
             System.Diagnostics.Debug.WriteLine("begin:TDPointFeatureSource.GetAllFeaturesCore " + _tableName); 
  
             // Alays be sure to wrap imporant code that accesses resources that need 
             // to be closed.  In the Finally we will ensure they always get cleaned up. 
             try 
             { 
                 // We need to construct the part of the SQL statement for retuning the 
                 // column data.  We only return the columns asked for byt the columnNames 
                 // parameter of the function.  This ensures we do not get more than we need. 
                 string columnsSQL = string.Empty; 
                 foreach (string columnName in columnNames) 
                 { 
                     columnsSQL += “,” + “&#34;” + columnName + “&#34;”; 
                 } 
  
                 if (columnsSQL.Length > 0) 
                     columnsSQL = columnsSQL.Substring(1); 
  
                 if (columnsSQL.Length == 0) 
                     columnsSQL = " TG_ID, TG_X, TG_Y "; 
  
                 if ((columnsSQL.Length > 0) && (!columnsSQL.Contains(“TG_ID”))) 
                     columnsSQL = " TG_ID, TG_X, TG_Y, " + columnsSQL; 
  
                 // Here we build up and execute the query string using the columns the users defined in the properties 
                 // such as the XColumnName and IdColumnName. 
                 cmd = cn.CreateCommand(); 
                 cmd.CommandText = "SELECT " + columnsSQL + " FROM " + _tableName + _Where_wi; 
                 cmd.Connection = cn; 
                 reader = cmd.ExecuteReader(); 
  
                 // We now loop though all of the results and build up our features that we need to return. 
                 while (reader.Read()) 
                 { 
  
                     PointShape pointShape = new PointShape(Convert.ToDouble(reader[“TG_X”]), Convert.ToDouble(reader[“TG_Y”])); 
                     pointShape.Id = reader[“TG_ID”].ToString(); 
                     Feature feature = pointShape.GetFeature(); 
  
                     // This small part populates the column values that were requested.  They are data 
                     // such as columns used for ClassBreakStyles or TextStyles for labeling. 
                     foreach (string columnName in columnNames) 
                     { 
                         feature.ColumnValues.Add(columnName, reader[columnName].ToString()); 
                     } 
                     returnFeatures.Add(feature); 
                 } 
             } 
             finally 
             { 
                 // Cleanup any of the objects that need to be closed or disposed. 
                 if (cmd != null) { cmd.Dispose(); } 
                 if (reader != null) { reader.Dispose(); } 
             } 
  
             System.Diagnostics.Debug.WriteLine(“end:TDPointFeatureSource.GetAllFeaturesCore”); 
  
             return returnFeatures; 
         } 
  
         // Though this method is not required for creating our new class it is an important one. 
         // This method is used to get only the features inside of the bounding box passed in.  The reason 
         // this is critical is that many other methods on the QueryTools such as Touches, Overlaps, Intersects, 
         // and many others use this method as a first pass filter.  If you do not override this method then 
         // the default code calls the GetAllFeatures and look at each to see if it is in the bounding box. 
         // While this method will produce the correct result it will not perform as well as your custom code. 
         protected override Collection<Feature> GetFeaturesInsideBoundingBoxCore(RectangleShape boundingBox, IEnumerable<string> returningColumnNames) 
         { 
             TdCommand cmd = null; 
             TdDataReader reader = null; 
             Collection<Feature> returnFeatures = new Collection<Feature>(); 
  
             System.Diagnostics.Debug.WriteLine("begin:TDPointFeatureSource.GetFeaturesInsideBoundingBoxCore " + _tableName); 
  
             // Alays be sure to wrap imporant code that accesses resources that need 
             // to be closed.  In the Finally we will ensure they always get cleaned up. 
             try 
             { 
                 // We need to construct the part of the SQL statement for retuning the 
                 // column data.  We only return the columns asked for byt the columnNames 
                 // parameter of the function.  This ensures we do not get more than we need. 
                 string columnsSQL = string.Empty; 
                 foreach (string columnName in returningColumnNames) 
                 { 
                     columnsSQL += “,” + “&#34;” + columnName + “&#34;”; 
                 } 
  
                 if (columnsSQL.Length > 0) 
                     columnsSQL = columnsSQL.Substring(1); 
  
                 if (columnsSQL.Length == 0) 
                     columnsSQL = " TG_ID, TG_X, TG_Y "; 
  
                 if ((columnsSQL.Length > 0) && (!columnsSQL.Contains(“TG_ID”))) 
                     columnsSQL = " TG_ID, TG_X, TG_Y, " + columnsSQL; 
  
                 // This whereSql is important becasue it is what is used to determine if the point is in 
                 // the bounding box passed in.  While it is a bit complex it get your results quickly in 
                 // large datasets so long as the X & Y columns are indexed. 
                 string whereSql = "TG_X <= " + boundingBox.LowerRightPoint.X + " AND TG_X >= " + boundingBox.UpperLeftPoint.X + " AND  TG_Y <= " + boundingBox.UpperLeftPoint.Y + " AND TG_Y >= " + boundingBox.LowerRightPoint.Y; 
                 cmd = cn.CreateCommand(); 
                 cmd.CommandText = "SELECT " + columnsSQL + " FROM " + _tableName + " WHERE " + whereSql + " " + _Where_wo; 
                 cmd.Connection = cn; 
                 reader = cmd.ExecuteReader(); 
  
                 // We now loop though all of the results and build up our features that we need to return. 
                 while (reader.Read()) 
                 { 
                     Feature feature = new Feature(Convert.ToDouble(reader[“TG_X”]), Convert.ToDouble(reader[“TG_Y”]), reader[“TG_ID”].ToString()); 
                     // This small part populates the column values that were requested.  They are data 
                     // such as columns used for ClassBreakStyles or TextStyles for labeling. 
                     foreach (string columnName in returningColumnNames) 
                     { 
                         feature.ColumnValues.Add(columnName, reader[columnName].ToString()); 
                     } 
                     returnFeatures.Add(feature); 
                 } 
             } 
             finally 
             { 
                 // Cleanup any of the objects that need to be closed or disposed. 
                 if (cmd != null) { cmd.Dispose(); } 
                 if (reader != null) { reader.Dispose(); } 
             } 
  
             System.Diagnostics.Debug.WriteLine(“end:TDPointFeatureSource.GetFeaturesInsideBoundingBoxCore”); 
             return returnFeatures; 
         } 
  
         // This method returns all of the columns in the data source.  This method is not required however 
         // if it is not overridden then the FeatureSource will not have any columns available to it. 
         // Since having access to the column data is usefull for labeling and such we suggest you override it. 
         protected override Collection<FeatureSourceColumn> GetColumnsCore() 
         { 
             TdCommand cmd = null; 
             TdDataReader reader = null; 
             Collection<FeatureSourceColumn> returnColumns = new Collection<FeatureSourceColumn>(); 
  
             System.Diagnostics.Debug.WriteLine("begin:TDPointFeatureSource.GetColumnsCore " + _tableName); 
  
             // Alays be sure to wrap imporant code that accesses resources that need 
             // to be closed.  In the Finally we will ensure they always get cleaned up. 
             try 
             { 
                 // Here we have a query that will quickly return nothing.  As strange as it sounds it is a 
                 // good way to get the table structure back without having to return any column data. 
                 cmd = cn.CreateCommand(); 
                 cmd.CommandText = “SELECT * FROM " + _tableName + " WHERE 1=2”; 
                 cmd.Connection = cn; 
                 reader = cmd.ExecuteReader(); 
  
                 // We now loop through and create our column list.  In the FeatureSourceColumn we can 
                 // optionally provide the column type but it is just informational so we didn’t code it. 
                 for (int i = 0; i < reader.FieldCount; i++) 
                 { 
                     FeatureSourceColumn featureSourceColumn = new FeatureSourceColumn(reader.GetName(i)); 
                     returnColumns.Add(featureSourceColumn); 
                 } 
             } 
             finally 
             { 
                 // Cleanup any of the objects that need to be closed or disposed. 
                 if (cmd != null) { cmd.Dispose(); } 
                 if (reader != null) { reader.Dispose(); } 
             } 
             System.Diagnostics.Debug.WriteLine(“end:TDPointFeatureSource.GetColumnsCore”); 
             return returnColumns; 
         } 
  
         // This is another method that does not need to be overridden but we suggest that you do. 
         // This method gets the cound of all the features in the data source.  If you choose not to  
         // override it then the default will call the GetAllFeatures and count them.  This is not very 
         // effecient so we suggest you override it. 
         protected override int GetCountCore() 
         { 
             TdCommand cmd = null; 
             int count = 0; 
  
             System.Diagnostics.Debug.WriteLine("begin:TDPointFeatureSource.GetCountCore " + _tableName); 
             // Alays be sure to wrap imporant code that accesses resources that need 
             // to be closed.  In the Finally we will ensure they always get cleaned up. 
             try 
             { 
                 // Here we do a standard SQL count statement and return the results. 
                 cmd = cn.CreateCommand(); 
                 cmd.CommandText = "SELECT COUNT(*) FROM " + _tableName + _Where_wi; 
                 cmd.Connection = cn; 
                 count = Convert.ToInt32(cmd.ExecuteScalar()); 
             } 
             finally 
             { 
                 // Cleanup any of the objects that need to be closed or disposed. 
                 if (cmd != null) { cmd.Dispose(); } 
             } 
  
             System.Diagnostics.Debug.WriteLine(“end:TDPointFeatureSource.GetCountCore #=” + count.ToString()); 
             return count; 
         } 
     } 
 } 
  
 ************************************************************* 
  
 My featureLayer: 
  
 ************************************************************* 
 using System; 
 using System.Collections.Generic; 
 using System.Collections.ObjectModel; 
  
 using System.Linq; 
 using System.Text; 
  
 using ThinkGeo.MapSuite.Core; 
 using ThinkGeo.MapSuite.DesktopEdition; 
  
 using Teradata; 
 using Teradata.Client; 
 using Teradata.Client.Provider; 
  
 using System.Data.Common; 
 using System.Data.Odbc; 
 using System.Data.OleDb; 
 using System.Data.ProviderBase; 
 using System.Data.Sql; 
 using System.Data.SqlClient; 
 using System.Data.SqlTypes; 
  
 namespace GisB20 
 { 
     class TDPointFeatureLayer : FeatureLayer 
     { 
         TDPointFeatureSource tdPointFeatureSource; 
  
         // It is important for compatability that you always have a default constructor that 
         // takes no parameters.  This constructor will just chain to the more complex one. 
         public TDPointFeatureLayer() 
             : this(string.Empty, string.Empty) 
         { } 
  
         // We have provided another constructor that has all of the parameters we need for the FeatureLayer 
         // to work. 
         public TDPointFeatureLayer(string tableName, string where) 
             : base() 
         { 
             // Here is where we create our FeatureSource and set the internal property on the FeatureLayer 
             tdPointFeatureSource = new TDPointFeatureSource(tableName, where); 
             this.FeatureSource = tdPointFeatureSource; 
         } 
  
         // The next dozen lines are all of the properties we need.  It is good form to always match 
         // the parameters in your constructors with properties of the exact same name. 
  
     } 
 } 


Jean-marie,  
  
 Thanks for your code. 
  
 I found you query information from database for your TDPointFeatureSource. And when you "GetFeaturesInsideBoundingBox" you did that in database side. 
  
 If you want to use exsiting feature for the spatial query, I think you need to use base.GetFeaturesInsideBoundingBoxCore(boundingBox, returningColumnNames);  in "GetFeaturesInsideBoundingBoxCore" instead of query from database. I think you can add a Field in TDPointFeatureSource class for control that. 
  
 At the same time, you still can try to get all features from database and add them to an InmemoryFeatureLayer when application launch and then did spatial query in that layer. 
  
 Regards,  
  
 Don