...

View Full Version : zip code searching



doodguy
05-06-2009, 10:43 PM
Hey all. I'm VERRRRRY CONFUSED by this (Sorry) :( But pls point me in right direction. I found a link to a zip file which built all zip codes in my database, and it gave me a class with following code:


<?php
/*******************************************************************************
* ZIP Code and Distance Claculation Class
*******************************************************************************
* Author: Micah Carrick
* Email: email@micahcarrick.com
* Website: http://www.micahcarrick.com
*
* File: zipcode.class.php
* Version: 1.2.0
* Copyright: (c) 2005 - Micah Carrick
* You are free to use, distribute, and modify this software
* under the terms of the GNU General Public License. See the
* included license.txt file.
*
*******************************************************************************
* VERION 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
*
*******************************************************************************
* DESCRIPTION:

* A PHP Class and MySQL table to find the distance between zip codes and
* find all zip codes within a given mileage or kilometer range.
*
*******************************************************************************
*/



// 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);


class zipcode_class {
var $last_error = "";

// last error message set by this class
var $last_time = 0;

// last function execution time (debug info)
var $units = _UNIT_MILES;

// miles or kilometers
var $decimals = 2;

// decimal places for returned distance






function get_distance($zip1, $zip2) {

// returns the distance between to zip codes. If there is an error, the
// function will return false and set the $last_error variable.

$this->chronometer();

// start the clock


if ($zip1 == $zip2) return 0; // same zip code means 0 miles between. :)

// get details from database about each zip and exit if there is an error

$details1 = $this->get_zip_point($zip1);

$details2 = $this->get_zip_point($zip2);

if ($details1 == false) {
$this->last_error = "No details found for zip code: $zip1";
return false;
}


if ($details2 == false) {
$this->last_error = "No details found for zip code: $zip2";
return false;
}







// calculate the distance between the two points based on the lattitude
// and longitude pulled out of the database.





$miles = $this->calculate_mileage($details1[0], $details2[0], $details1[1], $details2[1]);
$this->last_time = $this->chronometer();



if ($this->units == _UNIT_KILOMETERS) return round($miles * _M2KM_FACTOR, $this->decimals);




else return round($miles, $this->decimals); // must be miles

}





function get_zip_details($zip) {

// This function pulls the details from the database for a
// given zip code.





$sql = "SELECT lat AS lattitude, lon AS longitude, city, county, state_prefix, state_name, area_code, time_zone FROM zip_code WHERE zip_code='$zip'";$r = mysql_query($sql);


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


else {
$row = mysql_fetch_array($r, MYSQL_ASSOC);
mysql_free_result($r);
return $row;
}

}












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 zip_code WHERE zip_code='$zip'";
$r = mysql_query($sql);




if (!$r) {
$this->last_error = mysql_error();

return false;
}



else {
$row = mysql_fetch_array($r);


mysql_free_result($r);
return $row;
}
}

















function calculate_mileage($lat1, $lat2, $lon1, $lon2) {

// used internally, this function actually performs that calculation to
// determine the mileage between 2 points defined by lattitude and
// longitude coordinates. This calculation is based on the code found
// at http://www.cryptnet.net/fsp/zipdy/






// Convert lattitude/longitude (degrees) to radians for calculations



$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
$lat2 = deg2rad($lat2);
$lon2 = deg2rad($lon2);





// Find the deltas
$delta_lat = $lat2 - $lat1;
$delta_lon = $lon2 - $lon1;




// Find the Great Circle distance
$temp = pow(sin($delta_lat/2.0),2) + cos($lat1) * cos($lat2) * pow(sin($delta_lon/2.0),2);
$distance = 3956 * 2 * atan2(sqrt($temp),sqrt(1-$temp));
return $distance;
}














function get_zips_in_range($zip, $range, $sort=1, $include_base) {

// returns an array of the zip codes within $range of $zip. Returns
// an array with keys as zip codes and values as the distance from
// the zipcode defined in $zip.


$this->chronometer(); // start the clock

$details = $this->get_zip_point($zip); // base zip details


if ($details == false) return false;

// This portion of the routine calculates the minimum and maximum lat and
// long within a given range. This portion of the code was written
// by Jeff Bearer (http://www.jeffbearer.com). This significanly decreases
// the time it takes to execute a query. My demo took 3.2 seconds in
// v1.0.0 and now executes in 0.4 seconds! Greate job Jeff!






// Find Max - Min Lat / Long for Radius and zero point and query
// only zips in that range.



$lat_range = $range/69.172;
$lon_range = abs($range/(cos($details[0]) * 69.172));
$min_lat = number_format($details[0] - $lat_range, "4", ".", "");
$max_lat = number_format($details[0] + $lat_range, "4", ".", "");
$min_lon = number_format($details[1] - $lon_range, "4", ".", "");
$max_lon = number_format($details[1] + $lon_range, "4", ".", "");
$return = array(); // declared here for scope



$sql = "SELECT zip_code, lat, lon FROM zip_code ";


if (!$include_base) $sql .= "WHERE zip_code <> '$zip' AND ";

else $sql .= "WHERE ";
$sql .= "lat BETWEEN '$min_lat' AND '$max_lat' AND lon BETWEEN '$min_lon' AND '$max_lon'";



$r = mysql_query($sql);


if (!$r) { // sql error


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

}



else {
while ($row = mysql_fetch_row($r)) {

// loop through all 40 some thousand zip codes and determine whether
// or not it's within the specified range.


$dist = $this->calculate_mileage($details[0],$row[1],$details[1],$row[2]);


if ($this->units == _UNIT_KILOMETERS) $dist = $dist * _M2KM_FACTOR;



if ($dist <= $range) {
$return[str_pad($row[0], 5, "0", STR_PAD_LEFT)] = round($dist, $this->decimals);
}
}





mysql_free_result($r);
}

// sort array
switch($sort)
{
case _ZIPS_SORT_BY_DISTANCE_ASC:
asort($return);
break;

case _ZIPS_SORT_BY_DISTANCE_DESC:
arsort($return);
break;

case _ZIPS_SORT_BY_ZIP_ASC:
ksort($return);
break;
case _ZIPS_SORT_BY_ZIP_DESC:
krsort($return);
break;
}


$this->last_time = $this->chronometer();

if (empty($return)) return false;
return $return;
}




function chronometer() {

// chronometer function taken from the php manual. This is used primarily
// for debugging and anlyzing the functions while developing this class.




$now = microtime(TRUE); // float, in _seconds_

$now = $now + time();
$malt = 1;
$round = 7;



if ($this->last_time > 0) {
/* Stop the chronometer : return the amount of time since it was started,
in ms with a precision of 3 decimal places, and reset the start time.
We could factor the multiplication by 1000 (which converts seconds
into milliseconds) to save memory, but considering that floats can
reach e+308 but only carry 14 decimals, this is certainly more precise */




$retElapsed = round($now * $malt - $this->last_time * $malt, $round);
$this->last_time = $now;
return $retElapsed;
} else {
// Start the chronometer : save the starting time


$this->last_time = $now;
return 0;
}
}

}



Problem is I don't know how to use it :( Can someone point me in right direction: What I need to do is pull a zip code from stored user info (easy) and then find x miles from that zip code with this class, get all zip codes in range, and then sort through a mysql search statement to include profile in search.

Guessing I'm gonna store the results in an array? Pls help with how to use this thank you very much in advance

