Previous: PHP MySQL Database Class
Next: Intro to Programming PIC Microcontrollers in Linux

PHP Zip Code Range and Distance Calculation

April 19th, 2005

Planet Source Code Superior Code Winner!

This is a PHP class to do various calculations with zip codes. It's primary purposes are to calculate the distance between two zip codes and to extract all the zip codes that are within a range of a given zip code. All calculations are based on lattitude and longitude coordinates stored in the database. Included is a class file, demonstration file, and the sql files required to create and populate the MySQL tables.

Download:
Download Zip Code Range and Distance Script zipcode-1.2.0.zip

Do you like this code? Rate This Script at Hot Scripts.

If you enjoy this free PHP script and want to show your support, you can throw me a couple of bucks using paypal. Any donation, even just a buck, is always appreciated.

Here is a Postgresql port of the zip code database to contributed by max: zip_codes_postgresql.zip.

Version History

  • v1.2.0 [Oct 22, 2006] - Using a completely new database based on user contributions which resolves many data bugs. Added sorting to get_zips_in_range(), added ability to include/exclude the base zip from get_zips_in_range()
  • v1.1.0 [Apr 30, 2005] - Added Jeff Bearer's code to make it MUCH faster!
  • v1.0.1 [Apr 22, 2005] - Fixed a typo :)
  • v1.0.0 [Apr 12, 2005] - Initial Version

Screenshot of the MySQL database class in action

Setting up the Database

There are 6 files in the /sql directory which contain SQL statements intended for a MySQL database (though they should work just fine for any SQL database). The zip code database consists of over 40,000 records which would be too large a file for many configurations of phpMyAdmin. Therefore, I have broken the data into records of 10,000 resulting in 5 files. In phpMyAdmin, you can import these 6 files 1 at a time using the 'Import' tab. You MUST import the 'create_table.sql' file first, then each of the data files.

create_table.sql
data_1.sql
data_2.sql
data_3.sql
data_4.sql
data_5.sql

The database has changed since the last version. The data itself is comprised of data from multiple sources starting with the 2000 and 2004 US Census data. Unlike version 1.x of this class, the database is no longer comprised of a state AND zip_code table, but a single zip_code table. Additionally, the table is created with an index on the zip_code column.

The Class: zipcode.class.php

Constants

// constants for setting the $units data member
define('_UNIT_MILES', 'm');
define('_UNIT_KILOMETERS', 'k');
 
// constants for passing $sort to get_zips_in_range()
define('_ZIPS_SORT_BY_DISTANCE_ASC', 1);
define('_ZIPS_SORT_BY_DISTANCE_DESC', 2);
define('_ZIPS_SORT_BY_ZIP_ASC', 3);
define('_ZIPS_SORT_BY_ZIP_DESC', 4);
 
// constant for miles to kilometers conversion
define('_M2KM_FACTOR', 1.609344);

Data Members

decimals Read/Write. When returning distance values, this variable determines how many decimal places the result should be rounded to.
last_error Read-only. Holds a string value of the last error that occured. This can be used should a method return an error value to get a human readable version of the error.
last_time Read-only. Holds the last method execution time used to debug the efficiency of the get_zips_in_range() method.
units Read/Write. Holds the type of units to show results in. Must be either _UNITS_MILES or _UNITS_KILOMETERS

Methods

calculate_mileage($lat1, $lat2, $lon1, $lon2) Calculates the mileage between the two lattitude/longitude points: $lat1/$lon1 and $lat2/$lon2.
chronometer() Chronometer function from PHP manual used to time execution of get_distance() and get_zips_in_range() methods.
get_distance($zip1, $zip2) Returns the distance between the two zip codes: $zip1 and $zip2. If there is an error, false is returned. Since the function can return 0 when 2 zips are the same, you must evaluate for an error condition using:
if (get_distance($zip1, $zip2) === false) // error condition
get_zip_details($zip) Returns the details about the zip code: $zip. Details are in the form of a keyed array. The keys are: latitude, longitude, city, county, state_prefix, state_name, area_code, and time_zone. All are pretty self-explanitory. Returns false on error.
get_zip_point($zip) Gets the lattitude and longitude coordinates of the given zip code as a keyed array. The keys are: lat and lon. Returns false on error.
get_zips_in_range($zip, $range, $sort,$include_base) Finds all zip codes within $range from $zip as a keyed array sorted by $sort. If $include_base is set to true or not specified, then $zip is included in the results. If it is set to false, then $zip is excluded from results. Possible values for $sort are: _ZIPS_SORT_BY_DISTANCE_ASC (default), _ZIPS_SORT_BY_DISTANCE_DESC, _ZIPS_SORT_BY_ZIP_ASC, _ZIPS_SORT_BY_ZIP_DESC. The return value is a keyed array where the key is the zip code and the value is the distance from the base zip $zip. Returns false on error.


