Drill-down Geocoder Tutorial |
A drill-down geocoder (see DrillDownGeoCoder) allows the user to perform a forward geocode by successively 'drilling down' through decreasingly smaller search regions. A drill-down geocoder can rapidly narrow a search to a street; this is often useful when a complete address is unknown. As an example, a customer's voice might be difficult to understand over the phone - using a drill-down geocoder, an intelligent guess can often be made when given the appropriate options.
The drill-down geocoder works by allowing the user to select successively smaller regions, 'drilling-down' towards a target street. To demonstrate this procedure, consider the following drill-down:
At the conclusion of this search, Enterprise (in Aliso Viejo, Orange, California) will have been located. California, Orange and Aliso Viejo are all regions, whereas Enterprise is a street. California (a top-level region) is assigned a region level of zero. Each child region has a region level one higher than its parent region. For example, Aliso Viejo has a region level of 2.
In this tutorial we will create an application to find a street using the drill-down geocoder, and pin-point the street on a map using a BalloonPushPin. To find the street the user will first select a state, then a county, then a city, then the street name.
The code in this tutorial will request an array of regions in a given region level using the GetRegions(Int32) method. If the user selects a region from the list box, we will set that region in the drill-down geocoder using the SetRegion(RegionData) method. After all the regions have been set, we will perform a street search using the GetStreets method.
Open Visual Studio and create a new Windows application called DrillDown Geocoder, and add a reference to geobase.net.dll.
Create the drill-down geocoder form as laid out below.
Control type | Role | Name |
---|---|---|
ListBox | Display list of regions / addresses to choose from | listBoxResults |
ListBox | Display user's search / selection history | listBoxHistory |
TextBox | Enter characters to search | textBoxSearch |
Button | Reset search, and clear list boxes | btnReset |
MapCtrl | View search results - map zooms in as search regions are set | mapCtrl1 |
Label | Informative label. Set its text to Drill down geocoder results. | |
Label | Informative label. Set its text to Selection History. | |
Label | Informative label. Set its text to Search. |
In Form1.cs, add the directive 'using Telogis.GeoBase' to access the Telogis.GeoBase namespace, and then add the following member properties:
//Drill-down geocoder DrillDownGeoCoder ddgc; //Balloon push pin to highlight search result BalloonPushPin bpp = new BalloonPushPin(new LatLon(34,-118)); //Rendererlist for bpp RendererList renderList = new RendererList(); //Keep track of which region level we're on int regionLevel;
Edit the Form1 constructor as shown below:
public Form1() { InitializeComponent(); //Change the country if you're not in the USA ddgc = new DrillDownGeoCoder(Country.USA); //RegionLevel will be incremented as we drill-down towards street level regionLevel = 0; //Load top-level regions into our listbox Filter(); //Draw the balloon push pin on the map renderList.Add(bpp); mapCtrl1.Renderer = renderList; }
Add a Text Changed event to the textBoxSearch text box. Modify the Text Changed method to contain the following code.
private void textBoxSearch_TextChanged(object sender, EventArgs e) { //Add search term to history AddHistory(); Filter(); }
The AddHistory method is called to add the search term to the history listbox (listBoxHistory), while the Filter method will update the regions, and streets, displayed in the drill-down geocoder results' listbox (listBoxResults) when the user enters search text.
private void AddHistory() { //Add search term to history if(textBoxSearch.Text.Length > 0) { listBoxHistory.Items.Add(textBoxSearch.Text); } }
The following Filter method will use the contents of the textbox (the filter) to display regions at the selected region level. Regions will be displayed in the results' listbox. The method begins by checking if all the region levels have been set - if they have, then a list of streets is displayed based on the current filter. If all the region levels have not been set, then the child regions of the parent region are displayed in the results' (listBoxResults) listbox.
private void Filter() { string filterText = textBoxSearch.Text; //If all regions have been set, search for a street if (regionLevel >= ddgc.NumRegionLevels) { listBoxResults.Items.Clear(); if (filterText.Length == 0) { filterText = "a"; } //Get an array of streets StreetData[] sd = ddgc.GetStreets(filterText).Results; //Add the filtered streets to the listbox for (int i = 0; i < sd.Length; i++) { listBoxResults.Items.Add(sd[i].ToString()); } } //...otherwise, set remaining region levels else if (regionLevel < ddgc.NumRegionLevels) { listBoxResults.Items.Clear(); //Get an array of filtered regions RegionData[] rd; if (filterText.Length > 0) { rd = ddgc.GetRegions(regionLevel, filterText).Results; } else { rd = ddgc.GetRegions(regionLevel).Results; } //Add the filtered regions to the listbox for (int i = 0; i < rd.Length; i++) { listBoxResults.Items.Add(rd[i].ToString()); } } }
Add a Selected Index Changed event to the listBoxResults listbox to respond to the user selecting a region, or street.
The method begins by testing to see if we're at street or region level. If we are at region level, then the region level is set (using SetRegion), and incremented (regionLevel++;). The map then centers and zooms on the selected region.
If we're at street level, then we get the streets available based on the filter (search) text: StreetData[] sd = ddgc.GetStreets(filterText).Results;, and then center and zoom the map to the selected location. Finally, we add a balloon push pin at the selected address.
Modify the event to match the following:
private void listBoxResults_SelectedIndexChanged(object sender, EventArgs e) { if (listBoxResults.SelectedItem != null) { //Are we at region level or street level? if (regionLevel < ddgc.NumRegionLevels) { //At region level. Set the selected region RegionData rd = ddgc.GetRegions( regionLevel, listBoxResults.SelectedItem.ToString()).Results[0]; ddgc.SetRegion(rd); //Add to history listBoxHistory.Items.Add(listBoxResults.SelectedItem.ToString()); //Update region level regionLevel++; textBoxSearch.Text = ""; //Center and zoom the map on the selected region. //As we drill down, the map will zoom closer and closer. mapCtrl1.Center = rd.Location; int zoom = 4 * (ddgc.NumRegionLevels - regionLevel); mapCtrl1.Zoom = 1 + zoom; // = 1 at street level Filter(); // Filter on the next region level } else { //Must be at street level //Show the location on the map string filterText; string address = listBoxResults.SelectedItem.ToString(); //Add result to history AddHistory(); //Set search term filterText = textBoxSearch.Text; StreetData[] sd = ddgc.GetStreets(filterText).Results; //Set latlon based on chosen listbox item LatLon ll = sd[listBoxResults.SelectedIndex].GetLocation(); mapCtrl1.Center = ll; mapCtrl1.Zoom = 0.8; //Put a balloon at the address bpp.Name = "Drill-Down Search Result"; bpp.Information = address; bpp.Location = ll; } } }
Finally, add a Click event to the btnReset button. Modify the Click event method to contain the following code.
private void btnReset_Click(object sender, EventArgs e) { // Clear searchbox and history textBoxSearch.Clear(); listBoxHistory.Items.Clear(); // reset DDGC and repopulate listbox with states ddgc.Reset(); regionLevel = 0; Filter(); // Clear any existing pins, recenter/zoom map bpp.Information = null; bpp.Location = new LatLon(); mapCtrl1.Center = new LatLon(34.0, -118.0); mapCtrl1.Zoom = 1000; // Refresh map mapCtrl1.Invalidate(); }
Build and run your application. The form will open with a list of regions in the 'Drill-down geocode results' listbox (listBoxResults), as shown below. You can drill-down through successive region levels either by selecting from the list box (listBoxResults), or by typing in the search text box (textBoxSearch). Notice how your selections are recorded in the history listbox (listBoxHistory). When a single address is selected, its location will be displayed on the map with the balloon push pin.