Total Pageviews

Monday, April 15, 2013

Consume Google Maps geoCode API Web Service in .NET

Hi,

In this post I will be talking about geocoding the address and extracting XML node values such as "Google defined address(Formatted_Address)", "Latitude", "Longitude" etc.

As we all must have searched some address location on Google Maps so I'm assuming you all to be aware with the basic look & feel and working of Google Maps.

Mostly we enter one address at a time on "Google Maps" UI, which in turn, redirects and points the location using markers.
In the scenario where you have to search for specific nodes or node-values i.e. customized search for any associated value for a particular address, it may be tedious and frustrating.

So lets see how we can use the freely available "Google Maps API" and attain our desired results.
For full documentation on Google API Web Services visit Google API 

The web service request is made in the format given below:

http://maps.googleapis.com/maps/api/service/output?parameters 

Although the same web service can also be access via HTTPS protocol in the format given below:
https://maps.googleapis.com/maps/api/service/output?parameters


For the browser to make web service call to Google Maps API, following url string is parsed where address take the parameter value {0}:
http://maps.google.com/maps/api/geocode/xml?address={0}&sensor=false 

To make it more simpler, I'm using Melbourne, Australia as the parameter value for address field. This will be interpreted by the system in the format:
http://maps.google.com/maps/api/geocode/xml?address=Melbourne,Australia&sensor=false

To see the results you can directly paste the above url into your browser and see the results
Google API Web Service Call Result
Results for Google API Web Service Call with Address as Melbourne, Australia

Now this XML result set gives you all the nodes that can be used, based on your requirements. But it's hard to manually change the address values when geocoding hundreds or thousands of addresses.

To overcome this scenario I opted for reading all the addresses from a text file in the format using while loop:
uniqueID:address
uniqueID:address
uniqueID:address

Method ReadAddressFromFile on PageLoad or Button Click


private void ReadAddressFromFile()
        {
            try
            {
                // Create an instance of StreamReader to read from a file.
                // The using statement also closes the StreamReader.
                using (StreamReader sr = new StreamReader(@"D:\geocode\Uploads\Address_1.txt"))
                {
                    String line;
                    string _address = "";
                    // Read and display lines from the file until the end of
                    // the file is reached.
                    while ((line = sr.ReadLine()) != null)
                    {
                        _recordCount++;
                        Session["_recordCount"] = _recordCount;
                        _merchantID = line.Split(':');
                        Session["_merchantId"] = _merchantID[0].ToString();
                 
                        _address = _address + line+"\n\n";
                        _address = _address.Replace("\t", " ");
                        Session["addressFromFile"] = _merchantID[1].ToString();
                        FindCoordinates();
                        _address = ""; //
                    }
           
                }
            }
            catch (Exception ex)
            {
                // Let the user know what went wrong.
            }
        }


In the above code snippet, on each line read "FindCoordinates" Method is been called where I'm passing ID and address after delimiting/tokenizing on ':' (If you remember uniqueID:address still :) )

Now let's see what's happening in FindCoordinates method...
Method FindCoordinates()


private void FindCoordinates()
        {
            try
            {
                url = String.Format("http://maps.google.com/maps/api/geocode/xml?address={0}&sensor=false", Session["addressFromFile"].ToString().Replace(" ", ","));
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream outputStream = response.GetResponseStream();
                StreamReader reader = new StreamReader(outputStream, Encoding.ASCII);
                output = reader.ReadToEnd();

                response.Close();
                outputStream.Close();
                reader.Close();

                //write to File                            
                deSerializeResponse();
            }
            catch (Exception ex)
            {
                //Catch execptions here
            }
            finally
            {
               //clear any session values
            }
        }


In the above code snippet, web service call is made with address parameter from Session value "addressFromFile" which is assigned in ReadAddressFromFile method.


What Happens in deSerializeResponse Menthod?

Here's the snippet:


private void deSerializeResponse()
        {
            string _lat = "";
            string _long = "";
            string results = "";
            string formatted_address = "";
            string oldFileName = @"D:\geocode\Uploads\XMLOutput.txt";
            string newFileName = @"D:\geocode\Uploads\myXmFile.xml";
            File.Delete(oldFileName);
            // Write the string to a file.
            System.IO.StreamWriter file = new System.IO.StreamWriter(@"D:\geocode\Uploads\XMLOutput.txt");
            System.IO.StreamWriter wsResultFile = File.AppendText(@"D:\geocode\Uploads\WebServiceResult.txt");
            // Retrieve XML document

            XmlTextReader reader = new XmlTextReader(new StringReader(txtLatitude.Text));

            // Skip non-significant whitespace
            reader.WhitespaceHandling = WhitespaceHandling.Significant;
            file.WriteLine(txtLatitude.Text);

            file.Close();
            File.Delete(newFileName);
            File.Move(oldFileName, newFileName);
            XmlDocument xdXml = new XmlDocument();
            xdXml.Load(@"D:\geocode\Uploads\myXmFile.xml");
            //Make a nodelist
            XmlNodeList nodes = xdXml.SelectNodes("//GeocodeResponse/result"); //1
            foreach (XmlNode node in nodes)
            {
                formatted_address = node["formatted_address"].InnerText;
            }
            nodes = xdXml.SelectNodes("//GeocodeResponse/result/geometry/location"); //2
            foreach (XmlNode node in nodes)
            {
                _lat = node["lat"].InnerText;
                _long = node["lng"].InnerText;
            }
            results = Session["_merchantId"].ToString() + " | " + Session["addressFromFile"].ToString().Trim() + " | " + formatted_address + " | " + _lat + " | " + _long;        
            wsResultFile.WriteLine(results);
            wsResultFile.Close();
        }

"There are few files that I've created for myself so don't get confused in number of files been created in the above code snippet ;-) "


Mainly, you are looking at "myXmFile.xml" from where iterate through each node of GeocodeResponse>>result (refer 1 in the code snippet) and further iterate within geometry>>location node(refer 2) to read latitude("lat") and longitude("lng") values.


Why I used uniqueID?
Because, all these geocode results will be uploaded into database with these ID mappings.

The above scenario will be often seen for web apps or mobile apps where you have to list down addresses or locations based on "GPS current location", "Postcode", "region" searches.

Cheers!

1 comment: