Click or drag to resize

Location Search Tutorial

Verizon Connect Logo
Print this page
Learn more about Verizon Connect GeoBase.
Get information about the latest release
Overview

The LocationSearch class enables you to search for locations across different sources, such as geocoders, POI sources or custom data sources.

This feature can be used to search for street addresses, LatLon locations, regions, POI types (for example 'petrol', 'pizza' or 'italian food'), named POIs, or even custom terms that you define yourself; all from a single unified search interface.

In this tutorial, we will create a Windows Forms application with a map, a drop-down list of POI types, a search box for addresses or query strings, and a results list. You can select a POI type from the list and either enter the address or coordinates into the search box, or double click on the map to select a location. Alternatively you can enter the query string straight into the search box. The resulting location suggestions will be displayed on the map as PushPins and in the results list with their address. For locations selected from the map, the distance from the search location will also be displayed.

The complete code for the project example created in this tutorial is included at the bottom of this page.

Note Note

The LocationSearch requires requires GBFS files that are compatible with the Autocomplete Geocoder. If you are using files from Q3 2014 onwards, the files will have an uppercase A among the letters immediately prior to the .gbfs extension, for example HERE_USAWestUSA_15Q4_30x151208-1141_CaIAZ.gbfs. If you are using files from before Q3 2014, these files will have an _acgc suffix immediately prior to the .gbfs extension, for example HERE_USAWestUSA_14Q1_acgc.gbfs. If you do not have the appropriate files, contact GeoBase Support via gbsupport@verizonconnect.com to arrange a trial.

Creating the Application

Create a new Visual Studio Project (a Windows Forms Application), targeting .NET Framework 4.0 or higher. Name it 'LocationSearch'.

Add a reference to geobase.net.dll. Then, to make the code simpler and more readable, add the following using directives to the top of the project form (Form1.cs).

C#
using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Telogis.GeoBase;
using Telogis.GeoBase.ImageUtils;
using Telogis.GeoBase.Geocoding;
using Telogis.GeoBase.Repositories;

Return to the 'Design' view (Shift+F7) and add the following controls to the form:

  • A MapCtrl named mapMain - this will display the location of suggested results as numbered PushPins (1 being the best result, 2 second, and so on).
  • A ComboBox named poiTypes - this will be used to contain a string list of POI (point of interest) types. In the Properties for the ComboBox under Items add POI types such as "Petrol", "Hotels", "Restaurants". Also add "None" to the top of the list and set the ComboBox property Text to "None".
  • A TextBox named QueryBox - this is where we will enter the addresses, LatLon coordinates, or entire search query strings we want to search for.
  • A TextBox named ResultsBox - this will display the address suggestions. For locations selected from the map it will also display the distance from the address suggestion to the selected location. ResultsBox should be configured with its ScrollBars property set to 'Vertical' and placed to the right of the form.
  • Two buttons named buttonSearch with the label text 'Search' and buttonReset with the label text 'Reset' - these will be used to execute the LocationSearch, and to reset the form, respectively.

When run, your new application should appear similar to the screenshot below:

locationsearch

Go back to the 'Design' view (Shift+F7). Using the Events view on the Properties panel, double click to create handler stubs for the following events:

  • The Click event of buttonReset.
  • The MouseClick event of mapMain.
  • The TextChanged event of QueryBox.
  • The Click event of buttonSearch.

Next, return to 'Code' view (F7), and add the following code to the top of the project such that the items are global (immediately above the Form1 constructor).

This code will:

  • Create a render list to display the map and PushPins.
  • Create an empty LatLon variable that will later be used to track the last location double-clicked on the map.
  • Create a string constant that contains the instruction text.

C#
// Create rendererlist
RendererList renderList = new RendererList();
private LatLon lastClickLocation = LatLon.Empty;
private const string instructionText = "Select a POI type and either enter an address/location or double-click a location on the map. Alternatively enter a full search query. Then click the Search button.";