venegal
05-06-2009, 10:50 PM
function get_zips_in_range($zip, $range, $sort=1, $include_base) {

// returns an array of the zip codes within $range of $zip. Returns
// an array with keys as zip codes and values as the distance from
// the zipcode defined in $zip.


That's the method you need.

doodguy
05-06-2009, 11:04 PM
vengel, I figured that, so copied it as so for testing, but don't know what to echo, trying to read it but don't follow this=>val much. have hard time reading way its written.


//Get starting zip code and surround zip codes
$zipcode=$_GET['zipcode'];
$range=$_POST['distance'];




function get_zips_in_range($zipcode, $range, $include_base) {
// returns an array of the zip codes within $range of $zip. Returns
// an array with keys as zip codes and values as the distance from
// the zipcode defined in $zip.


$this->chronometer(); // start the clock

$details = $this->get_zip_point($zipcode); // base zip details


if ($details == false) return false;


// This portion of the routine calculates the minimum and maximum lat and

// long within a given range. This portion of the code was written

// by Jeff Bearer (http://www.jeffbearer.com). This significanly decreases

// the time it takes to execute a query. My demo took 3.2 seconds in

// v1.0.0 and now executes in 0.4 seconds! Greate job Jeff!






// Find Max - Min Lat / Long for Radius and zero point and query
// only zips in that range.



$lat_range = $range/69.172;
$lon_range = abs($range/(cos($details[0]) * 69.172));
$min_lat = number_format($details[0] - $lat_range, "4", ".", "");
$max_lat = number_format($details[0] + $lat_range, "4", ".", "");
$min_lon = number_format($details[1] - $lon_range, "4", ".", "");
$max_lon = number_format($details[1] + $lon_range, "4", ".", "");
$return = array(); // declared here for scope



$sql = "SELECT zip_code, lat, lon FROM zip_code ";


if (!$include_base) $sql .= "WHERE zip_code <> '$zipcode' AND ";

else $sql .= "WHERE ";
$sql .= "lat BETWEEN '$min_lat' AND '$max_lat' AND lon BETWEEN '$min_lon' AND '$max_lon'";



$r = mysql_query($sql);


if (!$r) { // sql error


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

}



else {
while ($row = mysql_fetch_row($r)) {


// loop through all 40 some thousand zip codes and determine whether

// or not it's within the specified range.


$dist = $this->calculate_mileage($details[0],$row[1],$details[1],$row[2]);


if ($this->units == _UNIT_KILOMETERS) $dist = $dist * _M2KM_FACTOR;



if ($dist <= $range) {
$return[str_pad($row[0], 5, "0", STR_PAD_LEFT)] = round($dist, $this->decimals);
}
}





mysql_free_result($r);
}

// sort array

switch($sort)
{

case _ZIPS_SORT_BY_DISTANCE_ASC:

asort($return);

break;


case _ZIPS_SORT_BY_DISTANCE_DESC:

arsort($return);

break;


case _ZIPS_SORT_BY_ZIP_ASC:
ksort($return);
break;
case _ZIPS_SORT_BY_ZIP_DESC:

krsort($return);
break;
}


$this->last_time = $this->chronometer();

if (empty($return)) return false;
return $return;
}


I'm playing around through trying to follow it.

venegal
05-06-2009, 11:18 PM
Ah no, that's not how to use a class. You instantiate it and call its public methods, and the beauty of it is, that you don't have to care about how it's written.

First you include the file containing your class, then you instantiate, like this:

$zipCalculator = new zipcode_class();

and after setting $zipcode and $range use it like this:


$zipCodesWithinRange = $zipCalculator->get_zips_in_range($zipcode, $range, 1, true);

doodguy
05-06-2009, 11:28 PM
and then just echo zipcodes within range? I'm sorry :( I'm learning.

venegal
05-06-2009, 11:57 PM
Well after doing that, $zipCodesWithinRange is an array containing all the zip codes within $range of $zipcode.

I guess what you want to do next is query your database to get all users living in those zip codes?

Your query would be something like

$query = "SELECT * FROM users WHERE zipcode IN (".implode(",", $zipCodesWithinRange).");";

doodguy
05-07-2009, 12:03 AM
ok here is code I have and I'm getting this error:

Fatal error: Cannot instantiate non-existent class: zipcode_class in /searchresults.php on line 82





//Get starting zip code and surround zip codes
$zipcode=$_GET['zipcode'];
$range=$_POST['distance'];


$zipCalculator = new zipcode_class();

$zipCodesWithinRange = $zipCalculator->get_zips_in_range($zipcode, $range, 1, true);

Echo "ZIP CODES: " . $zipCodesWithinRange . "<BR><BR><BR>";

venegal
05-07-2009, 12:32 AM
First you include the file containing your class,

Do that.

doodguy
05-07-2009, 04:12 PM
venegal: My class is in a file called zipcode.class.php do I have to copy that code over? Or should I do a readfile? I don't even know where to begin; I never use classes.

venegal
05-07-2009, 04:18 PM
Oh, that's the problem. include it.

Like so:

include("zipcode.class.php");

You should really read up on your PHP before trying to write or use complex code.

doodguy
05-07-2009, 05:24 PM
Lol ven I know that. I'm learning. I just never used classes before. I mean I overall can get by but this is some new stuff I'm dealing with.

PS: I got this working but now can't figure out why my results won't display :P Here is the code. The count works but results aren't displaying.


//Get starting zip code and surround zip codes
$zipcode=$_GET['zipcode'];
$range=$_POST['distance'];

include "zipcodeclass.php";
$zipCalculator = new zipcode_class();

$zipCodesWithinRange = $zipCalculator->get_zips_in_range($zipcode, $range, 1, true);


$results=count($zipCodesWithinRange);

Echo "TOTAL ZIP CODES: " . $results;
$i=1;
while ($i <= $results)
{
Echo "<BR>ZIP" . $i . $zipCodesWithinRange[$zipcode];
$i++;
}

doodguy
05-07-2009, 06:41 PM
ok this is result I'm getting from above code:

TOTAL ZIP CODES: 8
ZIP1
ZIP2
ZIP3
ZIP4
ZIP5
ZIP6
ZIP7
ZIP8ZIP CODES:


DISPLAY ALL RESULTS 5 miles AWAY FROM 34698

What am I doing wrong plssss? This is how I'm learning, I'm reading the php array manual to no avail :(

doodguy
05-07-2009, 07:15 PM
ok doing this bit by bit, I'm sorry, I'm learning php/mysql through google searching. Single dad, don't have money for classes, etc. so teaching myself best I can.

I used a print_r statement and got the results of the array. now that I'm getting results I'm piecing the search query together. I have this so far:


$search =mysql_query("select * from users left join info using(id_user) left join aboutme using(id_user) left join

active using(id_user) where users.login='$user' or active.new='n' AND active.active='y' AND info.gender='$sex' AND

info.bodystyle LIKE '$bodystyle' AND info.eyecolor LIKE '$eyecolor' AND info.haircolor LIKE '$haircolor' AND

info.ethnicity LIKE '$ethnicity' AND (floor(info.rating/info.ratingnumbervotes)) BETWEEN '$rating' and '10' AND

YEAR(CURRENT_DATE()) - YEAR(info.birthdate) - (RIGHT(CURRENT_DATE(),5) < RIGHT(info.birthdate,5)) BETWEEN '$agelow'

and '$agehigh' AND info.zipcode LIKE $zipcode ORDER BY info.lastlogindate DESC")or die(mysql_error());


So now I'm starting with trying to figure out how to tell it to look in zip_code database, I know I wouldn't use a left join but not sure where to put it; having issues there. Any suggestions?

doodguy
05-07-2009, 08:37 PM
Just to update whoever may read this, I have the following code, which displays no errors, but also only returns results where the zip code in a user profile is blank. Any help plssss?



//Get starting zip code and surround zip codes
$zipcode=$_GET['zipcode'];
$range=$_POST['distance'];

include "zipcodeclass.php";
$zipCalculator = new zipcode_class();

$zipCodesWithinRange = $zipCalculator->get_zips_in_range($zipcode, $range, 1, true);


$results=count($zipCodesWithinRange);

Echo "TOTAL ZIP CODES: " . $results;
$i=1;
while ($i <= $results)
{
Echo "<BR>ZIP" . $i . $zipCodesWithinRange[$i];
$i++;
}

print_r($zipCodesWithinRange);

//Echo "ZIP CODES: " . $zipCodesWithinRange[$zipcalculator] . "<BR><BR><BR>";












Echo "DISPLAY ALL RESULTS " . $range . " miles AWAY FROM " . $zipcode;

$search =mysql_query("select * from users left join info using(id_user) left join aboutme using(id_user) left join

active using(id_user) where users.login='$user' or active.new='n' AND active.active='y' AND info.gender='$sex' AND

info.bodystyle LIKE '$bodystyle' AND info.eyecolor LIKE '$eyecolor' AND info.haircolor LIKE '$haircolor' AND

info.ethnicity LIKE '$ethnicity' AND (floor(info.rating/info.ratingnumbervotes)) BETWEEN '$rating' and '10' AND

YEAR(CURRENT_DATE()) - YEAR(info.birthdate) - (RIGHT(CURRENT_DATE(),5) < RIGHT(info.birthdate,5)) BETWEEN '$agelow'

and '$agehigh' AND info.zipcode IN (".implode(",",$zipCodesWithinRange).") ORDER BY info.lastlogindate DESC")or

die(mysql_error());

venegal
05-07-2009, 09:06 PM
Well, hard to tell. What do you mean by "which displays no errors"? Does that mean that you made sure $zipCodesWithinRange contains actual zip codes? Are they the same format as the ones in your database?

If so, lose everything in your query except the zip code stuff, so it's easier to find out what the problem is.

And please put your code in PHP tags, not CODE tags. That might attract other people willing to help.

doodguy
05-07-2009, 09:50 PM
I'm sorry venegal didn't realize there was a diff. between the php tags code and the code tags.

Anyways, hope this clarifies pretty well. . .

This code:



//Get starting zip code and surround zip codes
$zipcode=$_GET['zipcode'];
$range=$_POST['distance'];

include "zipcodeclass.php";
$zipCalculator = new zipcode_class();

$zipCodesWithinRange = $zipCalculator->get_zips_in_range($zipcode, $range, 1, true);


$results=count($zipCodesWithinRange);

Echo "TOTAL ZIP CODES: " . $results;
$i=1;
while ($i <= $results)
{
Echo "<BR>ZIP" . $i . $zipCodesWithinRange[$i];
$i++;
}

print_r($zipCodesWithinRange);

//Echo "ZIP CODES: " . $zipCodesWithinRange[$zipcalculator] . "<BR><BR><BR>";




Echo "DISPLAY ALL RESULTS " . $range . " miles AWAY FROM " . $zipcode;

$search =mysql_query("select * from users left join info using(id_user) left join aboutme using(id_user) left join

active using(id_user) where users.login='$user' or active.new='n' AND active.active='y' AND info.gender='$sex' AND

info.zipcode IN (".implode(",",$zipCodesWithinRange).") ORDER BY info.lastlogindate DESC")or die(mysql_error());




Yields this:

TOTAL ZIP CODES: 18
ZIP1
ZIP2
ZIP3
ZIP4
ZIP5
ZIP6
ZIP7
ZIP8
ZIP9
ZIP10
ZIP11
ZIP12
ZIP13
ZIP14
ZIP15
ZIP16
ZIP17
ZIP18Array ( [34698] => 0 [34697] => 0.29 [33763] => 1.96 [33761] => 2.69 [33766] => 2.94 [34660] => 3.05 [33757] => 3.1 [33755] => 3.31 [33758] => 3.56 [33765] => 3.67 [34682] => 3.72 [34683] => 3.76 [33767] => 4.15 [33759] => 4.27 [33769] => 4.35 [34681] => 4.5 [34684] => 4.53 [34695] => 4.57 ) DISPLAY ALL RESULTS 5 miles AWAY FROM 34698

as well as two users both of which do NOT have a zip code listed.

venegal
05-07-2009, 10:09 PM
Alright, so what the class returns is not an array with the actual zip codes as values, but an array with the zipcodes as keys and the distances to the base zip code as values.

If you don't need those distances, you can just change your code from

$zipCodesWithinRange = $zipCalculator->get_zips_in_range($zipcode, $range, 1, true);
to

$zipCodesWithinRange = array_keys($zipCalculator->get_zips_in_range($zipcode, $range, 1, true));

doodguy
05-07-2009, 10:18 PM
ven: Ur a genious :) TY it works perfectly now :)



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum