ThinkGeo.com    |     Documentation    |     Premium Support

Searching in Shapefiles Using SilverlightMapConnector

With the Web version of the ThinkGeo GIS components we could do a search with Sql or by using other methods. For example:


address_pointLayer.ExecuteSqlQuery("select top 1 * from address_point where ROAD_NAME like '" + streetName + "%'");


or


layer.FeatureSource.GetFeatureById(selectedValue.ToString(), new string[] { "RecID" });


How can this be done from within Silverlight when there are no shape files in the Silverlight package? We will strictly using the SilverlightMapConnector for serving up GIS to Silverlight.


If this is not possible, is it possible when using Silverlight to talk to a WMS?


 



Is there a sample that does this?

Christian, 
  
 Currently, we don’t have any sample for your requirement. Currently Microsoft Silverlight Verison doesn’t support DataTable and Sql stuffs, so we don’t support SqlQuery on the client side. But you still can use QueryTools to query features by Id and other conditions such as Spatial Query. 
  
 On the other hand, we support Sql Query on the server side, but you need to response for the communication such as using Web Service and WCF etc. I’m sure there are bunch of technical articles for this.  
  
 We are designing a way to communicate with map connector; I think it’ll be a good feature in the further version. Please keep an eye on our web site. 
  
 Any questions please let me know. 
  
 Thanks, 
 Howard

"But you still can use QueryTools to query features by Id and other conditions such as Spatial Query."


Is there a sample for this? Can you point me to some documentation please? Specifically, we need to do things like zooming to particular point, and highlighting it. Please confirm whether or not this is possible.


 


It seems the answer to the question is that the SilverlightMapConnector does not support queries on shape files. So, is the only workaround to do a query using some other method, and then pass the return value up through a web service? How do we perform an Sql search on a shape file using the existing ThinkGEO API (which ships with Silverlight) so that the results can be served up through a web service?



Christian,



Here are some our installed samples for you.



1.    Samples\MovingAroundTheMap\ZoomInToAFeatureClicked.xaml (I guess this is the sample you are looking for)

2.    Samples\Querying Feature Layer\SpatialQueryAFeatureLayer.xaml (does some spatial query with a rectangle)

3.    Samples\GettingStarted\FindFeatureClicked.xaml (highlights an area where user clicked)



SilverlightMapConnector does support queries on the shape file, but it doesn’t support transferring the data to the client side. For example, you can use any method in the QueryTools which you can find the documentation at:

gis.thinkgeo.com/mapsuite3do...mbers.html



But when the feature returns, you need to serialize it and transfer the data to the client side. That’s the part what we want to enhance currently.



Hope it helps.



Thanks,

Howard



Where can I download these samples from?

Christian,



You can download the samples mentioned above in our installed samples. But we don't have the sample to communicate with Web Service and WCF. But the code is the same as the client code. Please try to create a layer on the server side and call the following code:
Collection<Feature> features = layer.QueryTools.GetAllFeatures(ReturningColumnsType.AllColumns);


Any questions please let me know.



Thanks,

Howard



So, what you are saying is that you have no samples on how to query the shapes files on the server side, and have those results displayed in the Silverlight client? 
  
 Also, you are saying that you have no samples which allow you to search for features on the server side shape files by clicking on the client in Silverlight? 
  
 Can we please have some documentation on how to achieve this?

Christian



QueryTools for Client Query which query features from client’s machine.

gis.thinkgeo.com/mapsuite3do...Tools.html



We also have a sample for it which you can find in our installed sample under 

[Installed Path]\Samples\Querying Feature Layer\SpatialQueryAFeatureLayer.xaml

[Installed Path]\Samples\MovingAroundTheMap\ZoomInToAFeatureClicked.xaml





I guess you need to query the features from server side. Here is the documentation for server side QueryTool which you can find at:

gis.thinkgeo.com/mapsuite3do...Tools.html



In our silverlight samples we only specify little feature of our server side, and focus on our client side features. Hope you understand this is Silverlight Edition which runs on the client side.



We don’t have some APIs for communicating between client and server; because uses may like to use Web Service, WCF or Ajax for transferring so that we want to discuss more for the communication before we design to integrate it in our product. 



Here is a sample which uses Web Service to query features. Hope it helps.



Any questions please let me know.



Thanks,

Howard

 



1050-QueryFeatures.zip (27.7 KB)

Howard,


I don't really see how it will ever be possible to do searches on shape files on the Client side. This is because most customers have large shape files with a lot of data. If these shape files are in the Xap package, it will be too big. Therefore, it only really makes sense to keep the shape files on the server. Please correct me if I have misunderstood something here. However, in my opinion, the client side application should eventually have functionality to send a search to a server, and have the results returned (e.g. through WMS, or SilverlightMapConnector).