Update the Form1 constructor method with the following code. It will:

  • Set the initial state of the form when first loaded.
  • Set the map repository.
  • Specify the default mouse drag behavior (allowing the map to be dragged, and zoomed in and out of with the mouse scroll-wheel).
  • Set the text instructions that will be shown in the ResultsBox when first loaded.
  • Set the initial map center, zoom level, and renderer.
  • Add a new custom search source to our Location Search.

C#
public Form1() {
    InitializeComponent();

    // Create a repository to point to your map data.
    SimpleRepository repository = new SimpleRepository(@"\PATH\TO\MAP\DATA_acgc.gbfs");
    Repository.CurrentThreadRepository = repository;

    // Carry out some housekeeping tasks to configure our form.
    mapMain.DragBehavior = DragBehavior.Hand;
    QueryBox.BackColor = Color.White;
    ResultsBox.Text = instructionText;
    mapMain.Center = new LatLon(33.879692, -118.029161);
    mapMain.Zoom = 50;
    mapMain.UiEnable = true;
    mapMain.Renderer = renderList;

    // Add a new custom search source to our Location Search.
    LocationSearch.AddSearchSource(new TelogisCustomSource());
}

Update the buttonReset_Click method that was added automatically when you double-clicked the events for the UI controls from the Design view. This code is executed when the 'Reset' button (buttonReset) is pressed, and will:

  • Invalidate (clear and redraw) the map, then reset the map's center location and zoom level.
  • Clear the renderlist of any PushPins added to the renderlist after any previous location searches.
  • Set the ResultsBox text field to its initial state (containing instructions and removing any result suggestions) and clear the QueryBox field (removing any string Query args).

C#
private void buttonReset_Click(object sender, EventArgs e) {
    renderList.Clear();

    // Clear the results and query text fields.
    ResultsBox.Text = instructionText;
    QueryBox.Clear();

    // Set the POI Type back to 'None'.
    poiTypes.Text = "None";

    // Reset the map center location and zoom levels.
    mapMain.Center = new LatLon(33.879692, -118.029161);
    mapMain.Zoom = 50;

    // Reset the map to its initial state when the Reset button is clicked.
    mapMain.Invalidate();
}

Then update the mapMain_MouseClick method. This code is executed when the mouse is double-clicked on a map location. It will:

  • Convert the X and Y coordinates of the clicked map position into a LatLon coordinate then reverse geocode the location to the nearest street address.
  • If a valid address is found, add it to the QueryBox text field.
  • Create a BalloonPushPin, set it to the located street address and add it to the renderlist, so that it is visible, and then set the form's focus to the map (so the user can immediately pan and zoom on the map).

C#
void mapMain_MouseClick(object sender, MouseEventArgs e) {
    // Get the map location when the map is double-clicked and convert to a LatLon.
    LatLon mapLocation = mapMain.XYtoLatLon(e.X, e.Y);
    // Reverse geocode the address under the mouse double-click.
    Address address = GeoCoder.ReverseGeoCode(mapLocation);
    // If a valid address is found, display it in the query textbox.
    if (address != null) {
        QueryBox.Text = address.ToString();
    }
    // Update the last click location, for our address BalloonPushPin.
    lastClickLocation = mapLocation;
    // Create and add the BalloonPushPin to the map.
    BalloonPushPin bpp = new BalloonPushPin(lastClickLocation);
    renderList.Add(bpp);
    mapMain.Renderer = bpp;
    mapMain.Invalidate();
    mapMain.Focus();
}

Next, update the QueryBox_TextChanged method with the code below. This code is executed when the text content of the QueryBox field changes.

C#
private void QueryBox_TextChanged(object sender, EventArgs e) {
    // Check whether the text in the query text field has changed. If it
    // has, clear the last click location and clear the map of pins.
    lastClickLocation = LatLon.Empty;
    renderList.Clear();
}

Next, paste the following code into the buttonSearch_Click method. This code is executed when the 'Search' button is clicked, contains the Location Search operations and will:

  • Retrieve the selected POI type (if applicable) and location (from the QueryBox) and combine them into a query string.
  • Construct the Location Search arguments.
  • Perform a Location Search with the constructed arguments.
  • Retrieve the results of the Location Search.
  • Loop through the search results and for each search result add the description to a result string, expand the BoundingBox to include the result location and create a push pin with the correct number, depending on how good the resulting match is. For locations selected from the map, also work out the distance from the clicked location to the location of the search result.
  • Write the list of search result descriptions into the ResultsBox.
  • Zoom to the area on the map that is described by the BoundingBox.

C#
private void buttonSearch_Click(object sender, EventArgs e) {
    // Get the POI type selected in the drop-down list
    String poiType = poiTypes.Text;
    // Get the address or query string. 
    String queryString = QueryBox.Text;
    // Is there text in the query box?
    if (queryString != "") {
        // Is there a POI type selected? If not then we will use the query string as it is. The 
        // query string may already contain a query consisting of the POI name and a string like "near"
        // or "in".
        if (poiType != "None"){
            // Combine the POI type and the address, adding 'near' to the string. 
            queryString = poiType + " near " + queryString;
        }
        // Construct the Location Search arguments
        var args = new LocationSearchArgs {
            Query = queryString,
            // Specifying the country speeds the search process. When not supplied,
            // all countries will be searched.
            Countries = new Country[] { Country.USA },
            // Set a location hint for the center of the map. This is
            // used to bias the results toward the area currently visible on the map.
            LocationHint = mapMain.Center
        };
        // Supply the Location Search search args.
        var search = new LocationSearch(args);
        // Then retrieve the results.
        LocationSearchResult output = search.Search();
        // Begin constructing the text that will be shown in the results field.
        StringBuilder locationResults = new StringBuilder("\r\nResults for Location Search '");
        locationResults.AppendFormat(queryString + "' \r\n\r\n");
        // Check that there are some results returned.
        if (output.Suggestions.Length > 0) {
            // Clear the renderlist to prepare for result PushPins
            renderList.Clear();
            // Add back the address used in the Location Search.
            if (lastClickLocation != LatLon.Empty) {
                BalloonPushPin bpp = new BalloonPushPin(lastClickLocation);
                renderList.Add(bpp);
            }
            // Set a counter to number the results, and to get the correct icons
            // to use for the PushPins. These will be used to number the results in order from
            // best to worst match.
            int counter = 1;
            int pincounter = Icons.Number1;
            // Create a new BoundingBox. We will add each result location to this, then
            // zoom the map to encompass the BoundingBox
            BoundingBox searchArea = new BoundingBox();

            // Begin the loop through the Location Search results.
            for (int idx = 0; idx < output.Suggestions.Length; idx++) {
                // Append the result description.
                locationResults.AppendFormat(counter + ": " + output.Suggestions[idx].DisplayText);
                // Then, if the last clicked location and the suggested location aren't empty...
                if (lastClickLocation != LatLon.Empty && output.Suggestions[idx].Location != LatLon.Empty) {
                    // Work out the distance between the result location and the clicked location, and convert it to miles.
                    // Also getting rid of any unnecessary decimal points.
                    decimal distShort = (decimal)(Math.Truncate((double)lastClickLocation.QuickDistanceTo(output.Suggestions[idx].Location, DistanceUnit.MILES) * 100.0) / 100.0);
                    // Add the distance to the string.
                    locationResults.AppendFormat(" (distance " + distShort + " miles)");
                }
                // Add some newlines at the end of the result string.
                locationResults.AppendFormat("\r\n\r\n");
                // Then add a simple PushPin at the result location.
                PushPin pin = new PushPin(output.Suggestions[idx].Location);
                // And expand the BoundingBox with the result location.
                searchArea.Add(output.Suggestions[idx].Location);
                // Then set the PushPin icon to a number from 1-20 (20 is the default number of results returned
                // from a Location Search operation).
                pin.Icon = pincounter;
                // Add the pin to the renderlist and increment the two counters by one.
                renderList.Add(pin);
                counter++;
                pincounter++;
                // Loop back to the top and repeat for the next result, if any.
            }
            // Convert the results StringBuilder text into a string, then add this to the results field.
            ResultsBox.Text = locationResults.ToString();
            mapMain.Renderer = renderList;
            searchArea.Inflate(0.001);
            // Zoom the map to the results BoundingBox.
            mapMain.ZoomToBoundingBox(searchArea, 100);
            mapMain.Invalidate();
            // Change focus to the map, so the user can pan and zoom.
            mapMain.Focus();

        } else {
            // If the Location Search completed without any results found, display this in the results field.
            ResultsBox.Text = "No results found";
        };

    } else {
        // Display an alert message if the search button is clicked without providing text in the query field.
        MessageBox.Show("Double-click a location on the map, or enter an address/location. Alternatively enter your own search query (for example 'hotels in los angeles'). Then click the Search button.");
    }
}