Categories
Web Development PHP

Related Posts


Technorati Tags

109 Responses to “PHP Zip Code Range and Distance Calculation”

RSS Subscription Comments RSS Feed

  1. dan Says:

    Does this script still exist? How can one obtain it? The zip file db-1.0.4.zip seems to contain something else

  2. Micah Says:

    I apologize. I have fixed the link. You can now download the zip code class.

  3. Andrew Says:

    thanks for the great app Micah... It was a huge time saver!

  4. Micah Says:

    Sure thing. I'm glad you liked it.

  5. Matt Says:

    Hi,

    Is there anything special that needs to be done to the script to make it work for English postcodes?

    The format of a typical english postcode is either AB10 1XL or AB1 1XL.

    Great script BTW... if I can get it working for England it will ahve saved me a HUGE amount of time ;)

    Matt

  6. Jonathan Says:

    Great script! I'm trying to loop through results passed by a URL variable

    i tried working with the get_zips_in_range.....

    get_zips_in_range(''.$_GET['zip'].'', $_GET['miles'], _ZIPS_SORT_BY_DISTANCE_ASC, true);
    {
    foreach ($zips as $key => $value) {
    echo "$key is $value miles away.";
    }
    }
    ?>

    And

    get_zip_details('$zips');

    if ($details === false) echo 'Error: '.$z->last_error;
    else {
    foreach ($details as $key => $value) {
    $key = str_replace('_',' ',$key);
    $key = ucwords($key);
    echo "$key: $value";
    }
    }
    ?>

    and ideas (basically trying to do a store locator off of your script

  7. Micah Says:

    Matt: the script uses a database populated with US zip codes. For UK postal codes, you would need to obtain data including at least the postal code, latitude, and longitude. I'm not sure if any such database exists for free.

  8. Micah Says:

    Jonathan: I don't have time to get into the details now, but when I have time I'll try to dig up an example of using the script as a store locator.

  9. Matt Says:

    Hi,

    Sorry, I should have said that already have a database full of UK postcodes along with latitudes and longitudes. In fact with no changes your class calculates the distance between postcodes fine... however it errors when trying to find those codes within a radius.

    In order to get it to work as far as i have, i simply took your demo.php file and changed the static zip_codes for postcodes in my DB. So literally with only changing those i have the first part working, i just can't see why the codes within a range isn't. All it comes up with is "Error:" but no actual error message.

    Any help you could give would be most appreciated. My first thoughts were that you might be performing some kind of validation on the zip_codes, in which case postcodes would fail... but as the distance between codes works i can't see how this would be the case. Below is an entry from my DB:

    postcode x y lattitude longitude
    AB10 392900 804900 57.135 -2.117

    I have obviously changed the SQL to match my tables. The X and Y values aren't used at all... they just came with the data.

    Thanks for your help

  10. Micah Says:

    Make sure the SQL in get_zip_point() as well as get_zips_in_range() matches your table column names. Also make sure that the order of the column names in the SQL are the same as my original ones since I'm using the array indexes and not their keys.

    Looking at it very quickly, I think maybe 0 rows are returned? Try changing the get_zip_point() method from this:

    if (!$r) {
    $this->last_error = mysql_error();
    return false;
    } else {
    $row = mysql_fetch_array($r);
    mysql_free_result($r);
    return $row;
    }

    to this:

    if (!$r) {
    $this->last_error = mysql_error();
    return false;
    } elseif (!$row) {
    $this->last_error = "Zip code $zip was not found in the database.";
    return false;
    } else {
    $row = mysql_fetch_array($r);
    mysql_free_result($r);
    return $row;
    }

  11. Matt Says:

    Oh dear... time for me to put on the idiot hat I think!

    Thank you for your help... you were right about one thing... 0 rows were being returned... but not because of any problems with SQL or coding... there were simply none in a 2 mile range!

    If you add the following to the code in get_zips_in_range then it will let you know there were 0 in range:

    if (!$r) { // sql error

    $this->last_error = mysql_error();
    return false;

    } elseif (mysql_num_rows($r)==0) {

    $this->last_error = "There were 0 zips within range";

    } else {

    Thanks for all your help... and i'll say it again, great script!

  12. Micah Says:

    Right. I noticed that as well. I'm going to make that little change and re-upload the file.

  13. sam sen Says:

    Micha - Awesome script! Just real quick, I'm going to point you to a post I made on a php developer forum. I was able to turn your script into a dealer locater type thing where it fetches records from a db table based on the zipcodes it found within X miles from a zipcode you enter. The only remaining issue I have is sorting the output by miles. I know your script does this by default but I took it a step further and grabbed information from a mysql table.

    If you can just briefly look at the code (it's not long) and give me some advice, I would appreciate it. I am not asking you to rewrite it for me, just some tips or advice so i can figure out where to go from here.

    http://forums.devshed.com/php-development-5/need-to-sort-output-by-distance-481694.html

  14. Free Ebook Covers - No Photoshop Required Says:

    Wow, this looks great. I'm going to try it out and if it works I'll be back to give you a donation. It looked like I might have to pay $50 for this solution elsewhere. Nice that others also contributed some performance boosters (based on your versions). I'm psyched!

  15. Davide Lasi Says:

    Hi!

    Could anyone please suggest me how to set up a similar database for Italian ZIP (CAP) codes?

    Thank you!

    Bye bye

    Davide

  16. Micah Says:

    I don't know of any free databases other than the US one. Often times the data is available (though perhaps as text file or other obscure method) through a government agency website.

  17. Adam Ellis Says:

    I see people asking about non-US databases, but is there a free up to date US zip code database that can downloaded somewhere?

  18. Micah Says:

    The download includes a free US database. However, it might not be entirely up to date--though I am using it on a commercial site with no problems to date. Whenever a change is found/needed and I am notified, I add it to the database.

  19. Rich Says:

    Rather than downloand and install, I'd like to run a query for zips 40 miles in radius from 21030 and have a list of zips returned. If anyone has this script online on an accessible webserver, can you please post a URL? Sounds like a wonderful scrip!
    Thanks,
    Rich

  20. Nick Says:

    Is there an update to the zip code database available?

  21. Micah Says:

    The database included in the zip is the most recent one I have.

  22. paul Says:

    how can i creat the form that will work with this data base !

  23. Ben Says:

    incredible...that's about all i can say. you have saved me TONS of time. I've gotta be honest, I usually don't leave comments but im just blown away by the script and had to say something. thanks a lot

  24. Mark Holtz Says:

    This is an excellent script. I ended up adapting it as a "Find A Club" script for District 39 Toastmasters since the core of the code is finding all matches by latitude/longitude (See www.district39.org/findclub , although the lookup is restricted to Northern California/Western Nevada clubs). I did change the lat/lon datatypes from FLOAT to DOUBLE for accuracy.

    As for sources of free zip codes that are up to date, see http://geocoder.ibegin.com/downloads.php . You may need to apply PHP string fuctions so that it isn't all caps. Not aware of a source for Canada ZIP codes that are FREE.

  25. Jonathan Says:

    Mark or Micah, would you be so kind as to share how you modded the script for the club search?! That would be amazing!

  26. Lancelight Says:

    This is an interesting little snippet but I think you could improve it if you get rid of the database requirement and add a mashup option. You could look at my store locator for an example of how that type of implementation would go. I once had a requirement of using a zipcode DB also but I soon found that people were complaining of zip data being out of date. So rather than updating the zips DB every few months, I decided to let someone else do that and used some geocoders instead (Yahoo and Google Maps for example). Something like this would have been nice when I first started coding my locator.

  27. Moody Says:

    Hi,
    Can some one please show me how I can include this in a simple form to calculat distance between two zip codes, thanks.

    Moody

  28. nathanR Says:

    i think u use the method "get_zips_in_range($zip,$range,$sort, $include_base)" shown above...
    i too am trying to get the closest location from the var $zip from the input of the form...
    i guess u need to input range as well, or predefine somehow
    hoep this helps i am noob lol 8^p

  29. nathanR Says:

    it is used in the demo.php file u get with the class.....

    $zips=$z->get_zips_in_range($zip, $range, $sort, $include_base) where $zip is a var from your

    html form

    so your form should look something like this:

  30. nathanR Says:

    Zip1

    above is my form code, when i hit submit it should pass zip1 to the method below

    echo 'A sample getting all the zip codes withing a range: 2 miles from '.$_POST['zip1'].'';

    $zips = $z->get_zips_in_range('zip1', 2, _ZIPS_SORT_BY_DISTANCE_ASC, true);

    if ($zips === false) echo 'Error: '.$z->last_error;
    else {

    foreach ($zips as $key => $value) {
    echo "Zip code $key is $value miles away from ".$_POST['zip1']".";
    }

    is this correct?

    cheers
    nate

  31. Josh Giese Says:

    I have been running this script for some time now. Its one of the best I have found out there. Thanks for maintaining this Micah.

  32. Martin Says:

    Can your script work with social engine?

    http://www.socialengine.net

  33. Eric Stern Says:

    Hi Micah,

    This is some awesome code and I've found it really useful for a project I've been working on recently. However, I found a bug in the get_zips_in_range function that's going to throw off the accuracy of the results. Your code uses:
    $lat_range = $range/69.172;
    $lon_range = abs($range/(cos($details[0]) * 69.172));
    I was doing the math to make sure things seemed fine, as I wanted to wrap my head around the calculations. Everything seemed fine until I hit the cosine in the $lon_range calculation - PHP's cos() function expects a value in radians, but it's receiving a value in degrees. The other functions use the deg2rad() function to correct this, but not here. I just wanted to let you know since it can produce results that include either a pretty accurate longitude range or something wildly inaccurate due to the cyclical nature of cosine. Just wrap $details[0] in deg2rad() and the problem goes away, as far as I can tell.

    I confirmed things with this snippet:
    $range = 5;
    echo 'With deg2rad: ';
    for ($lat = -89; $lat ";
    }

    echo 'Without deg2rad:';
    for ($lat = -89; $lat ";
    }

    You can see that without the deg2rad function, the longitude range doesn't change if you increase the latitude by 2pi degrees, which is of course a full wave of cosine in radians. Switch the 2*M_PI to 5 or something just to show how it bounces all over the place w/out deg2rad but goes from narrow to wide to narrow ranges with.

    I know I wouldn't have gotten where I had without finding your code, so thanks a ton for offering it up. I'd feel bad not giving back a bit, and expect a donation once the site is up and running :)

    Regards,
    Eric

  34. Eric Stern Says:

    Whoops, looks like wordpress parsed out half my code. Shoot me an email for the correct version.

  35. PHP Web Developer Says:

    Lovely work and a very usefull class ... converted to my own database and used for UK postcode database ... Cheers

  36. Bill Gates Says:

    In the problem Matt was having with the 0 rows, the elseif in get_zip_point() should be:

    } elseif (mysql_num_rows($r)==0) {

    not:

    } elseif (!$row) {

    Because $row can't be set until the else section, so it's always going to fail in the elseif check.

    Also, similarly to sam sen's problem, I still haven't figured out an efficient way to search for the zips in the master zip_code table (to get lat/long and zips in range), then marry that data with a store location table's zip codes -- and still sort by the mileage. I can build an SQL statement on the fly with the initial returned range like "select * from stores where zip IN ('12345', '12321', '12333', '12367')", then display the result and miles with something like:

    while ($row = mysql_fetch_array($stores_query))
    {
    echo $row[store_name] .' - '. echo $zips[$row['store_zip']] .' miles away';
    }

    Adding an "order by" won't really help because if someone searches for '12317', but '12314' and '12315' are in range, ordering by zip will display the other zips before the one the user actually searched for.

    It probably would work with a subselect (customizing queries in the class file) but I'm NOT working with mysql 4.1 -- so I can't test this. Anyone that has a solution, please post. I'd appreciate some ideas.

  37. GreenBoy Says:

    Mica

    Great script - thank you.

    Now the big question for me is how to reverse the search.

    Let me explain - what I want is to get a list of people who are within their own specified travelling distance of an event when posted.

    So for example you have a persons postcode, lat and long, and the distance they are prepared to travel in miles.

    This therefore works like a radial search but in reverse s you need to go through every person in the list and effectively do a radial search based on them being in the center and seeing if the event is within the search radius.

    This is ok with only a small Db but i am guessing it gets longwinded for a large one.

    Sorry I haven’t posted code but I’m a noob so anything i post is likely to be a red-herring.

    I am however keen on making this work so glad of any pointers or assistance you or others can give.

    Many thanks

    GreenBoy

  38. Max Says:

    Hi - Great Work!!

    I've converted the SQL so that it works for PostgreSQL, Drop me a line if you want me to ZIP the SQL files and email them to you.

    Max

  39. SpeedyWord Says:

    Hey Micah. Great work.
    I've integrated your code into my site.. http://www.SpeedyWord.com/

  40. John Says:

    hello I'm new to php, I'm trying to make a function that will allow users to calculate distance between two zip codes and based on the miles give a dollar value. I have the database ready I just need to get this form to work. Thank you for you Time.

  41. Mark H. Says:

    Hey Micah, this is great work.

    Btw, Google has posted a tutorial on how to create a store finder using their map API...maybe this helps someone here: http://code.google.com/support/bin/answer.py?answer=87134&topic=&ctx=sibling

    -Mark

  42. Mark Holtz Says:

    So, how did I adapt it as a club finder....

    As the routine is designed, the results from a MySQL query is returned as Zip Code, Latitude, and Longitude. If you geocode every address in the club database with a latitude and longitude, you simply point the routine to search the club table rather than the zip code table and return the club numbers based upon the latitude/longitude results. The initial geocoding can be done through Google Maps API.

    So, for find-a-club, the programming steps are:
    1. Get the address and range of seach (I limit mine to 99 miles).
    2. Parse the address through Google Maps to return the latitude and longitude.
    3. Plug in the latitude and longitude into a modified version if Micah's routine to retrieve the club numbers and mileage.
    4. Take the club numbers and pull up the results.

    Thanks, Micah!

  43. Brandon Says:

    I have integrated this script into an open source script I am developing called Open Auto Classifieds http://openautoclassifieds.com and this is a very helpful script and just wanted to say thank you!

  44. Nabeel Says:

    I m getting error when am find the range of zip code 96006. can any one help me out that why i m faceing this error ? n how can i slove it

  45. Chris Says:

    Excellent script! Like others here, I hope to use it to build a proximity search application. I'm wondering though, if its possible to call the get_zip_details function within a for loop that uses get_zips_in_range? And if so, what would the syntax to do that?

    I tried using a for loop within a for loop to no avail.

    If I am able to do this, then the proximity search and details about each are accomplished all in one shot.

    I am, of course, planning on modifying the script to suit my needs, but if I could figure a way to pull the details of each while going through the proximity search, that would be great!

  46. chad Says:

    Trying to set up a distance calculator where a person selects a location and then enters their zip code and the distance calculator spits out the distance. I'm stuck. uploaded is what i believe is needed for the basic premise to work, but it isn't. sos. mayday. HELLLLLLLP.

    Zip Code Range and Distance Calculation Demo

    BODY, P { font-family: sans-serif; font-size: 9pt; }
    H3 { font-family: sans-serif; font-size: 14pt; }

    Sacramento, CA
    Colorado Springs, CO
    Denver, CO
    Berlin, CT
    Jacksonville, FL
    Orlando, FL
    Atlanta, GA
    Boise, ID
    Central Chicago, IL
    Chicago, IL
    Elgin, IL
    Woodridge, IL
    Indianapolis, IN
    Merrillville, IN
    Baltimore, MD
    Frederick, MD
    Washington DC, MD
    Detroit, MI
    Grand Rapids, MI
    Southgate, MI
    Westland, MI
    Minneapolis, MN
    Springfield, MO
    St. Louis, MO
    Albany, NY
    Buffalo, NY
    Long Island, NY
    Niagara Falls, NY
    Syracuse, NY
    Charlotte, NC
    Greensboro, NC
    Akron, OH
    Canton, OH
    Cincinnati, OH
    Columbus, OH
    E. Cleveland, OH
    Mentor, OH
    W. Cleveland, OH
    Portland, OR
    Philadelphia, PA
    Pittsburgh, PA
    Greer, SC
    Rapid City, SD
    Knoxville, TN
    Nashville, TN
    Arlington, TX
    Ogden, UT
    Provo, UT
    Salt Lake City, UT
    Newport News, VA
    N. Virginia, VA
    Seattle, WA
    Spokane, WA
    Milwaukee, WI

    Enter your zip code

    function calculate() {
    $z = new zipcode_class;
    $miles = $z->get_distance($value, &zip);
    }
    {
    if ($miles === false) echo 'Error: '.$z->last_error;
    else echo "Zip code value is $miles miles away from &zip.";
    }

  47. Indigo Says:

    Where can I get the most recent ZIP data? It's not really obvious where to get it on the USPS site - and search doesn't help.

    Also, does anyone have any suggestions about making this compatible with Mexican Postal codes? I'd like someone to be able to do a search for a Texas ZIP, that would also return results from Mexican postal codes (which follow the same 5 digit convention as US ZIP codes)

  48. Norman Says:

    This is great script!

    Thanks to Chad (2 posts before mine) i was getting an error before i added:

    $z = new zipcode_class;

    That fixed everything!

  49. Mauricio Zuniga Says:

    Thank you for this excellent Class. I will be trying to update the data with the 2006 data set that I have found on the web. I will post a mysql dump (haha!) for everyone there. Please check back soon.

  50. Norman Says:

    After dozens of tests, maybe i have not implemented this correctly in my application, but sometimes it works, and sometimes it does not. That's the best way to describe it. It actually works sometimes, and not others. I do have my own dedicated server, so i don't think it's a server problem.

    Not sure where to go from here.

  51. SlinkyAZ Says:

    I have the "up to today" zip code date for the US and Canada. When i say up to date.....it even has the correct cords for all the new zips and zip code moves that was done late last year.....all up to dat lat and long.

    Here is the trade....I have a real large database....30 million records...and i want to split it into 999 exact databases, based on the first 3 digits of the zip code. Now i can split servers, backups alot easier with smaler databases.

    Seems that the easiest way would be a fast zip code module, that calculates, and searches multiple databases based on the result. By using 3 digits, it really would only search 2 or 3.

    Just for the record....we own these files....they were not scarfed somewhere. We bought 20 databases, and did a compare, then a manual lookup.

    I really need the help with this.

  52. Norman Says:

    FYI, i have it implemented just fine now.

    Thanks again for the great script.

  53. Rob Says:

    Not sure if anyone caught this yet, but when calling a function that passes zipcodes, the zips should be enclosed in single quotes. This will enable lookups of zips that start or end with zeros. Without the quotes, I got a weird error telling me it couldn't find info for a zip string that I had not entered. Great script nonetheless!

  54. Lawrence Says:

    Could u apply this to an auto search script for me and teach me how you did it by me viewing the files with you over the phone or using a confence program ? If so what would u charge me for that? and when could we get started if yes? how long do u think it would take to show me. I want to be able to know how this worked when applied to listings in database when using search form n such, thanks Lj

  55. Donny Lairson Says:

    One other thing I was thinking about was adding an XML page write function to the zips in range. The math portion of this is terrible on high volume servers and once its done once storing an XML of the data from the db to just pull the hardfile xml would be much much faster. Loading data into a memory table for page work is uber fast compared to redoing the math.

  56. Donny Lairson Says:

    function get_zip_point($zip) {

    // This function pulls just the lattitude and longitude from the
    // database for a given zip code.

    $sql = "SELECT lat, lon from city_location WHERE zip_code ='$zip' limit 1";

    Added the Limit 1 to this sql statement this prevents mysql from continue to search for more like it when we only needed one set of lat and lon from the db for another function.

    Slight speed increase.

  57. Donny Lairson Says:

    Just found this cause I was having so much trouble with the range finding function.

    Mysql will do this type of search for you directly.

    SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM city_location HAVING distance

  58. chad Says:

    anyone help get this thing working, its like the last piece of a puzzle to get the locator operational.

    Here is the complete code:

    require_once('zipcode.class.php');("phpsqlsearch_dbinfo.php");
    require_once('zipcode.class.php');("zipcode.class.php");
    $outstanding = echo "$zip1 is $miles miles from $zip2";
    function calculate() {
    $z = new zipcode_class;
    $miles = $z->get_distance($zip1, $zip2);
    }
    {
    if ($miles === false) echo 'Error: '.$z->last_error;
    else $outstanding
    }

    Your Zipcode:   
    Supply Zipcode:   

  59. Donny Lairson Says:

    Chad with the code i posted right above yours. Drop this database and geo encode your informatin and use the sql query. its lightning fast and eliminates the need for all these scripts. Not that this wasn't brilliant at the time but there is just a better way.

  60. mikep Says:

    Donny,
    Could you explain your sql query and what exactly it replaces and/or how to use it? Does it do the range calculation, the distance calculation or both? What are the constants 3959, 37 and -122?

  61. RedMol.com Says:

    If you add this code to your database schema SQL, it will be faster:

    create index lat_idx on zip_code(lat);
    create index lon_idx on zip_code(lon);

  62. Jim Says:

    Micah,

    This script is excellent and wanted to let you know how much I appreciate your efforts. One day when I am capable of writing sound classes I hope to give back to the coding community as well - pay it forward!

  63. Ashok Says:

    hai great script thanks a lot

  64. Yogesh Says:

    Hi Greate script it reduced my headache very much because I had got very irritated due to this functionality
    Thanks once again

  65. Gus Says:

    I haven't tried the script yet but I found interesting stuff for canadians : http://www.populardata.com/downloads.html

  66. Jose Luna Says:

    Hi Micah, great job.
    I need to know if there is a newer database, how often I need to update and from where can I obtain such update.
    Thanks in advance.

  67. Jack Says:

    Donny Lairson example above got me digging. I could not figure out how to implement it at first. for closest 20 results within 25 miles of location (45.1539, -93.1425)

    $latitude= 45.1539;
    $longitude= -93.1425;

    SELECT id, ( 3959 * acos( cos( radians( $latitude ) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians( $longitude ) ) + sin( radians( $latitude ) ) * sin( radians( lat ) ) ) ) AS distance
    FROM zip_code
    HAVING distance

  68. Jack2 Says:

    since it doesn't like "less than" symbol

    SELECT id, ( 3959 * acos( cos( radians( $latitude ) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians( $longitude ) ) + sin( radians( $latitude ) ) * sin( radians( lat ) ) ) ) AS distance
    FROM zip_code
    HAVING distance (less than symbol) 25
    ORDER BY distance
    LIMIT 0 , 20;

  69. Roman weaver Says:

    ok... question.. i would like to know how to ammend the code to include.. distances from 0-25 miles and 25-50 miles.. how would i do that?

  70. Michael Novello Says:

    Something is wrong with your script: When people search for members located with 5 miles, I get 0 results. When I search for members located within 10 miles, I get members located within 4 miles, etc. For some reason, the search radius only works if it is 2 or 3 miles beyond whatever you're searching for...I have it set to use miles, so I'm not sure whta the problem is.

  71. Lalit Says:

    Hi,

    My requirement as below
    e.g. I have post CH64 and i want to find the postcodes within 10miles from CH64 (redius).

    Is it possible with one formula?

  72. Richard Says:

    The zip file only contains the SQL script...where is the PHP file/files?

  73. W. Shawn Wilkerson Says:

    Roman,

    You should be able to do the first by simply looking in a 25 mile radius.

    The second would be to pull a query which would be NOT IN the first query.

    In other words pull a query on the 50 mile radius and then remove from that list anything less than 25 miles, by having the query NOT IN a 25 mile query.

  74. Randy9000 Says:

    How can I configure the script to accept mileage from Washington to California? It looks like it will only do about 50 miles as of now.

  75. M Jensen Says:

    Micah,

    How do I implement this program? I'm attempting to calculate distances from each line of my spreadsheet. I currently have about 6,600 addresses so therefore would like to obtain 1 match for each unit, roughly 45.6 million outputs to rank by distance. Or I can do a smaller data load, by state or such?

    Do you have any suggestions?

    Thanks

  76. Michael Arnold Says:

    Hi Micah,

    Thanks a bunch for this great app.Just left you a donation.

    Michael

  77. Jason Says:

    I did some benchmarking measuring time between a PHP script + MySQL to calculate proximity and implementing proximity search via MySQL only (see Jack's comment above or http://code.google.com/support/bin/answer.py?answer=87134&topic=&ctx=sibling).

    With the MySQL only way, you take an initial performance hit due to the query not being cached yet. The first time averages about 0.400 seconds. Whereas the initial query the PHP way is only about 0.100 seconds.

    However, once the query is cached (for newbies, MySQL does this automatically) the MySQL only way is slightly faster than the PHP calculation, ~0.0007 sec and ~0.0009 respectively. BTW, changing the zipcode or range results in a new uncached query.

    Since the MySQL only way is also more accurate than the PHP+MySQL way, I'd say go with the MySQL only way.

    Note: Benchmarks performed with PHP Version 5.2.4 and MySQL version 5.0.51a on a minimal load server. Performance may be different on a high load server.

  78. maan Says:

    http://www.weberdev.com/get_example-4131.html

  79. James Says:

    Thanks for the script Micah!!!

    I'm trying to do a select based on the zip codes that are returned, however, the $key variable is changed every time the value is returned and ends up being only the last value returned. Is there a way to stick this into an array so I can read all the values and do a select based upon all values? For example:

    1. user enters a zip code and distance and submits (have this working)
    2. user is returned all zip codes in range
    3. SELECT * from Table WHERE $clientlocation = $anyzipreturned

    Thanks!
    James

  80. Adam Moro Says:

    Just downloaded and giving it a run. Looks great, thanks for all the notes!

  81. Jerome Says:

    Great class. You should have someone update the Zip Code sql soon though, the united states government just added a bunch of new zip codes into existing areas. the change comes into effect near the start of 2009

  82. Runtim Says:

    This code really helped me simplify my store locator query. Rather than query all of the zip codes for a radius, I first queried the initial zip entered for it's lat and lon, then queried it against my stores table that I entered the lat and long for each. This sped up the query since I only have 1400 stores. There was no need to do a search for zip codes that were not included in my stores table.

  83. Roy Lindauer Says:

    Thank you so much for this! Saved me a bunch of time and money :)

  84. Roy Lindauer Says:

    Donation sent!

  85. Ted C Says:

    Thanks! I had downloaded a few others before this and they didn't cut it. Yours worked as described. It is well documented on this site. And, basically, it just works which is what you want. I've created this new crowd sourced shopping list creator called SLIRM and it really needed the ability locate stores by zip code. I couldn't have done it with out your help.

    Thanks and when I get some cash I'll send you a fat donation.

  86. Chris Everson Says:

    Just ran into an interesting problem...

    When I was trying to run get_zips_in_range(); with a 50 mile radius in a dense metropolitan are (take Houston's 77008 for instance) it fails to come back with an array. Actually, the largest radius I could get with 77008 is 11 miles.

    I'm working on figuring out the problem and a possible solution, but if anyone has run into this before, I'd love for a little hint or two on getting this functional!

    Thanks!

  87. Jake Cortese Says:

    Thank you for this class. Very useful.

  88. Dean Says:

    I'm having a problem. I'm using it in a Drupal website and modified the code to use Drupal's database abstraction at access the zip_code table. That shouldn't affect the relevant code, but now I get this error:

    Fatal error: Cannot use object of type stdClass as array in /home/content/m/l/a/mlamberth/html/drupal/sites/all/modules/homeform/zipcode.class.php on line 176

    Line 176 is this:

    $lon_range = abs($range/(cos($details[0]) * 69.172));

    I have very little OO experience and I'm using php 5. Any ideas?

  89. Chris Everson Says:

    Nevermind what I said up above. It turned out that even though I double checked my MySQL installation, I overlooked a couple settings. Triple checking caught it.

    Party time.

  90. Steph Says:

    Did anyone get this code to function as a store locator? I'm confused as to which code will help me get my goal solved.

    Sincerly,
    Stephanie :P

  91. Morris Sasser Says:

    Have you considered updating this from mysql to mysqli?

  92. ray Says:

    hey Micah Carrick, thanks for the post. however do you know where i can get access to a data base that uses actual road distance between two zip codes as with google maps? thanks

  93. Penny Says:

    Thanks for the great script! Is there a method to sort the following:?
    $miles = $z->get_distance($zip1, $zip2)
    echo 'Company: '.$row['mycompany'].' is '. $miles .' away from '.$zip1.'';

    BTW
    This saved me days

  94. J Says:

    I love you.

  95. brian Says:

    is there any code for calculating distance between latitutes and longitudes? (geo code)
    if anyone know, please let me know.
    thanks.

  96. Alex Says:

    Wow after a lot of search in google I finally found the perfect scripts I was looking for. I need to show business which are within few miles of a particular zip code. I really liked the alogorithm you have used. Thanks dude for the script, this is really a time saver.

  97. Ayurveda Says:

    Hi, looks promising... where can we see it live? thnaks

  98. Tyler Says:

    What an elegant script! I can't tell you how much time you've saved me.

  99. Ibrar Kamboh Says:

    Hi,

    Dears i have implement this script to my website but my requirement is that i want to find zip codes between two ranges of miles distance mean let say distance between 10 to 20 miles.
    The current script is working for only one distance range.
    How i can do it help me please.
    I am waiting for your response.
    It is a great script and nice effort by the developer.

  100. Diego Says:

    Thanks a lot. Very helpful tool.

  101. Obinna Says:

    Hey,

    I will like to know if you can customize the script to enable a search of a place, say, Archdale South Carolina USA, and then do an advance search within the results displayed to further search for nearby places in the database say 5 miles around Archdale South Carolina USA search results...that is search with a search result. If you can, I will like to make a sizeable donation. Thank you.

    Obinna

  102. Deano Says:

    Great script that provided me with a great platform for learning about geocoding and radius search requirements.

    I have implemented with both UK and US sites...

    I have also extended the script (as per some of the comments above) to use google to update the information on the fly as searches are performed. This gives me the best of both worlds...

    Fast local Data, No need to update...

  103. Cesar Quinteros Says:

    Great app Micah! There are other similar installable apps on the web which didn't work too well...but yours did (v1.2.0). Awesome work!

  104. Robbo Says:

    Hi I am new to php and mysql. I build my websites using Drupal does anyone know how I can implement this with drupal.

    I need to be able to find the distance using a search with a dropdown box saying say Bricklayer, Builder, Plumber etc and the user inputs there postcode. this will give the result of there nearest trade..

    If someone helps I will be very greatfull..

    Thanks

    Robert

  105. Matt Toca Says:

    Alright... I just slipped in small error statements to find out where the get_zips_in_range function might be getting stuck. Here are the places:

    Where is first starts:

    $details = $this->get_zip_point($zip); // base zip details
    if ($details == false) {
    $this->last_error = "There is no Zip!";
    return false;
    }

    And then at the end to see if the $return array is empty:

    if (empty($return)) {
    $this->last_error = "The Array is Empty!";
    return false;
    }
    return $return;

    When I reload demo.php, the "The Array is Empty!" error pops up now instead of it being blank. Why would $return be empty? I've tried a range of 100 miles. I've even tried hard wiring the zip code and the range into the equations. Any suggestions?

  106. Willie Says:

    Hello to you all:

    Can someone help me on using the get_distance method. I would like to use it by entering only on zipcode in the the html form, and the second zip code would come from an array of zipccodes. This array of zip codes is the answer from the get_zips_in_range(); method. That is, the user inputs the a zip code and the distance in miles, and the result would be the range of zip codes within that distance as well as the distance from each zip code in from get_zips_in_range() to the zip code the user entered.

    In other words, how can I use the get_distance method with the latitude, longitude from the database, a range of zip codes from the get_zips_in_range method, a zip code entered by the user, and the distance entered by the user as well.

    Thanks.

  107. John Says:

    This thing is amazing!

    I ran into a couple of bugs here and there, nothing major that I wasnt able to fix myself. But the only thing that really is holding me back from being able to use this is database updates. Theres a bunch of new zipcodes in the USPS and those need to also be included. Its not something I can really expect Micah to try to keep up on his own, so if ANYONE has a link to an up to date database that can be imported into MySql, I think it would be beneficial to everyone.

  108. Oleg Says:

    Has anyone been able to do a reverse zip code lookup ?
    I need to be able to lookup a "city name, state" and get a zip code that corresponds to that location. Typical lookup on google returns a list of zip codes but even jus the first one would suffice. I have been searching the internet for a while now and can't find a usefull script that does that .

  109. Maryam Says:

    Thanks, that is a great PHP script. There is just a problem, that I want to search for a

    distance from a city not specific zipcode. I need the database of Us cities longitude and latitude and I couldn't find it.

    I will apprecite if anybody give me a clue.

    Thanks

Leave a Reply