Doing client-side address validation with the Google Maps Geocode API

Update:  If you look at the comments down below you’ll see a comment from Thor Mitchell.  Thor is the product manager for the Google Maps API.  His comment informed me that the technique I demonstrate below (using a server-side proxy service to relay geocoded addresses) is needlessly complicated and against the Google Terms of Service.  Consequently, I updated my code and published this code in an new blog post.  I recommend that you use alternate technique to validate street addresses.

I’ve never been a fan of address forms that look like this;

Bad address form design

From a data entry perspective, it forces users to hit TAB constantly.  And users who are less web savvy will transition between the mouse and the keyboard to negotiate these fields.  It’s not a natural way to type an address.

By contrast, here is a human friendly address form:

Good address form design

While typing an address into this field, users can stay on the keyboard and simply type the address in a way that feels natural.  The problem [for programmers] is parsing and validating this raw text once the form has been submitted.

Client-side address validation using the Google Maps Geocode API

Converting raw text to a valid address

Overcoming this challenge was part of a recent project.  In a past life, I worked with the US Census Bureau TIGER data to validate & geocode addresses.  I was only marginally successfully at this task.

Thankfully, since that time, Google Maps was created and Google made available an API for validating & geocoding addresses.  My plan was to tackle this challenge with the following 3 ingredients:

  1. jQuery
  2. jQuery Validator
  3. Google Maps Geocoding API

On paper, this appeared relatively straight-forward.  However, in practice I encountered several hurdles.  Below I’ve detailed how I overcame these hurdles.

Fetching a geocoded address from the Google Map API

It’s relatively simple to get a geocoded address from Google’s Map API.  Furthermore, Google does an incredible job of converting seemingly bad input into valid input.  Take, for example, the following address:

1600 Amphitheatre Pkwy

I’ve not specified a city, a state or a zip code.  This address looks invalid.  However, because this address is unique enough, Google is able to resolve this incomplete address into a valid address.  This can be done using the following URL:

http://maps.google.com/maps/api/geocode/json?address=1600%20Amphitheatre%20Parkway&sensor=false

The address parameter contains the value of the possible address.  Google will attempt to resolve this address.  If resolved, a geocoded full address is returned using JSON. Click the link above to see the JSON data and explore the structure of the data.

By the way, the address could also be returned in XML by replacing json, in the URL above, with xml.

Creating a proxy service for the Google Maps API

Fetching the Google geocode URL (shown above) is extremely easy.  As a result, my inclination was to write some Javascript to fetch this URL from the web browser and then use the result to validate (or invalidate) the address.

However, when I executed this client-side Javascript code, I received the following error:

XMLHttpRequest cannot load http://maps.google.com/maps/api/geocode/json?address=1600%20Amphitheatre%20Parkway&sensor=false. Origin http://localhost:52306 is not allowed by Access-Control-Allow-Origin.

After some research I discovered this is not possible due to cross site scripting prohibitions that Google has applied to the Google Maps API.  If you’re interested in more information about these limitations, check out the following links:

To make a long story short, it is not possible to get client-side scripting access to the Google Maps Geocode API and then handoff the result to custom Javscript method .  To overcome this challenge it is necessary to create a proxy service (server-side) that fetches the Google Maps API result on behalf of the client.

Here are the basic steps this proxy service needs to perform:

  1. Accept a possible address from the client web browser
  2. Pass this address to Google to geocode
  3. Receive the result from Google
  4. Pass the result to the client

There are many different flavors of server-side environments & languages.  Consequently, I’m not going to be able to help everyone.  The language I use most often is C#.  The particular web site I was creating was based on ASP.NET MVC.  Below is the controller I created for this service.  I documented the steps in my code to help anyone who is converting this code to another languages:

~/Controllers/ApiController.cs

using System.Linq;
using System.Web.Mvc;
using System.Xml.Linq;
using MvcApplication.Models;