Next, paste the following TelogisCustomSource class. This code will define a custom search source that will be used by the Location Search engine in conjunction with built-in sources to modify search results. Location Search can be used without any custom search sources, but you can use them to provide new results or adjust existing results according to your own requirements. This custom source was added in the constructor for Form1 above.

C#
public class TelogisCustomSource : LocationSearchSource {

    // Override the CreateSearchOperation method, which creates the operation to perform 
    // the searching with this search source.
    public override LocationSearchOperation CreateSearchOperation(LocationSearchArgs args) {
        return new TelogisSearchOperation(args);
    }

    // Override the HandledResultType for this location search with an
    // IndividualPointOfInterest result type. It describes the type 
    // of results that this source returns (in this case, specifically 
    // named points of interest), which the Location Search engine uses 
    // to determine whether the search source is relevant for a particular search.
    public override LocationSearch.ResultType HandledResultTypes {
        get {
            return LocationSearch.ResultType.IndividualPointOfInterest;
        }
    }

    // Override the PriorityForSearch method with a priority that is higher than
    // the DefaultPoiPriority, so that this search source is used first.
    public override int PriorityForSearch(LocationSearchArgs args) {
        return LocationSearchSource.DefaultPoiPriority + 1;
    }

    // Override the GetIgnoredTypesForMatchingSearches method with an 
    // IndividualPointOfInterest result type. When this source finds 
    // results, the Location Search engine will ignore other search 
    // sources with a lower priority that find IndividualPointOfInterest results.
    public override LocationSearch.ResultType GetIgnoredTypesForMatchingSearches(LocationSearchArgs args) {
        return LocationSearch.ResultType.IndividualPointOfInterest;
    }

    // Override the name of this search source.
    public override string Name {
        get {
            return string.Format(@"TelogisCustomSource");
        }
    }
}

Finally paste the following TelogisSearchOperation class. This code will return a specific example location (i.e. the location of the Telogis headquarters) when the search query contains a prefix of "Telogis", for example "Telo".

C#
public class TelogisSearchOperation : LocationSearchOperation {
    // The arguments to use for this location search.
    private LocationSearchArgs _args;

    // Constructor for this LocationSearchOperation
    public TelogisSearchOperation(LocationSearchArgs args) {
        _args = args;
    }