Here is my sample code for searching within a radius. Bare in mind it returns a list of Assets which is our custom class. The search gets the COMPKEY of the records in the shape file but people can alter this code to suit their own needs. This is a WCF service OperationContract which can be called from within Silverlight. You can call it from the Map's Click event using the X,Y coords.


        [OperationContract]

        public List<Asset> GetAssetsWithinDistanceOf(double x, double y, double searchRadius)

        {

            //Get controls

            var layerDictionary = new Dictionary<string, Collection<Feature>>();

            var retVal = new List<Asset>();



            //Iterate through layers

            foreach (ShapeFileFeatureLayer layer in ShapeFileFeatureLayerProvider.Layers.Values)

            {

                layer.Open();

                if (layer.QueryTools != null)

                {

                    //Get columns in layer

                    var columns = layer.QueryTools.GetColumns();



                    //Add column names to a list

                    var columnNames = new List<string>();

                    foreach (var column in columns)

                    {

                        columnNames.Add(column.ColumnName);

                    }



                    //Query the layer for features within one meter of point clicked

                    string columnNamesString = string.Join(",", columnNames.ToArray());

                    var pointString = string.Format("X: {0}\tY: {1}", x, y);



                    try

                    {



                        var pointShape = new PointShape(x, y);



                        var features = layer.QueryTools.GetFeaturesWithinDistanceOf(pointShape, GeographyUnit.Meter, DistanceUnit.Meter, searchRadius, columnNames);



                        //Add features to dictionary

                        layerDictionary.Add(layer.Name, features);



                    }

                    catch (Exception ex)

                    {

                        Logger.Log(string.Format("Error occurred when calling GetFeaturesWithinDistanceOf().\r\nLayer: {0}\r\nTarget Shape: {1}\r\nDistance: {2}\r\nReturning Column Names: {3}\r\nError:\r\n{4}", layer.Name, pointString, searchRadius, columnNamesString, ex.ToString()), Logger.MessageType.Error, this.GetType().FullName, true);

                        layer.Close();

                        throw ex;

                    }



                }



                layer.Close();

            }



            //Clear the assetlistbox items

            retVal.Clear();



            //Iterate through layers

            foreach (var layerName in layerDictionary.Keys)

            {

                //Iterate through each selected feature in the layer

                foreach (var selectedFeature in layerDictionary[layerName])

                {

                    //Check that there is a COMPKEY and UNITID

                    if (

                            selectedFeature.ColumnValues.ContainsKey("COMPKEY") &&

                            !selectedFeature.ColumnValues["COMPKEY"].Equals(DBNull.Value) &&

                            selectedFeature.ColumnValues.ContainsKey("UNITID") &&

                            !selectedFeature.ColumnValues["UNITID"].Equals(DBNull.Value) &&

                            !string.IsNullOrEmpty(selectedFeature.ColumnValues["UNITID"]) &&

                            !string.IsNullOrEmpty(selectedFeature.ColumnValues["COMPKEY"])

                        )

                    {



                        //Remove decimal places if necessary

                        var gisKeyString = (string)selectedFeature.ColumnValues["COMPKEY"];

                        if (gisKeyString.IndexOf('.') != -1)

                        {

                            gisKeyString = gisKeyString.Split(new char[] { '.' })[0];

                        }



                        var asset = BusinessAccessLayer<Asset>.GetRecord(Asset.ColumnGISKey, gisKeyString, null);

                        retVal.Add(asset);

                    }

                }

            }



            return retVal;

        }


Christian


 


 


 



