In this section, we're going to discuss queries and spatial queries using local content in the Runtime geodatabase.
Not only can you search online content, but you can also search through an ArcGIS Runtime geodatabase. However, in order to accomplish this, you can't use QueryTask
or FindTask
because they require a URI to an online data source. Also, you will need to access the local geodatabase's table instead of directly accessing the layer. To get access to the table, we must first open the ArcGIS Runtime geodatabase. Let's look at an example:
var gdb = await Geodatabase.OpenAsync(this.GDB); Envelope extent = null; foreach (var table in gdb.FeatureTables) { var flayer = new FeatureLayer() { ID = table.Name, DisplayName = "Parking Meters", FeatureTable = table };
You've seen this code before. It simply opens a Runtime geodatabase (SQL Lite), and then creates a FeatureLayer
resource. Let's create some code to search for a parking meter:
Json.NET
, or take a look at the sample that came with this book called Chapter7a
.<TextBlock Name="Search" Background="#77000000" HorizontalAlignment="Center" VerticalAlignment="Top" Padding="5" Foreground="White" > <Run>Enter a parking meter ID: </Run> <TextBox Name="SearchTextBox" Width="50" Text="{Binding SearchText}"></TextBox> <Button Content="Find" Width="50" Command="{Binding SearchRelayCommand}" CommandParameter="{Binding Path=Text, ElementName=SearchTextBox}" > </Button> </TextBlock>
RelayCommand
to the MainViewModel
class:public RelayCommand<string> SearchRelayCommand { get; private set; }
RelayCommand
member:this.SearchRelayCommand = new RelayCommand<string>(Search);
public async void Search(string searchString) { FeatureLayer featureLayer = this.mapView.Map.Layers[1] as FeatureLayer; GeodatabaseFeatureTable table = featureLayer.FeatureTable as GeodatabaseFeatureTable; // Define an attribute query var filter = new Esri.ArcGISRuntime.Data.QueryFilter(); filter.WhereClause = "POST_ID = '" + searchString + "'"; // 666-13080 // Execute the query and await results IEnumerable<Feature> features = await table.QueryAsync(filter); foreach(Feature feature in features) { string address = feature.Attributes["STREETNAME"] as string; System.Diagnostics.Debug.WriteLine("Address: " + address); } }
666-13080
as the meter ID. The address will be sent to the Visual Studio Output window in this example.As you can see from this code, the process is still quite similar to using a QueryTask
class, except that we had to get the table of FeatureLayer
. We then set a QueryFilter
object using a WHERE
clause. In this particular layer, the meter's ID is called POST_ID
. It is of type string. Once the query is finished, it returns an enumerable list of features, which we iterated through to get the address in this case. One significant piece of information to be aware of is that querying the ArcGIS Runtime geodatabase requires that you follow the syntax rules of SQLite. For more information, navigate to http://www.sqlite.org/docs.html.
Let's do something a little more interesting with offline data in this section. In this section, we're going to search for all the meters within 200 meters of meter 666-13080
. To implement this, add a new method to your ViewModel
class called SearchByMeterID
. It will have the same signature as the previous searching method:
public async void SearchByMeterID(string searchString) { SimpleLineSymbol sls = new SimpleLineSymbol() { Color = System.Windows.Media.Colors.Red, Style = SimpleLineStyle.Solid, Width = 2 }; // get the layer and table FeatureLayer featureLayer = this.mapView.Map.Layers[1] as FeatureLayer; GeodatabaseFeatureTable table = featureLayer.FeatureTable as GeodatabaseFeatureTable; // Define an attribute query var filter = new Esri.ArcGISRuntime.Data.QueryFilter(); filter.WhereClause = "POST_ID = '" + searchString + "'"; // Try 666-13080 // Execute the query and await results IEnumerable<Feature> features = await table.QueryAsync(filter); // iterate the feature. Should be one in this case. foreach (Feature feature in features) { // Get the MapPoint, Project to Mercator so that we are // working in meters MapPoint mapPoint = feature.Geometry as MapPoint; MapPoint pointMercator = GeometryEngine.Project(mapPoint, SpatialReferences.WebMercator) as MapPoint; Geometry polygon = GeometryEngine.Buffer(pointMercator, 200); // Re-project the polygon to WGS84 so that we can query // against the layer which is in WGS84 Polygon polygonWgs84 = GeometryEngine.Project(polygon, SpatialReferences.Wgs84) as Polygon; // add the circle (buffer) Graphic graphic = new Graphic(); graphic.Symbol = sls; graphic.Geometry = polygonWgs84; this.graphicsLayer.Graphics.Add(graphic); // Make sure the table supports querying if (table.SupportsQuery) { // setup the query to use the polygon that's in WGS84 var query = new SpatialQueryFilter(); query.Geometry = polygonWgs84 as Geometry; query.SpatialRelationship = SpatialRelationship.Intersects; var result = await table.QueryAsync(query); // Loop through query results foreach (Esri.ArcGISRuntime.Data.Feature f in result) { // do something with results } } } }
RelayCommand
and XAML code to find this new method.666-13080
.You will see a buffer on the map around the meter found:
In this method, we've set up a SimpleLineSymbol
class so we can view the buffer, retrieved the layer and layer's table, defined a QueryFilter
object so that we can get the MapPoint
instance of a parking meter, and iterated over the parking meters. Once we have a parking meter, we re-project the parking meter's MapPoint
class from WGS84 to Mercator so that we can buffer it using a hardcoded value of 200 meters. We can't use meters with a WGS84 coordinate system because that coordinate system uses angular units. We then compute the buffer, which produces a polygon, and then we re-project the polygon back into WGS84 so that it will display correctly on the map. After that, we create a Graphic
class of the buffer and display it on the map. Then, we check to make sure that the table supports being queried. This is an optional setting when the layer was published to a ArcGIS Runtime geodatabase. Then, we set up the SpatialQueryFilter
class to use the polygon in WGS84, and set up the spatial relationship to search for any parking meter that intersects with the polygon. Lastly, we issue Query
asynchronously, and then iterate over the results.
This example shows how to perform queries on offline data, while at the same time it shows how to perform spatial analysis. Spatial analysis is the primary engine of GIS and we will return to it in a later chapter.