    // Override the FindResults method with a specific example.
    public override LocationSearchResult FindResults() {
        LocationSearchSuggestion[] suggestions = new LocationSearchSuggestion[0];
        string lowerCaseQuery = _args.Query.Trim().ToLower();
        if ("telogis".StartsWith(lowerCaseQuery)) {
            suggestions = new LocationSearchSuggestion[] {
                new LocationSearchSuggestion {
                    ResultType = LocationSearch.ResultType.IndividualPointOfInterest,
                    Location = new LatLon(33.583906, -117.731399),
                    Confidence = 0.6 + 0.4 * lowerCaseQuery.Length / "telogis".Length,
                    StreetNumber = 20,
                    StreetName = "Enterprise",
                    Regions = new string[] {"Aliso Viejo", "Orange", "California"},
                    PostCode = "92656",
                    Country = Country.USA,
                    Name = "Telogis Headquarters",
                    DisplayText = "Telogis Headquarters, 20 Enterprise, Aliso Viejo, California 92656"
                }
            };
        }
        return new LocationSearchResult() {
            Suggestions = suggestions,
            Errors = new string[0],
            Status = SearchResult.SearchCompleted
        };
    }

    // Override the TryCancel method, immediately returning true to indicate cancellation 
    // was successful, because this operation takes a negligible amount of time to run.
    public override bool TryCancel() {
        return true;
    }
}
Complete Code

The complete code for this example project is included below:

C#
using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Telogis.GeoBase;
using Telogis.GeoBase.ImageUtils;
using Telogis.GeoBase.Geocoding;
using Telogis.GeoBase.Repositories;

namespace LocationSearchMap {

    public partial class Form1 : Form {
        // Create rendererlist
        RendererList renderList = new RendererList();
        private LatLon lastClickLocation = LatLon.Empty;
        private const string instructionText = "Select a POI type and either enter an address/location or double-click a location on the map. Alternatively enter a full search query. Then click the Search button.";

        public Form1() {
            InitializeComponent();

            // Create a repository to point to your map data.
            SimpleRepository repository = new SimpleRepository(@"\PATH\TO\MAP\DATA_acgc.gbfs");
            Repository.CurrentThreadRepository = repository;

            // Carry out some housekeeping tasks to configure our form.
            mapMain.DragBehavior = DragBehavior.Hand;
            QueryBox.BackColor = Color.White;
            ResultsBox.Text = instructionText;
            mapMain.Center = new LatLon(33.879692, -118.029161);
            mapMain.Zoom = 50;
            mapMain.UiEnable = true;
            mapMain.Renderer = renderList;

            // Add a new custom search source to our Location Search.
            LocationSearch.AddSearchSource(new TelogisCustomSource());
        }

        private void buttonReset_Click(object sender, EventArgs e) {
            renderList.Clear();

            // Clear the results and query text fields.
            ResultsBox.Text = instructionText;
            QueryBox.Clear();

            // Set the POI Type back to 'None'.
            poiTypes.Text = "None";

            // Reset the map center location and zoom levels.
            mapMain.Center = new LatLon(33.879692, -118.029161);
            mapMain.Zoom = 50;

            // Reset the map to its initial state when the Reset button is clicked.
            mapMain.Invalidate();
        }

        void mapMain_MouseClick(object sender, MouseEventArgs e) {
            // Get the map location when the map is double-clicked and convert to a LatLon.
            LatLon mapLocation = mapMain.XYtoLatLon(e.X, e.Y);
            // Reverse geocode the address under the mouse double-click.
            Address address = GeoCoder.ReverseGeoCode(mapLocation);
            // If a valid address is found, display it in the query textbox.
            if (address != null) {
                QueryBox.Text = address.ToString();
            }
            // Update the last click location, for our address BalloonPushPin.
            lastClickLocation = mapLocation;
            // Create and add the BalloonPushPin to the map.
            BalloonPushPin bpp = new BalloonPushPin(lastClickLocation);
            renderList.Add(bpp);
            mapMain.Renderer = bpp;
            mapMain.Invalidate();
            mapMain.Focus();
        }

        private void QueryBox_TextChanged(object sender, EventArgs e) {
            // Check whether the text in the query text field has changed. If it
            // has, clear the last click location and clear the map of pins.
            lastClickLocation = LatLon.Empty;
            renderList.Clear();
        }