Here is a sample of a WCF which returns the bounding box of a feature. It is assumed that the feature has a "RecID" column which uniquely identifies the record. It was necessary to create my own rectangle shape because the RectangleShape class in the MapSuiteCore is not marked as a DataContract. In future these classes should be marked as DataContracts so that they can be transferred over WCF. At the moment, we have to convert the coordinates manually. 
  
 namespace Adapt.Model.General 
 { 
     [DataContract] 
     public class GISPoint 
     { 
         private double _X; 
         private double _Z; 
         private double _Y; 
  
         [DataMember] 
         public double X 
         { 
             get { return _X; } 
             set { _X = value; } 
         } 
  
         [DataMember] 
         public double Y 
         { 
             get { return _Y; } 
             set { _Y = value; } 
         } 
  
         [DataMember] 
         public double Z 
         { 
             get { return _Z; } 
             set { _Z = value; } 
         } 
  
         public GISPoint() 
         { 
         } 
  
         public GISPoint(double x, double y, double z) 
         { 
             _X = x; 
             _Y = y; 
             _Z = z; 
         } 
     } 
  
     [DataContract] 
     public class GISRectangle 
     { 
         private GISPoint _LowerLeftPoint; 
         private GISPoint _LowerRightPoint; 
         private GISPoint _UpperLeftPoint; 
         private GISPoint _UpperRightPoint; 
  
         [DataMember] 
         public GISPoint LowerLeftPoint 
         { 
             get { return _LowerLeftPoint; } 
             set { _LowerLeftPoint = value; } 
         } 
  
         [DataMember] 
         public GISPoint LowerRightPoint 
         { 
             get { return _LowerRightPoint; } 
             set { _LowerRightPoint = value; } 
         } 
  
         [DataMember] 
         public GISPoint UpperLeftPoint 
         { 
             get { return _UpperLeftPoint; } 
             set { _UpperLeftPoint = value; } 
         } 
  
         [DataMember] 
         public GISPoint UpperRightPoint 
         { 
             get { return _UpperRightPoint; } 
             set { _UpperRightPoint = value; } 
         } 
  
         public GISRectangle() 
         { 
         } 
  
         public GISRectangle(GISPoint lowerLeftPoint, GISPoint lowerRightPoint, GISPoint upperLeftPoint, GISPoint upperRightPoint) 
         { 
             _LowerLeftPoint = lowerLeftPoint; 
             _LowerRightPoint = lowerRightPoint; 
             _UpperLeftPoint = upperLeftPoint; 
             _UpperRightPoint = upperRightPoint; 
         } 
     } 
 } 
  
 WCF Method: 
  
         [OperationContract] 
         public GISRectangle GetBoundingBoxOfFeature(string GISKey, string layerName) 
         { 
             var layer = ShapeFileFeatureLayerProvider.Layers[layerName]; 
  
             layer.FeatureSource.Open(); 
             var feature = layer.FeatureSource.GetFeatureById(GISKey, new string[] { "RecID" }); 
             layer.FeatureSource.Close(); 
  
             var boundingBox = feature.GetBoundingBox(); 
  
             var lowerLeftPoint = new GISPoint(boundingBox.LowerLeftPoint.X, boundingBox.LowerLeftPoint.Y, boundingBox.LowerLeftPoint.Z); 
             var lowerRightPoint = new GISPoint(boundingBox.LowerRightPoint.X, boundingBox.LowerRightPoint.Y, boundingBox.LowerRightPoint.Z); 
             var upperLeftPoint = new GISPoint(boundingBox.UpperLeftPoint.X, boundingBox.UpperLeftPoint.Y, boundingBox.UpperLeftPoint.Z); 
             var upperRightPoint = new GISPoint(boundingBox.UpperRightPoint.X, boundingBox.UpperRightPoint.Y, boundingBox.UpperRightPoint.Z); 
  
             var retVal = new GISRectangle(lowerLeftPoint, lowerRightPoint, upperLeftPoint, upperRightPoint); 
  
             return retVal; 
         } 
  
 Silverlight code (when user selects address, GISKEy is sent to WCF to get bounding box of feature): 
  
         private void theAddressSearch_AddressSelected(Address selectedAddress) 
         { 
             var addressBoxProxy = WCFFactory.GetAssetInventoryService(); 
             addressBoxProxy.GetBoundingBoxOfFeatureCompleted += (addressBoxSender, addressBoxResults) => 
             { 
                 var upperLeftPoint = new PointShape(addressBoxResults.Result.UpperLeftPoint.X, addressBoxResults.Result.UpperLeftPoint.Y); 
                 var lowerRightPoint = new PointShape(addressBoxResults.Result.LowerRightPoint.X, addressBoxResults.Result.LowerRightPoint.Y); 
                 Map1.CurrentExtent = new RectangleShape(upperLeftPoint, lowerRightPoint); 
             }; 
             addressBoxProxy.GetBoundingBoxOfFeatureAsync(selectedAddress.GISKey, "address_point"); 
         } 
  


Christian, 
  
 Querying on the client side is quite useful. For example, when someone wants to open a shape file on the client side; or someone wants to Querying features from InMemoryFeatureLayer; or someone wants to querying the features which they drawn and edited. Querying is not limited in ShapeFeatureFeatureLayer.  
  
 Map Suite Product has a general structure which means our Web Edition, Desktop Edition, Service Edition, Silverlight Edition, Routing, GeoCoder are all based on a MapSuiteCore.dll. And we want to make our product more general using such as using in .Net 2.0, .Net3.0, .Net 3.5… If the beginning is too high so that the future product will have more limits. Unfortunately, DataContract is for 3.0+, if we mark this attribute, the service edition cannot work in .Net2.0 environment anymore. 
  
 We are now planning the communication in Silverlight Edition between server and client; I think we’ll have more general and extensible methods integrated in our further version. 
  
 Hope you understand and sorry for the inconvenience. 
  
 Any questions please let me know. 
  
 Thanks, 
 Howard