PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/Includes/libs/zipcode.class.php

https://bitbucket.org/leow/openautoclassifieds-14-stable
PHP | 251 lines | 133 code | 45 blank | 73 comment | 28 complexity | a97a7297486d8b2db9c4ef79eeea0516 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /*******************************************************************************
  3. * ZIP Code and Distance Claculation Class
  4. *******************************************************************************
  5. * Author: Micah Carrick
  6. * Email: email@micahcarrick.com
  7. * Website: http://www.micahcarrick.com
  8. *
  9. * File: zipcode.class.php
  10. * Version: 1.2.0
  11. * Copyright: (c) 2005 - Micah Carrick
  12. * You are free to use, distribute, and modify this software
  13. * under the terms of the GNU General Public License. See the
  14. * included license.txt file.
  15. *
  16. *******************************************************************************
  17. * VERION HISTORY:
  18. * v1.2.0 [Oct 22, 2006] - Using a completely new database based on user
  19. contributions which resolves many data bugs.
  20. - Added sorting to get_zips_in_range()
  21. - Added ability to include/exclude the base zip
  22. from get_zips_in_range()
  23. * v1.1.0 [Apr 30, 2005] - Added Jeff Bearer's code to make it MUCH faster!
  24. * v1.0.1 [Apr 22, 2005] - Fixed a typo :)
  25. * v1.0.0 [Apr 12, 2005] - Initial Version
  26. *
  27. *******************************************************************************
  28. * DESCRIPTION:
  29. * A PHP Class and MySQL table to find the distance between zip codes and
  30. * find all zip codes within a given mileage or kilometer range.
  31. *
  32. *******************************************************************************
  33. */
  34. // constants for setting the $units data member
  35. define('_UNIT_MILES', 'm');
  36. define('_UNIT_KILOMETERS', 'k');
  37. // constants for passing $sort to get_zips_in_range()
  38. define('_ZIPS_SORT_BY_DISTANCE_ASC', 1);
  39. define('_ZIPS_SORT_BY_DISTANCE_DESC', 2);
  40. define('_ZIPS_SORT_BY_ZIP_ASC', 3);
  41. define('_ZIPS_SORT_BY_ZIP_DESC', 4);
  42. // constant for miles to kilometers conversion
  43. define('_M2KM_FACTOR', 1.609344);
  44. class zipcode_class {
  45. var $last_error = ""; // last error message set by this class
  46. var $last_time = 0; // last function execution time (debug info)
  47. var $units = _UNIT_MILES; // miles or kilometers
  48. var $decimals = 2; // decimal places for returned distance
  49. function get_distance($zip1, $zip2) {
  50. // returns the distance between to zip codes. If there is an error, the
  51. // function will return false and set the $last_error variable.
  52. $this->chronometer(); // start the clock
  53. if ($zip1 == $zip2) return 0; // same zip code means 0 miles between. :)
  54. // get details from database about each zip and exit if there is an error
  55. $details1 = $this->get_zip_point($zip1);
  56. $details2 = $this->get_zip_point($zip2);
  57. if ($details1 == false) {
  58. $this->last_error = "No details found for zip code: $zip1";
  59. return false;
  60. }
  61. if ($details2 == false) {
  62. $this->last_error = "No details found for zip code: $zip2";
  63. return false;
  64. }
  65. // calculate the distance between the two points based on the lattitude
  66. // and longitude pulled out of the database.
  67. $miles = $this->calculate_mileage($details1[0], $details2[0], $details1[1], $details2[1]);
  68. $this->last_time = $this->chronometer();
  69. if ($this->units == _UNIT_KILOMETERS) return round($miles * _M2KM_FACTOR, $this->decimals);
  70. else return round($miles, $this->decimals); // must be miles
  71. }
  72. function get_zip_details($zip) {
  73. // This function pulls the details from the database for a
  74. // given zip code.
  75. $sql = "SELECT lat AS lattitude, lon AS longitude, city, county, state_prefix,
  76. state_name, area_code, time_zone
  77. FROM zip_code
  78. WHERE zip_code='$zip'";
  79. $r = mysql_query($sql);
  80. if (!$r) {
  81. $this->last_error = mysql_error();
  82. return false;
  83. } else {
  84. $row = mysql_fetch_array($r, MYSQL_ASSOC);
  85. mysql_free_result($r);
  86. return $row;
  87. }
  88. }
  89. function get_zip_point($zip) {
  90. // This function pulls just the lattitude and longitude from the
  91. // database for a given zip code.
  92. $sql = "SELECT lat, lon from zip_code WHERE zip_code='$zip'";
  93. $r = mysql_query($sql);
  94. if (!$r) {
  95. $this->last_error = mysql_error();
  96. return false;
  97. } else {
  98. $row = mysql_fetch_array($r);
  99. mysql_free_result($r);
  100. return $row;
  101. }
  102. }
  103. function calculate_mileage($lat1, $lat2, $lon1, $lon2) {
  104. // used internally, this function actually performs that calculation to
  105. // determine the mileage between 2 points defined by lattitude and
  106. // longitude coordinates. This calculation is based on the code found
  107. // at http://www.cryptnet.net/fsp/zipdy/
  108. // Convert lattitude/longitude (degrees) to radians for calculations
  109. $lat1 = deg2rad($lat1);
  110. $lon1 = deg2rad($lon1);
  111. $lat2 = deg2rad($lat2);
  112. $lon2 = deg2rad($lon2);
  113. // Find the deltas
  114. $delta_lat = $lat2 - $lat1;
  115. $delta_lon = $lon2 - $lon1;
  116. // Find the Great Circle distance
  117. $temp = pow(sin($delta_lat/2.0),2) + cos($lat1) * cos($lat2) * pow(sin($delta_lon/2.0),2);
  118. $distance = 3956 * 2 * atan2(sqrt($temp),sqrt(1-$temp));
  119. return $distance;
  120. }
  121. function get_zips_in_range($zip, $range, $sort=1, $include_base) {
  122. // returns an array of the zip codes within $range of $zip. Returns
  123. // an array with keys as zip codes and values as the distance from
  124. // the zipcode defined in $zip.
  125. $this->chronometer(); // start the clock
  126. $details = $this->get_zip_point($zip); // base zip details
  127. if ($details == false) return false;
  128. // This portion of the routine calculates the minimum and maximum lat and
  129. // long within a given range. This portion of the code was written
  130. // by Jeff Bearer (http://www.jeffbearer.com). This significanly decreases
  131. // the time it takes to execute a query. My demo took 3.2 seconds in
  132. // v1.0.0 and now executes in 0.4 seconds! Greate job Jeff!
  133. // Find Max - Min Lat / Long for Radius and zero point and query
  134. // only zips in that range.
  135. $lat_range = $range/69.172;
  136. $lon_range = abs($range/(cos($details[0]) * 69.172));
  137. $min_lat = number_format($details[0] - $lat_range, "4", ".", "");
  138. $max_lat = number_format($details[0] + $lat_range, "4", ".", "");
  139. $min_lon = number_format($details[1] - $lon_range, "4", ".", "");
  140. $max_lon = number_format($details[1] + $lon_range, "4", ".", "");
  141. $return = array(); // declared here for scope
  142. $sql = "SELECT zip_code, lat, lon FROM zip_code ";
  143. if (!$include_base) $sql .= "WHERE zip_code <> '$zip' AND ";
  144. else $sql .= "WHERE ";
  145. $sql .= "lat BETWEEN '$min_lat' AND '$max_lat'
  146. AND lon BETWEEN '$min_lon' AND '$max_lon'";
  147. $r = mysql_query($sql);
  148. if (!$r) { // sql error
  149. $this->last_error = mysql_error();
  150. return false;
  151. } else {
  152. while ($row = mysql_fetch_row($r)) {
  153. // loop through all 40 some thousand zip codes and determine whether
  154. // or not it's within the specified range.
  155. $dist = $this->calculate_mileage($details[0],$row[1],$details[1],$row[2]);
  156. if ($this->units == _UNIT_KILOMETERS) $dist = $dist * _M2KM_FACTOR;
  157. if ($dist <= $range) {
  158. $return[str_pad($row[0], 5, "0", STR_PAD_LEFT)] = round($dist, $this->decimals);
  159. }
  160. }
  161. mysql_free_result($r);
  162. }
  163. // sort array
  164. switch($sort)
  165. {
  166. case _ZIPS_SORT_BY_DISTANCE_ASC:
  167. asort($return);
  168. break;
  169. case _ZIPS_SORT_BY_DISTANCE_DESC:
  170. arsort($return);
  171. break;
  172. case _ZIPS_SORT_BY_ZIP_ASC:
  173. ksort($return);
  174. break;
  175. case _ZIPS_SORT_BY_ZIP_DESC:
  176. krsort($return);
  177. break;
  178. }
  179. $this->last_time = $this->chronometer();
  180. if (empty($return)) return false;
  181. return $return;
  182. }
  183. function chronometer() {
  184. // chronometer function taken from the php manual. This is used primarily
  185. // for debugging and anlyzing the functions while developing this class.
  186. $now = microtime(TRUE); // float, in _seconds_
  187. $now = $now + time();
  188. $malt = 1;
  189. $round = 7;
  190. if ($this->last_time > 0) {
  191. /* Stop the chronometer : return the amount of time since it was started,
  192. in ms with a precision of 3 decimal places, and reset the start time.
  193. We could factor the multiplication by 1000 (which converts seconds
  194. into milliseconds) to save memory, but considering that floats can
  195. reach e+308 but only carry 14 decimals, this is certainly more precise */
  196. $retElapsed = round($now * $malt - $this->last_time * $malt, $round);
  197. $this->last_time = $now;
  198. return $retElapsed;
  199. } else {
  200. // Start the chronometer : save the starting time
  201. $this->last_time = $now;
  202. return 0;
  203. }
  204. }
  205. }