        private void buttonSearch_Click(object sender, EventArgs e) {
            // Get the POI type selected in the drop-down list
            String poiType = poiTypes.Text;
            // Get the address or query string. 
            String queryString = QueryBox.Text;
            // Is there text in the query box?
            if (queryString != "") {
                // Is there a POI type selected? If not then we will use the query string as it is. The 
                // query string may already contain a query consisting of the POI name and a string like "near"
                // or "in".
                if (poiType != "None"){
                    // Combine the POI type and the address, adding 'near' to the string. 
                    queryString = poiType + " near " + queryString;
                }
                // Construct the Location Search arguments
                var args = new LocationSearchArgs {
                    Query = queryString,
                    // Specifying the country speeds the search process. When not supplied,
                    // all countries will be searched.
                    Countries = new Country[] { Country.USA },
                    // Set a location hint for the center of the map. This is
                    // used to bias the results toward the area currently visible on the map.
                    LocationHint = mapMain.Center
                };
                // Supply the Location Search search args.
                var search = new LocationSearch(args);
                // Then retrieve the results.
                LocationSearchResult output = search.Search();
                // Begin constructing the text that will be shown in the results field.
                StringBuilder locationResults = new StringBuilder("\r\nResults for Location Search '");
                locationResults.AppendFormat(queryString + "' \r\n\r\n");
                // Check that there are some results returned.
                if (output.Suggestions.Length > 0) {
                    // Clear the renderlist to prepare for result PushPins
                    renderList.Clear();
                    // Add back the address used in the Location Search.
                    if (lastClickLocation != LatLon.Empty) {
                        BalloonPushPin bpp = new BalloonPushPin(lastClickLocation);
                        renderList.Add(bpp);
                    }
                    // Set a counter to number the results, and to get the correct icons
                    // to use for the PushPins. These will be used to number the results in order from
                    // best to worst match.
                    int counter = 1;
                    int pincounter = 208;
                    // Create a new BoundingBox. We will add each result location to this, then
                    // zoom the map to encompass the BoundingBox
                    BoundingBox searchArea = new BoundingBox();

                    // Begin the loop through the Location Search results.
                    for (int idx = 0; idx < output.Suggestions.Length; idx++) {
                        // Append the result description.
                        locationResults.AppendFormat(counter + ": " + output.Suggestions[idx].DisplayText);
                        // Then, if the last clicked location and the suggested location aren't empty...
                        if (lastClickLocation != LatLon.Empty && output.Suggestions[idx].Location != LatLon.Empty) {
                            // Work out the distance between the result location and the clicked location, and convert it to miles.
                            // Also getting rid of any unnecessary decimal points.
                            decimal distShort = (decimal)(Math.Truncate((double)lastClickLocation.QuickDistanceTo(output.Suggestions[idx].Location, DistanceUnit.MILES) * 100.0) / 100.0);
                            // Add the distance to the string.
                            locationResults.AppendFormat(" (distance " + distShort + " miles)");
                        }
                        // Add some newlines at the end of the result string.
                        locationResults.AppendFormat("\r\n\r\n");
                        // Then add a simple PushPin at the result location.
                        PushPin pin = new PushPin(output.Suggestions[idx].Location);
                        // And expand the BoundingBox with the result location.
                        searchArea.Add(output.Suggestions[idx].Location);
                        // Then set the PushPin icon to a number from 1-20 (20 is the default number of results returned
                        // from a Location Search operation).
                        pin.Icon = pincounter;
                        // Add the pin to the renderlist and increment the two counters by one.
                        renderList.Add(pin);
                        counter++;
                        pincounter++;
                        // Loop back to the top and repeat for the next result, if any.
                    }
                    // Convert the results StringBuilder text into a string, then add this to the results field.
                    ResultsBox.Text = locationResults.ToString();
                    mapMain.Renderer = renderList;
                    searchArea.Inflate(0.001);
                    // Zoom the map to the results BoundingBox.
                    mapMain.ZoomToBoundingBox(searchArea, 100);
                    mapMain.Invalidate();
                    // Change focus to the map, so the user can pan and zoom.
                    mapMain.Focus();

                } else {
                    // If the Location Search completed without any results found, display this in the results field.
                    ResultsBox.Text = "No results found";
                };

            } else {
                // Display an alert message if the search button is clicked without providing text in the query field.
                MessageBox.Show("Double-click a location on the map, or enter an address/location. Alternatively enter your own search query (for example 'hotels in los angeles'). Then click the Search button.");
            }
        }

        public class TelogisCustomSource : LocationSearchSource {

            // Override the CreateSearchOperation method, which creates the operation to perform 
            // the searching with this search source.
            public override LocationSearchOperation CreateSearchOperation(LocationSearchArgs args) {
                return new TelogisSearchOperation(args);
            }

            // Override the HandledResultType for this location search with an
            // IndividualPointOfInterest result type. It describes the type 
            // of results that this source returns (in this case, specifically 
            // named points of interest), which the Location Search engine uses 
            // to determine whether the search source is relevant for a particular search.
            public override LocationSearch.ResultType HandledResultTypes {
                get {
                    return LocationSearch.ResultType.IndividualPointOfInterest;
                }
            }

            // Override the PriorityForSearch method with a priority that is higher than
            // the DefaultPoiPriority, so that this search source is used first.
            public override int PriorityForSearch(LocationSearchArgs args) {
                return LocationSearchSource.DefaultPoiPriority + 1;
            }

            // Override the GetIgnoredTypesForMatchingSearches method with an 
            // IndividualPointOfInterest result type. When this source finds 
            // results, the Location Search engine will ignore other search 
            // sources with a lower priority that find IndividualPointOfInterest results.
            public override LocationSearch.ResultType GetIgnoredTypesForMatchingSearches(LocationSearchArgs args) {
                return LocationSearch.ResultType.IndividualPointOfInterest;
            }

            // Override the name of this search source.
            public override string Name {
                get {
                    return string.Format(@"TelogisCustomSource");
                }
            }
        }

        public class TelogisSearchOperation : LocationSearchOperation {
            // The arguments to use for this location search.
            private LocationSearchArgs _args;

            // Constructor for this LocationSearchOperation
            public TelogisSearchOperation(LocationSearchArgs args) {
                _args = args;
            }

            // Override the FindResults method with a specific example.
            public override LocationSearchResult FindResults() {
                LocationSearchSuggestion[] suggestions = new LocationSearchSuggestion[0];
                string lowerCaseQuery = _args.Query.Trim().ToLower();
                if ("telogis".StartsWith(lowerCaseQuery)) {
                    suggestions = new LocationSearchSuggestion[] {
                        new LocationSearchSuggestion {
                            ResultType = LocationSearch.ResultType.IndividualPointOfInterest,
                            Location = new LatLon(33.583906, -117.731399),
                            Confidence = 0.6 + 0.4 * lowerCaseQuery.Length / "telogis".Length,
                            StreetNumber = 20,
                            StreetName = "Enterprise",
                            Regions = new string[] {"Aliso Viejo", "Orange", "California"},
                            PostCode = "92656",
                            Country = Country.USA,
                            Name = "Telogis Headquarters",
                            DisplayText = "Telogis Headquarters, 20 Enterprise, Aliso Viejo, California 92656"
                        }
                    };
                }
                return new LocationSearchResult() {
                    Suggestions = suggestions,
                    Errors = new string[0],
                    Status = SearchResult.SearchCompleted
                };
            }

            // Override the TryCancel method, immediately returning true to indicate cancellation 
            // was successful, because this operation takes a negligible amount of time to run.
            public override bool TryCancel() {
                return true;
            }
        }
    }
}