Geocoding is process when user provides some geo data (eg: street name, house number, postal code, city name, state name, etc...). This data can sometimes be incomplete or even some pieces of data misspelled. Geocoder on the other hand is usually some service that implements mechanisms to parse this input data, corrects misspelled words (not allways) and finally returns user correct address and the most important - Latitude and Longitude information. This latitude/longitude coordinate represents a coordinate on the planet Earth where this particular address is located.
You all know devices like Garmin, TomTom etc, which is used to help you find the way. And those devices use some sort of geocoder, that converts your input data into coordinate, calculates the neares or most economic way and takes you right on the spot. Actually there is much more to that on how this calculations are made, but for the sake of simplicity, let's leave that for now :-)
There is maybe one more interesting thing to mention. Devices like Garmin, TomTom etc... don't exactly know where the building you are looking for is located. The database know only where the starting point and ending point of the street is, what is the starting house number and ending house number and it assumes that all houses are exactly the same measurements and equally spaced. But that is good enough to take you to location desired.
OK, enough of this theory, let's start building our app. Create a Windows Forms Application. On your form, add the following controls:

I named my controls as follows:
txtStreet
txtCity
txtState
btnGeocode
dgvAddresses
Now go to the code view and add the following using statement:
using System.Xml; using System.Data; using System.Globalization;
Now before the public partial class Form1 : Form add the struct that will be used to hold the geocoded address data:
public struct Address
{
public string Street;
public string City;
public string State;
public string Zip;
public string Country;
public double Latitude;
public double Longitude;
}
Before the Form1 constructor add the following two global variables. Notice the NumberFormatInfo class. I used it to correctly convert the value of latitude and longitude from string to double.
private DataTable table = null; private NumberFormatInfo format = null;
After that, create a private method named InitCustomComponents.
private void InitCustomComponents()
{
//Initialize format provider
format = new NumberFormatInfo();
//Set the decimal separator (you might have to change
//this to comma, since it depends on your local settings
format.NumberDecimalSeparator = ".";
//Initialize table for addresses and add the columns
table = new DataTable();
table.Columns.Add("Street", typeof(string));
table.Columns.Add("City", typeof(string));
table.Columns.Add("State", typeof(string));
table.Columns.Add("Zip", typeof(string));
table.Columns.Add("Country", typeof(string));
table.Columns.Add("Latitude", typeof(double));
table.Columns.Add("Longitude", typeof(double));
}
When you have done so, create an AddAddress method which is used to create and populate a row in table with data retrieved by geocoding service.
private void AddAddress(Address a)
{
//Add a row with geocoded data to our table
table.Rows.Add(a.Street, a.City, a.State, a.Zip, a.Country, a.Latitude, a.Longitude);
}
Now the most important part of application is the method GeocodeAddress which does all the work. It calls the geocoding service, passing it user inputs from textboxes. Geocoder then returns the XML schema object back to user and we need to parse this data using a simple XmlTextReader, iterating through all the lines in given XML.
private void GeocodeAddress(string street, string city, string state)
{
//Create a new instance for holding geocoded data
Address address = new Address();
//Geocoder returns data in XML format so we need to
//create a new instance of XMLTextReader and provide an url
XmlTextReader reader = new XmlTextReader
("http://local.yahooapis.com/MapsService/V1/geocode?appid=ENTER_YOUR_YAHOO_DEVELOPER_API_KEY_HERE&street=" + street + "&city=" + city + "&state=" + state);
//Specify the way how white space is handled
reader.WhitespaceHandling = WhitespaceHandling.Significant;
//Start reading geocoded data
while (reader.Read())
{
string node = reader.Name.ToString(); //current node in XML document
string value = reader.ReadString(); //value/inner text of current XML node
switch (node)
{
case "Address":
address.Street = value;
break;
case "City":
address.City = value;
break;
case "State":
address.State = value;
break;
case "Zip":
address.Zip = value;
break;
case "Country":
address.Country = value;
break;
case "Latitude":
address.Latitude = double.Parse(value, format);
break;
case "Longitude":
address.Longitude = double.Parse(value, format);
break;
default:
continue;
}
}
//Add geocoded address to our table
AddAddress(address);
}
Now there are only two things left to do. First is the button click event that at first clears all the data in Data Table that was left there from last geocoding request, that calls the GeocodeAddress method with new inputs and finally sets the data grid view DataSource property and tells it to use the data table with all the geocoded addresses for given address.
private void btnGeocode_Click(object sender, EventArgs e)
{
//Clear the table
table.Rows.Clear();
//Call the GeocodeAddress method with values from text boxes
GeocodeAddress(txtStreet.Text, txtCity.Text, txtState.Text);
//Attach table with geocoded data to our Data Grid View as its data source
dgvAddresses.DataSource = table;
}
Finally I implemented another method that is triggered when user double clicks on the row in data grid view. If the row with a geocoded address is clicked, this address and its location is opened in Google Maps using the default user's web browser.
private void dgvAddresses_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
//Verify that index of clicked row is not negative
if (!e.RowIndex.Equals(-1))
{
//Extract the complete row that was clicked and its data
DataGridViewRow row = dgvAddresses.Rows[e.RowIndex];
//Verify that clicked row is not an empty row (usually the last row)
if (!row.IsNewRow)
{
//Extract the latitude and longitude from the clicked row
double lat = (double)dgvAddresses["Latitude", e.RowIndex].Value;
double lng = (double)dgvAddresses["Longitude", e.RowIndex].Value;
//Show the geocoded result in Google Maps using your default browser
System.Diagnostics.Process.Start(string.Format("http://maps.google.com/maps?q={0},{1}", lat.ToString(format), lng.ToString(format)));
}
}
}
Final solution looks like this:
using System;
using System.Windows.Forms;
using System.Xml;
using System.Data;
using System.Globalization;
namespace GeocoderApp
{
//Structure that will hold data from geocoded address
public struct Address
{
public string Street;
public string City;
public string State;
public string Zip;
public string Country;
public double Latitude;
public double Longitude;
}
public partial class Form1 : Form
{
//Data table that will hold all the addresses returned by geocoder
//Note that same street with same house number may exist within
//the same city and same state
private DataTable table = null;
//Format provider is needed to correctly convert number as string
//into double
private NumberFormatInfo format = null;
public Form1()
{
InitializeComponent();
//Initialize our custom components
InitCustomComponents();
}
private void InitCustomComponents()
{
//Initialize format provider
format = new NumberFormatInfo();
//Set the decimal separator (you might have to change
//this to comma, since it depends on your local settings
format.NumberDecimalSeparator = ".";
//Initialize table for addresses and add the columns
table = new DataTable();
table.Columns.Add("Street", typeof(string));
table.Columns.Add("City", typeof(string));
table.Columns.Add("State", typeof(string));
table.Columns.Add("Zip", typeof(string));
table.Columns.Add("Country", typeof(string));
table.Columns.Add("Latitude", typeof(double));
table.Columns.Add("Longitude", typeof(double));
}
private void GeocodeAddress(string street, string city, string state)
{
//Create a new instance for holding geocoded data
Address address = new Address();
//Geocoder returns data in XML format so we need to
//create a new instance of XMLTextReader and provide an url
XmlTextReader reader = new XmlTextReader
("http://local.yahooapis.com/MapsService/V1/geocode?appid=ENTER_YOUR_YAHOO_DEVELOPER_API_KEY_HERE&street=" + street + "&city=" + city + "&state=" + state);
//Specify the way how white space is handled
reader.WhitespaceHandling = WhitespaceHandling.Significant;
//Start reading geocoded data
while (reader.Read())
{
string node = reader.Name.ToString(); //current node in XML document
string value = reader.ReadString(); //value/inner text of current XML node
switch (node)
{
case "Address":
address.Street = value;
break;
case "City":
address.City = value;
break;
case "State":
address.State = value;
break;
case "Zip":
address.Zip = value;
break;
case "Country":
address.Country = value;
break;
case "Latitude":
address.Latitude = double.Parse(value, format);
break;
case "Longitude":
address.Longitude = double.Parse(value, format);
break;
default:
continue;
}
}
//Add geocoded address to our table
AddAddress(address);
}
private void AddAddress(Address a)
{
//Add a row with geocoded data to our table
table.Rows.Add(a.Street, a.City, a.State, a.Zip, a.Country, a.Latitude, a.Longitude);
}
private void btnGeocode_Click(object sender, EventArgs e)
{
//Clear the table
table.Rows.Clear();
//Call the GeocodeAddress method with values from text boxes
GeocodeAddress(txtStreet.Text, txtCity.Text, txtState.Text);
//Attach table with geocoded data to our Data Grid View as its data source
dgvAddresses.DataSource = table;
}
//Handle the mouse double click when user clicks on address in data grid view
private void dgvAddresses_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
//Verify that index of clicked row is not negative
if (!e.RowIndex.Equals(-1))
{
//Extract the complete row that was clicked and its data
DataGridViewRow row = dgvAddresses.Rows[e.RowIndex];
//Verify that clicked row is not an empty row (usually the last row)
if (!row.IsNewRow)
{
//Extract the latitude and longitude from the clicked row
double lat = (double)dgvAddresses["Latitude", e.RowIndex].Value;
double lng = (double)dgvAddresses["Longitude", e.RowIndex].Value;
//Show the geocoded result in Google Maps using your default browser
System.Diagnostics.Process.Start(string.Format("http://maps.google.com/maps?q={0},{1}", lat.ToString(format), lng.ToString(format)));
}
}
}
}
}
Final result of our work looks something very similar to this:

I hoped you enjoyed this tutorial. If you have any questions or need assistance, feel free to drop a comment.




MultiQuote




|