[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Check valid or invalid address with Google Maps API

Posted on 2011-10-07
4
Medium Priority
?
3,210 Views
Last Modified: 2013-12-13
Our Student Management System feeds a third party bus routing system (Versatrans RP). Versatrans then takes the addresses and builds bus routes using geocoding. Campus clerks continually messup or misspell addresses (e.g. "18=9 Vic. Crclke" instead of the correct "189 Victory Circle") which of course causes the auto-build of routes to fail.

Can anyone point me in the direction of how to use Google Maps API or other webservice to grab addresses, assess them, and return TRUE if they're OK and FALSE if they're not?

Many thanks....
0
Comment
Question by:smetterd
  • 3
4 Comments
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 36935739
The US Postal Service offers a data verification API.  Google it if you want to learn more.  In my experience it was difficult to use and their "tech support" was nothing better than surly.  But that was a few years ago, and now that they are living in the fear of going out of business they may have gotten a better customer-service ethic.  

The geocoders from Google and Yahoo can also provide data verification.  You might want to check the terms of use carefully.  IIRC the Google geocoder must be used in conjunction with a Google Map, or you have to pay a fee for its commercial use.  Here is demonstration script showing how I have used the Google and Yahoo geocoders.  These technologies are changing under the covers of the services that provide them, so you may find new APIs or new data returned from the API.  It appears at this moment that the Yahoo service is down.  However it may be disabled since the V1 interface has been deprecated.  The new Yahoo service is called PlaceFinder and it is not integrated into my example here.

Testing this address which does not exist, you will see that Google tries to give a geocode and returns a precision of "6"
http://www.laprbass.com/RAY_geocoder.php?a=7520+Colleen+Lane&c=McLean&s=VA&z=22101

Testing this address which does exist, you get a precision of "8"
http://www.laprbass.com/RAY_geocoder.php?a=1446+Colleen+Lane&c=McLean&s=VA&z=22101

Using the new Yahoo PlaceFinder, you can see the results here:
Valid http://where.yahooapis.com/geocode?q=1446+Colleen+Lane,McLean,VA,22101
Bogus http://where.yahooapis.com/geocode?q=7520+Colleen+Lane,McLean,VA,22101

More info: http://developer.yahoo.com/geo/placefinder/

These services try to be helpful by guessing at what you mean if the address is not completely valid, so you may need to do some additional programming to compare your input to their response and see if the results make sense.  Note the address returned by the bogus call to the PlaceFinder!

HTH, ~Ray
<?php // RAY_geocoder.php
error_reporting(E_ALL);


// DEMONSTRATE HOW TO CALL THE YAHOO! AND GOOGLE GEOCODERS


// A NOMINAL DATA CLEAN-UP FUNCTION
function get_clean_text_string($s)
{
    return preg_replace('/[^A-Z 0-9_\.,\-#]/i', NULL, $s);
}


// API KEYS MAY BE REQUIRED
require_once('RAY_live_data.php');


// A FREEFORM LOCATION STATEMENT IS OPTIONAL (BUT NOT USED HERE)
$location = $a = $c = $s = $z = NULL;

// THESE ARE THE GET-FORM VARIABLES
$vars = array( 'a', 'c', 's', 'z' );

// FILTER THE ARGUMENTS
if (!empty($_GET))
{
    foreach ($_GET as $k => $v)
    {
        if (!in_array($k, $vars)) continue;
        $$k = get_clean_text_string($v);
    }
    if ($a != NULL) { $location .= $a . ' '; }
    if ($c != NULL) { $location .= $c . ' '; }
    if ($s != NULL) { $location .= $s . ' '; }
    if ($z != NULL) { $location .= $z . ' '; }
    $location = trim($location);
}

// PREPARE THE FORM
$form = <<<ENDFORM
<doctype html>
<head>
<title>$a $c $s $z Yahoo/Google Geocoder Query</title>
</head>
<body>
<form method="get">
Addr: <input type="text" name="a" autocomplete="off" value="$a" />
City: <input type="text" name="c" autocomplete="off" value="$c" />
ST:   <input type="text" name="s" autocomplete="off" value="$s" size="2" />
Zip:  <input type="text" name="z" autocomplete="off" value="$z" size="8" />
<input type="submit" value="Go" />
</form>
</body>
</html>
ENDFORM;

if (!$location)
{
    echo $form;
    die();
}


// PREPARE THE GEOCODER
$y_demo       = new SimpleGeoCoder;
$y_demo->address = $a;
$y_demo->city    = $c;
$y_demo->state   = $s;
$y_demo->zip     = $z;

// TEST THE YAHOO! GEOCODER
$y_demo->geocodeYahoo();
echo "<pre>";
echo PHP_EOL;
echo "YAHOO! DATA FOR <strong>$location</strong>";
echo PHP_EOL;
print_r($y_demo);
echo "</pre>";



// PREPARE THE GEOCODER
$g_demo        = new SimpleGeoCoder;
$g_demo->address  = $a;
$g_demo->city     = $c;
$g_demo->state    = $s;
$g_demo->zip      = $z;

// TEST THE GOOGLE GEOCODER
$g_demo->geocodeGoogle();
echo "<pre>";
echo PHP_EOL;
echo "GOOGLE DATA FOR <strong>$location</strong>";
echo PHP_EOL;
print_r($g_demo);
echo "</pre>";


// ALL DONE
echo $form;
die();



// SIMPLE GEOCODER CLASS
class SimpleGeoCoder
{

    // DECLARE THE INPUT DATA
    public $location; // USE THIS FOR A FREEFORM QUERY, OR USE THESE PARTS
    public $address;
    public $city;
    public $state;
    public $zip;

    // DECLARE THE WORKING DATA
    private $precis; // TEXTUAL PRECISION FROM YAHOO
    private $yfp, $yahooUrl,  $yahooResponse,  $ydata;
    private $gfp, $googleUrl, $googleResponse, $gdata;

    // DECLARE THE OUTPUT DATA
    public $latitude;
    public $longitude;
    public $precision;
    public $warning;
    public $geocoder;

    // DECLARE THE CONSTRUCTOR
    public function __construct()
    {
        $this->latitude  = 0.0;
        $this->longitude = 0.0;
        $this->precision = FALSE; // WANT A VALUE OF 5 OR HIGHER, HIGHER IS BETTER
        $this->warning   = '';
        $this->geocoder  = '';
    }


    // DECLARE THE YAHOO! VERSION OF THE WORKHORSE
    public function geocodeYahoo()
    {
        $this->geocoder = 'Yahoo!';
        $yahooUrl       = "http://local.yahooapis.com/MapsService/V1/geocode?&appid=";
        if (defined("YAHOO_API"))
        {
            $yahooUrl .= YAHOO_API;
        }

        // YOU CAN ASK FOR A FREEFORM QUERY
        if ($this->location != '')
        {
            $yahooUrl .= "&location=" . urlencode($this->location);
        }

        // YOU CAN ASK FOR INDIVIDUAL PIECES OF AN ADDRESS
        else
        {
            $yahooUrl .= "&street=" . urlencode($this->address);
            $yahooUrl .= "&city="   . urlencode($this->city);
            $yahooUrl .= "&state="  . urlencode($this->state);
            $yahooUrl .= "&zip="    . urlencode($this->zip);
        }

        // EXECUTE YAHOO GEOCODER QUERY
        // NOTE - USE ERROR SUPPRESSION OR IT WILL BARK OUT THE YAHOO API KEY - ON FAILURE RETURNS HTTP 400 BAD REQUEST
        if ($yfp = @fopen($yahooUrl, 'r'))
        {
            $yahooResponse = '';
            while (!feof($yfp)) { $yahooResponse .= fgets($yfp); }
            fclose($yfp);
        }
        else
        {
            return FALSE;
        }

        // EXAMINE THE RESULT
        if ($yahooResponse != '') // NOT EMPTY, WE GOT DATA
        {
            $ydata    = new SimpleXMLElement($yahooResponse);

            // CHECK FOR ANY ERROR MESSAGE, IF NONE, EXTRACT THE DATA POINTS
            $y_err    = $ydata->Message;
            if ($y_err == '')
            {
                $this->precis    = $ydata->Result["precision"];
                $this->warning   = $ydata->Result["warning"];
                $this->latitude  = $ydata->Result->Latitude;
                $this->longitude = $ydata->Result->Longitude;
                $this->address   = $ydata->Result->Address;
                $this->city      = $ydata->Result->City;
                $this->state     = $ydata->Result->State;
                $this->zip       = $ydata->Result->Zip;

                // SET PRECISION TO A NUMBER VALUE
                if ($this->precis == 'zip')     { $this->precision = "5"; }
                if ($this->precis == 'street')  { $this->precision = "6"; }
                if ($this->precis == 'address') { $this->precision = "8"; }
            }
            else
            {
                return FALSE;
            }
        }

        // NO RESULT - SOMETHING IS SICK AT YAHOO
        else
        {
            return FALSE;
        }
        return TRUE;
    } // END function geocodeYahoo



    // DECLARE THE GOOGLE VERSION OF THE WORKHORSE
    public function geocodeGoogle()
    {
        $this->geocoder = 'Google';
        $googleUrl      = "http://maps.google.com/maps/geo?output=csv";
        if (defined("GOOGLE_API"))
        {
            $googleUrl .= "&key=" . GOOGLE_API;
        }

        // YOU CAN ASK FOR A FREEFORM QUERY
        if ($this->location != '')
        {
            $googleUrl .= "&q=" . urlencode($this->location);
        }

        // YOU CAN ASK FOR INDIVIDUAL PIECES OF AN ADDRESS
        else
        {
            $googleUrl .= "&q=" . urlencode(trim($this->address .' '. $this->city .' '. $this->state .' '. $this->zip));
        }

        // EXECUTE GOOGLE GEOCODER QUERY
        if ($gfp = @fopen($googleUrl, 'r'))
        {
            $googleResponse = '';
            while (!feof($gfp)) { $googleResponse .= fgets($gfp); }
            fclose($gfp);
        }
        else
        {
            return FALSE;
        }

        // EXTRACT THE DATA
        $gdata    = explode(',',$googleResponse);

        // RESPONSE CODE SHOULD BE '200' -- IF 602 - BAD ZIP CODE OR UNUSABLE ADDRESS
        if ($gdata[0] != '200')
        {
            return FALSE;
        }

        // GEOCODE ACCURACY - ZIP CODE = 5, HIGHER NUMBERS ARE BETTER
        $this->precision = $gdata[1];
        $this->latitude  = $gdata[2];
        $this->longitude = $gdata[3];
        return TRUE;
    } // END function geocodeGoogle

} // END class SimpleGeocoder

Open in new window

0
 
LVL 111

Accepted Solution

by:
Ray Paseur earned 2000 total points
ID: 36937035
After some testing and updating today, here is an improved version of the class.  HTH, ~Ray
<?php // RAY_geocoder.php
error_reporting(E_ALL);


// DEMONSTRATE HOW TO CALL THE YAHOO! AND GOOGLE GEOCODERS


// A NOMINAL DATA CLEAN-UP FUNCTION
function get_clean_text_string($s)
{
    return preg_replace('/[^A-Z 0-9_\.,\-#]/i', NULL, $s);
}


// API KEYS MAY BE REQUIRED
require_once('RAY_live_data.php');


// A FREEFORM LOCATION STATEMENT IS OPTIONAL (BUT NOT USED HERE)
$location = $a = $c = $s = $z = NULL;

// THESE ARE THE GET-FORM VARIABLES
$vars = array( 'a', 'c', 's', 'z' );

// FILTER THE ARGUMENTS
if (!empty($_GET))
{
    foreach ($_GET as $k => $v)
    {
        if (!in_array($k, $vars)) continue;
        $$k = get_clean_text_string($v);
    }
    if ($a != NULL) { $location .= $a . ' '; }
    if ($c != NULL) { $location .= $c . ' '; }
    if ($s != NULL) { $location .= $s . ' '; }
    if ($z != NULL) { $location .= $z . ' '; }
    $location = trim($location);
}

// PREPARE THE FORM
$form = <<<ENDFORM
<doctype html>
<head>
<title>$a $c $s $z Yahoo/Google Geocoder Query</title>
</head>
<body>
<form method="get">
Addr: <input type="text" name="a" autocomplete="off" value="$a" />
City: <input type="text" name="c" autocomplete="off" value="$c" />
ST:   <input type="text" name="s" autocomplete="off" value="$s" size="2" />
Zip:  <input type="text" name="z" autocomplete="off" value="$z" size="8" />
<input type="submit" value="Go" />
</form>
</body>
</html>
ENDFORM;

if (!$location)
{
    echo $form;
    die();
}


// PREPARE THE GEOCODER
$y_demo          = new SimpleGeoCoder;
$y_demo->address = $a;
$y_demo->city    = $c;
$y_demo->state   = $s;
$y_demo->zip     = $z;

// TEST THE YAHOO! GEOCODER
$y_demo->geocodeYahoo();
echo "<pre>";
echo PHP_EOL;
echo "YAHOO! DATA FOR <strong>$location</strong>";
echo PHP_EOL;
print_r($y_demo);
echo "</pre>";



// PREPARE THE GEOCODER
$g_demo           = new SimpleGeoCoder;
$g_demo->address  = $a;
$g_demo->city     = $c;
$g_demo->state    = $s;
$g_demo->zip      = $z;

// TEST THE GOOGLE GEOCODER
$g_demo->geocodeGoogle();
echo "<pre>";
echo PHP_EOL;
echo "GOOGLE DATA FOR <strong>$location</strong>";
echo PHP_EOL;
print_r($g_demo);
echo "</pre>";


// ALL DONE
echo $form;
die();



// SIMPLE GEOCODER CLASS
class SimpleGeoCoder
{

    // DECLARE THE INPUT DATA
    public $location; // USE THIS FOR A FREEFORM QUERY, OR USE THESE PARTS
    public $address;
    public $city;
    public $state;
    public $zip;

    // DECLARE THE OUTPUT DATA
    public $latitude;
    public $longitude;
    public $precision;
    public $warning;
    public $geocoder;

    // DECLARE THE CONSTRUCTOR
    public function __construct()
    {
        $this->latitude  = 0.0;
        $this->longitude = 0.0;
        $this->precision = 0;   // HIGHER IS BETTER, BUT Y AND G USE DIFFERENT SCALES
        $this->warning   = '';
        $this->geocoder  = '';
    }

    // DECLARE THE YAHOO! VERSION OF THE WORKHORSE
    public function geocodeYahoo()
    {
        $this->geocoder = 'Yahoo!';
        $yahooUrl       = "http://where.yahooapis.com/geocode?gflags=Q&appid=";
        if (defined("YAHOO_API"))
        {
            $yahooUrl .= YAHOO_API;
        }

        // YOU CAN ASK FOR A FREEFORM QUERY
        if ($this->location != '')
        {
            $yahooUrl .= "&location=" . urlencode($this->location);
        }

        // YOU CAN ASK FOR INDIVIDUAL PIECES OF AN ADDRESS
        else
        {
            $loc = urlencode
            ( trim($this->address)
            . ', '
            . trim($this->city)
            . ', '
            . trim($this->state)
            . ' '
            . trim($this->zip)
            )
            ;
            $yahooUrl .= "&location=" . $loc;
        }

        // EXECUTE YAHOO GEOCODER QUERY SEE http://developer.yahoo.com/geo/placefinder/guide/requests.html
        // NOTE - USE ERROR SUPPRESSION OR IT WILL BARK OUT THE YAHOO API KEY - ON FAILURE RETURNS HTTP 400 BAD REQUEST
        if ($yfp = @fopen($yahooUrl, 'r'))
        {
            $yahooResponse = '';
            while (!feof($yfp)) { $yahooResponse .= fgets($yfp); }
            fclose($yfp);
        }
        else
        {
            return FALSE;
        }

        // EXAMINE THE RESULT
        if ($yahooResponse != '') // NOT EMPTY, WE GOT DATA
        {
            $ydata    = new SimpleXMLElement($yahooResponse);
            // echo "<pre>";var_dump($ydata);

            // CHECK FOR ANY ERROR MESSAGE, IF NONE, EXTRACT THE DATA POINTS
            $y_err    = (string)$ydata->Error;
            if ($y_err)
            {
                $this->warning   = $ydata->ErrorMessage;
                return FALSE;
            }
            else
            {
                $this->precision = (string)$ydata->Result->quality;
                $this->latitude  = (string)$ydata->Result->latitude;
                $this->longitude = (string)$ydata->Result->longitude;
                $this->address   = (string)$ydata->Result->line1;
                $this->city      = (string)$ydata->Result->city;
                $this->state     = (string)$ydata->Result->statecode;
                $this->zip       = (string)$ydata->Result->postal;

                $this->location
                = (string)$ydata->Result->line1
                . ', '
                . (string)$ydata->Result->line2
                . ' '
                . (string)$ydata->Result->line3
                ;
            }
        }

        // NO RESULT - SOMETHING IS SICK AT YAHOO
        else
        {
            return FALSE;
        }
        return TRUE;
    } // END function geocodeYahoo



    // DECLARE THE GOOGLE VERSION OF THE WORKHORSE
    public function geocodeGoogle()
    {
        $this->geocoder = 'Google';
        $googleUrl      = "http://maps.google.com/maps/geo?output=csv";
        $googleUrl      = "http://maps.googleapis.com/maps/api/geocode/xml?sensor=false";
        if (defined("GOOGLE_API"))
        {
            $googleUrl .= "&key=" . GOOGLE_API;
        }

        // YOU CAN ASK FOR A FREEFORM QUERY
        if ($this->location != '')
        {
            $googleUrl .= "&address=" . urlencode(trim($this->location));
        }

        // YOU CAN ASK FOR INDIVIDUAL PIECES OF AN ADDRESS
        else
        {
            $loc = urlencode
            ( trim($this->address)
            . ', '
            . trim($this->city)
            . ', '
            . trim($this->state)
            . ' '
            . trim($this->zip)
            )
            ;
            $googleUrl .= "&address=$loc";
        }

        // EXECUTE GOOGLE GEOCODER QUERY SEE http://code.google.com/apis/maps/documentation/geocoding/
        if ($gfp = @fopen($googleUrl, 'r'))
        {
            $googleResponse = '';
            while (!feof($gfp)) { $googleResponse .= fgets($gfp); }
            fclose($gfp);
        }
        else
        {
            return FALSE;
        }

        // EXTRACT THE DATA
        $gdata    = new SimpleXMLElement($googleResponse);
        // echo "<pre>";var_dump($gdata);

        if ($gdata->status != 'OK') return FALSE;

        // GEOCODE ACCURACY - ZIP CODE = 5, HIGHER NUMBERS ARE BETTER
        $this->location  = (string)$gdata->result->formatted_address;
        $this->precision = (string)$gdata->result->type;
        $this->latitude  = (string)$gdata->result->geometry->location->lat;
        $this->longitude = (string)$gdata->result->geometry->location->lng;
        return TRUE;
    } // END function geocodeGoogle

} // END class SimpleGeocoder

Open in new window

0
 
LVL 2

Author Closing Comment

by:smetterd
ID: 37019121
Sorry it took so long to respond. My acct was inadvertently held due to credit card expiration. Thank you!
0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 37023694
Thanks for the points - it's a great question, ~Ray
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Rules and regulations were devised in order to maintain the integrity of a system. However, interpretation of rules can be quite tricky.
Mailbox Corruption is a nightmare every Exchange DBA wishes he never has. Recovering from it can be super-hectic if not entirely futile. And though techniques like the New-MailboxRepairRequest cmdlet have been designed to help with fixing minor corr…
Video by: Mark
This lesson goes over how to construct ordered and unordered lists and how to create hyperlinks.
Michael from AdRem Software explains how to view the most utilized and worst performing nodes in your network, by accessing the Top Charts view in NetCrunch network monitor (https://www.adremsoft.com/). Top Charts is a view in which you can set seve…
Suggested Courses

872 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question