namespace MvcApplication.Controllers
{
    public class ApiController : Controller
    {
        /// <summary>
        /// The controller for http://myurl/api/geocode
        /// </summary>
        /// <param name="address">The address to be geocoded by Google Maps</param>
        /// <returns></returns>
        public JsonResult Geocode(string address)
        {
            // I hate magic strings, so I created a strongly-typed class to hold the address values
            var geoAddress = new Address();

            // This is the Google Maps API URL that will geocode this address
            var geoxml = XDocument.Load("http://maps.google.com/maps/api/geocode/xml" + "?address=" + address + "&sensor=false");

            if (geoxml.Descendants("result").Any())
            {
                // Use LINQ to XML to extract the geocode values from the returned XML
                var parts = from part in geoxml.Descendants("result").First().Descendants("address_component")
                    select new
                    {
                        LongName = part.Element("long_name").Value,
                        ShortName = part.Element("short_name").Value,
                        TypeName = part.Element("type").Value,
                    };

                // Ensure Google returned at least one result
                if (parts.Count() > 0)
                {
                    // Loop through each item
                    foreach (var part in parts)
                    {
                        switch (part.TypeName)
                        {
                            case "street_number":
                                geoAddress.StreetNumber = part.LongName;
                                break;
                            case "route":
                                geoAddress.StreetName = part.LongName;
                                break;
                            case "locality":
                                geoAddress.City = part.LongName;
                                break;
                            case "administrative_area_level_1":
                                geoAddress.State = part.LongName;
                                geoAddress.StateAbbreviation = part.ShortName;
                                break;
                            case "postal_code":
                                geoAddress.Zip = part.LongName;
                                break;
                        }
                    }
                }
            }

            // Serialize the Address class (created above) to JSON and return to the client
            return Json(geoAddress, JsonRequestBehavior.AllowGet);
        }
    }
}

I’m also using a strongly-typed model to represent the address.  Here is the code:

~/Models/Address.cs

namespace MvcApplication.Models
{
    public class Address
    {
        public string StreetNumber { get; set; }
        public string StreetName { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string StateAbbreviation { get; set; }
        public string Zip { get; set; }
    }
}

At the conclusion of this step, you should be able to make a request to your web server and receive a geocoded JSON address.  Example:

http://localhost/api/geocode?address=1600%20Amphitheatre%20Parkway

{"StreetNumber":"1600","StreetName":"Amphitheatre Pkwy","City":"Mountain View","State":"California","StateAbbreviation":"CA","Zip":"94043"}

Creating the web form

The proxy service (created above) can now relay address validation back & forth between the Google Maps API and the web client.  However, what is missing is the client-side Javascript to make all of this function.

To get started, I’ll create the simple web form shown in the screenshot above:

<form id="MyForm" name="MyForm" action="form.html">
    <div>
        <label for="Name">Name</label>
        <input id="Name" name="Name" type="text" />
    </div>
    <div>
        <label for="FullAddress">Address</label>
        <textarea id="FullAddress" name="FullAddress" cols="40" rows="5" class="fulladdressvalidator"></textarea>
    </div>
    <div>
        <input id="Submit" name="Submit" value="Submit" type="button" />
    </div>
</form>

Creating an address jQuery Validator

Next, I’ll create a custom jQuery validator that performs the following tasks:

  1. Sends the address to my proxy service
  2. Get the result from the proxy service
  3. Return a true/false value indicating whether the input is valid

This seems simple, but it involves an asynchronous call to the proxy service.  This means processing continues after the initial proxy service request is made.  Before the result is received the validator needs to return a true/false value indicating whether the address is valid or not.

To overcome this challenge, I added an IsChecking flag using jQuery’s data method.  This flag is set to true when the request is sent to the proxy service.  When a form is submitted I’m able to check this flag to see whether the field has truly been validated.

Here is the code for this jQuery full address validator:

function FullAddressValidator(value, element, paras) {

    // Convert the value variable into something a bit more descriptive
    var CurrentAddress = value;

    // If the address is blank, then this is for the required validator to deal with.
    if (value.length == 0) {
        return true;
    }

    // If we've already validated this address, then just return the previous result
    if ($(element).data("LastAddressValidated") == CurrentAddress) {
        return $(element).data("IsValid");
    }

    // We have a new address to validate, set the IsChecking flag to true and set the LastAddressValidated to the CurrentAddress
    $(element).data("IsChecking", true);
    $(element).data("LastAddressValidated", CurrentAddress);

    // The URL for my geocode proxy service
    var geocodeUrl = '/api/geocode?address=' + escape(CurrentAddress);

    // Make an asynchronous call to the geocode proxy service.
    $.ajax({
        url: geocodeUrl,
        type: "GET",
        dataType: 'json',
        success: function (data, textStatus, XMLHttpRequest) {
            // The code below only gets run after a successful proxy service call has completed.
            // Because this is an asynchronous call, the validator has already returned a 'true' result
            // to supress an error message and then cancelled the form submission.  The code below
            // needs to fetch the true validation from the proxy service and then re-execute the
            // jQuery form validator to display the error message.  Futhermore, if the form was
            // being submitted, the code below needs to resume that submit.

            // If StreetNumber exists, then the address is valid
            if (data.StreetNumber) {
                var address = data.StreetNumber + ' ' + data.StreetName + "\n" + data.City + ', ' + data.StateAbbreviation + ' ' + data.Zip;

                $(element).val(address);
                $(element).data("LastAddressValidated", address);
                $(element).data("IsValid", true);
                // Otherwise the address is invalid
            } else {
                $(element).data("IsValid", false);
            }

            // We're no longer in the midst of validating
            $(element).data("IsChecking", false);

            // Get the parent form element for this address field
            var form = $(element).parents('form:first');

            // This code is being run after the validation for this field,
            // if the form was being submitted before this validtor was
            // called then we need to re-submit the form.
            if ($(element).data("SubmitForm") == true) {
                form.submit();
            } else {
                // Re-validate this property so we can return the result.
                form.validate().element(element);
            }
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            $(element).data("IsValid", false);
            $(element).data("IsChecking", false);
            $(element).data("SubmitForm", false);
        }
    });

    return true;
}

$.validator.addMethod("fulladdress", FullAddressValidator);

Putting it all together

The steps above describe how to:

  1. Create a proxy service to the Google Maps Geocode API
  2. Create a custom jQuery Validator to use this proxy service for address validation

The final step is to combine all of these pieces into a working form.  Below is the completed client-side code for the form shown above.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Example Form </title>
    <link href="./css/site.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
    <script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <div id="main">
            <form id="MyForm" name="MyForm" action="form.html">
            <div>
                <label for="Name">
                    Name</label>
                <input id="Name" name="Name" type="text" />
            </div>
            <div>
                <label for="FullAddress">
                    Address</label>
                <textarea id="FullAddress" name="FullAddress" cols="40" rows="5" class="fulladdressvalidator"></textarea>
            </div>
            <div>
                <input id="Submit" name="Submit" value="Submit" type="button" />
            </div>
            </form>
        </div>
    </div>
    <script type="text/javascript">
        // The following code show execute only after the page is fully loaded
        $(document).ready(function () {

            if ($('#MyForm').exists()) {

                // Enable jQuery Validation for the form
                $("#MyForm").validate();

                // Add validation rules to the FullAddress field
                $("#FullAddress").rules("add", {
                    fulladdress: true,
                    required: true,
                    messages: {
                        fulladdress: "Google cannot locate this address."
                    }
                });

                // This function will be executed when the form is submitted
                function FormSubmit() {
                    $.submitForm = true;
                    if (!$('#MyForm').valid()) {
                        return false;
                    } else {
                        if ($("#FullAddress").data("IsChecking") == true) {
                            $("#FullAddress").data("SubmitForm", true);
                            return false;
                        }

                        return false;
                    }
                }

                // Attach the FormSubmit function to the Submit button
                if ($('#Submit').exists()) {
                    $("#Submit").click(FormSubmit);
                }

                // Execute the ForumSubmit function when the form is submitted
                $('#MyForm').submit(FormSubmit);
            }
        });

        // Create a jQuery exists method
        jQuery.fn.exists = function () { return jQuery(this).length > 0; }

        // FullAddress jQuery Validator
        function FullAddressValidator(value, element, paras) {

            // Convert the value variable into something a bit more descriptive
            var CurrentAddress = value;

            // If the address is blank, then this is for the required validator to deal with.
            if (value.length == 0) {
                return true;
            }

            // If we've already validated this address, then just return the previous result
            if ($(element).data("LastAddressValidated") == CurrentAddress) {
                return $(element).data("IsValid");
            }

            // We have a new address to validate, set the IsChecking flag to true and set the LastAddressValidated to the CurrentAddress
            $(element).data("IsChecking", true);
            $(element).data("LastAddressValidated", CurrentAddress);

            // The URL for my geocode proxy service
            var geocodeUrl = '/api/geocode?address=' + escape(CurrentAddress);

            // Make an asynchronous call to the geocode proxy service.
            $.ajax({
                url: geocodeUrl,
                type: "GET",
                dataType: 'json',
                success: function (data, textStatus, XMLHttpRequest) {
                    // The code below only gets run after a successful proxy service call has completed.
                    // Because this is an asynchronous call, the validator has already returned a 'true' result
                    // to supress an error message and then cancelled the form submission.  The code below
                    // needs to fetch the true validation from the proxy service and then re-execute the
                    // jQuery form validator to display the error message.  Futhermore, if the form was
                    // being submitted, the code below needs to resume that submit.

                    // If StreetNumber exists, then the address is valid
                    if (data.StreetNumber) {
                        var address = data.StreetNumber + ' ' + data.StreetName + "\n" + data.City + ', ' + data.StateAbbreviation + ' ' + data.Zip;

                        $(element).val(address);
                        $(element).data("LastAddressValidated", address);
                        $(element).data("IsValid", true);
                        // Otherwise the address is invalid
                    } else {
                        $(element).data("IsValid", false);
                    }

                    // We're no longer in the midst of validating
                    $(element).data("IsChecking", false);

                    // Get the parent form element for this address field
                    var form = $(element).parents('form:first');

                    // This code is being run after the validation for this field,
                    // if the form was being submitted before this validtor was
                    // called then we need to re-submit the form.
                    if ($(element).data("SubmitForm") == true) {
                        form.submit();
                    } else {
                        // Re-validate this property so we can return the result.
                        form.validate().element(element);
                    }
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    $(element).data("IsValid", false);
                    $(element).data("IsChecking", false);
                    $(element).data("SubmitForm", false);
                }
            });

            return true;
        }

        $.validator.addMethod("fulladdress", FullAddressValidator);
    </script>
</body>
</html>

All this work, just so I can do this:

Client-side address validation using the Google Maps Geocode API

Damn!

This entry was posted in Uncategorized. Bookmark the permalink.
  • Thor Mitchell

    It's much easier to use the JavaScript Maps API to do this rather than write server side code and set up a proxy. The JS API handles the cross site communication for you, and makes the geooding requests directly from the user's browser rather than your server. This reduces the risk of quota exhaustion, and improves performance:

    http://code.google.com/apis/maps/documentation/javascript/services.html#Geocoding

    Note also that the Terms of Service that cover all of the Maps APIs (including the web service used in the above example) require that the services be used in conjunction with a Google Map. If you use the JS API this becomes trivial. You can just drop a fully draggable map into your site next to the address validation field that shows the location of the validated address.

    Thor Mitchell
    Product Manager, Google Maps API

  • Gabe Sumner

    Hi Thor,

    I appreciate your reply. I'm preparing a new blog post that demonstrates the technique and map that you describe.

  • Gabe Sumner

    I updated my code and published a new blog post. Thor, thanks for posting a comment and leading me in the right direction.

  • Xiao Yu

    lol, you have 5 links to the same place…
    Solid name Thor :)

  • Anonymous

    Do you have a working sample applicaiton that I can play with on your web site…

  • Eric Kelly

    While I think that is definitely a great idea, this also presents a new problem. Doing the address entry this way could also confuse people because of the way that it forces people to learn a new way to enter their address information that is different from the way that they have been doing it for years.

  • Adamberkley

    I’ve just been passed this from a friendÂ
    http://www.postcodeanywhere.co.uk/demos/postal-code-address-validation.aspx?source=hmpgbanner

    I’ve never seen anything like it before for address “capture/input” – anyone else?

    Apparently it’s going to be using international data by the end of the week – pretty sweet.

  • Pingback: Geocode addresses | 747mediagroup