First, the MySQL stuff:
DELIMITER $
DROP FUNCTION IF EXISTS zip_deg2rad$
DROP FUNCTION IF EXISTS zip_gcd$
DROP FUNCTION IF EXISTS zip_distance$
DROP PROCEDURE IF EXISTS zip_radius$
CREATE FUNCTION zip_deg2rad(degrees DOUBLE) RETURNS DOUBLE
BEGIN
RETURN degrees / (180 / PI()+0.000000000000000);
END$
CREATE FUNCTION zip_gcd(type ENUM('M', 'N', 'K'), src_lat DOUBLE, src_long DOUBLE, dst_lat DOUBLE, dst_long DOUBLE) RETURNS DOUBLE
BEGIN
DECLARE temp DOUBLE;
DECLARE STATUTE_MILES DECIMAL(5,1);
DECLARE NAUTICAL_MILES DECIMAL(9,5);
DECLARE KILOMETERS DECIMAL(5,1);
SET STATUTE_MILES = 3963.0;
SET NAUTICAL_MILES = 3437.74677;
SET KILOMETERS = 6378.7;
SET src_lat = zip_deg2rad(src_lat);
SET src_long = zip_deg2rad(src_long);
SET dst_lat = zip_deg2rad(dst_lat);
SET dst_long = zip_deg2rad(dst_long);
SET temp = ACOS(SIN(src_lat) * SIN(dst_lat) + COS(src_lat) * cos(dst_lat) * cos(dst_long - src_long));
IF type = "M" THEN
SET temp = STATUTE_MILES * temp;
END IF;
IF type = "N" THEN
SET temp = NAUTICAL_MILES * temp;
END IF;
IF type = "K" THEN
SET temp = KILOMETERS * temp;
END IF;
RETURN temp;
END$
CREATE FUNCTION zip_distance(type ENUM('M', 'N', 'K'), zip_start VARCHAR(5), zip_finish VARCHAR(5)) RETURNS DOUBLE
BEGIN
DECLARE distance DOUBLE;
DECLARE start_lat DOUBLE;
DECLARE start_long DOUBLE;
DECLARE finish_lat DOUBLE;
DECLARE finish_long DOUBLE;
SELECT latitude, longitude INTO start_lat, start_long FROM zipcodes WHERE zip = zip_start;
SELECT latitude, longitude INTO finish_lat, finish_long FROM zipcodes WHERE zip = zip_finish;
SELECT zip_gcd(type, start_lat, start_long, finish_lat, finish_long) INTO distance;
RETURN distance;
END$
CREATE PROCEDURE zip_radius(IN type ENUM('M', 'N', 'K'), IN zip_start VARCHAR(5), IN radius INT, prec INT)
BEGIN
DECLARE src_lat DOUBLE;
DECLARE src_long DOUBLE;
SELECT latitude, longitude INTO src_lat, src_long FROM zipcodes WHERE zip = zip_start;
SELECT zip, city, state, county, ROUND(zip_gcd(type, src_lat, src_long, latitude, longitude), prec) AS `distance`
FROM zipcodes
WHERE zip != zip_start
AND (POW((69.1 * (longitude - src_long) * COS(src_lat / 57.3)), 2) + POW((69.1 * (latitude - src_lat)), 2)) <= (radius * radius)
ORDER BY `distance` ASC;
END$
DELIMITER ;
To use these, here are a few examples:
real_query($sql)) {
printf("Invalid query: %s\nWhole query: %s\n", $mysqli->error, $sql);
exit;
}
if ($result = $mysqli->store_result()) {
while ($record = $result->fetch_assoc()) {
$records[] = $record;
}
$result->close();
}
$mysqli->close();
print_r($records);
?>
Just change the dbuser and dbpass to their respective credentials, and run this script to see the following output:
Array
(
[0] => Array
(
[zip] => 28217
[city] => CHARLOTTE
[state] => NC
[county] => MECKLENBURG
[distance] => 2
)
[1] => Array
(
[zip] => 28134
[city] => PINEVILLE
[state] => NC
[county] => MECKLENBURG
[distance] => 4
)
[2] => Array
(
[zip] => 29716
[city] => FORT MILL
[state] => SC
[county] => YORK
[distance] => 5
)
[3] => Array
(
[zip] => 28210
[city] => CHARLOTTE
[state] => NC
[county] => MECKLENBURG
[distance] => 5
)
)
It is very important to note that every single time you use zip_radius and zip_distance to remember to ALWAYS quote the zip codes. Since some of these zip codes start with a zero, not quoting them will produce NULL results from MySQL. This is because MySQL will treat them as numbers, and as such, numbers do not start with zeros. [smile]