5 # Part of the ScoutLib application support library 6 # Copyright 2016 Edward Almasy and Internet Scout Research Group 7 # http://scout.wisc.edu 17 # ---- PUBLIC INTERFACE -------------------------------------------------- 25 $Trace = version_compare(PHP_VERSION,
"5.4.0",
">=")
26 ? debug_backtrace(FALSE, 2) : debug_backtrace(FALSE);
27 $Caller = basename($Trace[1][
"file"]).
":".$Trace[1][
"line"];
47 public static function CheckMyCaller($DesiredCaller, $ExceptionMsg = NULL)
49 # retrieve caller info 50 $Trace = version_compare(PHP_VERSION,
"5.4.0",
">=")
51 ? debug_backtrace(FALSE, 3) : debug_backtrace(FALSE);
52 $FullFile = $Trace[1][
"file"];
53 $File = basename($FullFile);
54 $Line = $Trace[1][
"line"];
55 $Class = isset($Trace[2][
"class"]) ? $Trace[2][
"class"] :
"";
56 $Function = isset($Trace[2][
"function"]) ? $Trace[2][
"function"] :
"";
58 # if caller does not match desired caller 59 if (($DesiredCaller != $Class)
60 && ($DesiredCaller != $Class.
"::".$Function)
61 && ($DesiredCaller != $Class.$Function)
62 && ($DesiredCaller != $File)
63 && ($DesiredCaller != $File.
":".$Line))
65 # if exception message supplied 66 if ($ExceptionMsg !== NULL)
68 # make any needed substitutions in exception message 83 $Class.
"::".$Function),
87 throw new Exception($Msg);
91 # report to our caller that their caller was not the desired one 96 # report to our caller that their caller was not the desired one 107 # return word unchanged if singular and plural are the same 108 if (in_array(strtolower($Word), self::$UncountableWords))
113 # check for irregular singular forms 114 foreach (self::$IrregularWords as $Pattern => $Result)
116 $Pattern =
'/'.$Pattern.
'$/i';
117 if (preg_match($Pattern, $Word))
119 return preg_replace($Pattern, $Result, $Word);
123 # check for matches using regular expressions 124 foreach (self::$PluralizePatterns as $Pattern => $Result)
126 if (preg_match($Pattern, $Word))
128 return preg_replace($Pattern, $Result, $Word);
132 # return word unchanged if we could not process it 143 # return word unchanged if singular and plural are the same 144 if (in_array(strtolower($Word), self::$UncountableWords))
149 # check for irregular plural forms 150 foreach (self::$IrregularWords as $Result => $Pattern)
152 $Pattern =
'/'.$Pattern.
'$/i';
153 if (preg_match($Pattern, $Word))
155 return preg_replace($Pattern, $Result, $Word);
159 # check for matches using regular expressions 160 foreach (self::$SingularizePatterns as $Pattern => $Result)
162 if (preg_match($Pattern, $Word))
164 return preg_replace($Pattern, $Result, $Word);
168 # return word unchanged if we could not process it 187 return ($A < $B) ? -1 : 1;
206 static $ZipCache = array();
208 # if we don't have a cached value for this zip, look one up 209 if (!isset($ZipCache[$Zip]))
211 # try to open our zip code database 212 $FHandle = fopen(dirname(__FILE__).
"/StdLib--ZipCodeCoords.txt",
"r");
214 # if we couldn't open the file, we can't look up a value 215 if ($FHandle === FALSE)
217 throw new Exception(
"Unable to open zip code coordinates file");
220 # iterate over our database until we find the desired zip 221 # or run out of database 222 while (($Line = fgetcsv($FHandle, 0,
"\t")) !== FALSE)
224 if ($Line[0] == $Zip)
226 $ZipCache[$Zip] = array(
227 "Lat" => $Line[1],
"Lng" => $Line[2]);
232 # if we've scanned the entire file and have no coords for 233 # this zip, cache a failure 234 if (!isset($ZipCache[$Zip]))
236 $ZipCache[$Zip] = FALSE;
240 # hand back cached value 241 return $ZipCache[$Zip];
254 $FirstPoint = self::GetLatLngForZipCode($ZipA);
255 $SecondPoint = self::GetLatLngForZipCode($ZipB);
257 # if we scanned the whole file and lack data for either of our 258 # points, return NULL 259 if ($FirstPoint === FALSE || $SecondPoint === FALSE)
264 return self::ComputeGreatCircleDistance(
265 $FirstPoint[
"Lat"], $FirstPoint[
"Lng"],
266 $SecondPoint[
"Lat"], $SecondPoint[
"Lng"]);
281 # See http://en.wikipedia.org/wiki/Great-circle_distance 283 # Convert it all to Radians 284 $Ps = deg2rad($LatSrc);
285 $Ls = deg2rad($LonSrc);
286 $Pf = deg2rad($LatDst);
287 $Lf = deg2rad($LonDst);
289 # Compute the central angle 290 return 3958.756 * atan2(
291 sqrt( pow(cos($Pf)*sin($Lf-$Ls), 2) +
292 pow(cos($Ps)*sin($Pf) -
293 sin($Ps)*cos($Pf)*cos($Lf-$Ls), 2)),
294 sin($Ps)*sin($Pf)+cos($Ps)*cos($Pf)*cos($Lf-$Ls));
310 # See http://mathforum.org/library/drmath/view/55417.html 312 # Convert angles to radians 313 $Ps = deg2rad($LatSrc);
314 $Ls = deg2rad($LonSrc);
315 $Pf = deg2rad($LatDst);
316 $Lf = deg2rad($LonDst);
318 return rad2deg(atan2(sin($Lf-$Ls)*cos($Pf),
319 cos($Ps)*sin($Pf)-sin($Ps)*cos($Pf)*cos($Lf-$Ls)));
326 # ---- PRIVATE INTERFACE ------------------------------------------------- 328 private static $PluralizePatterns = array(
329 '/(quiz)$/i' =>
"$1zes",
330 '/^(ox)$/i' =>
"$1en",
331 '/([m|l])ouse$/i' =>
"$1ice",
332 '/(matr|vert|ind)ix|ex$/i' =>
"$1ices",
333 '/(x|ch|ss|sh)$/i' =>
"$1es",
334 '/([^aeiouy]|qu)y$/i' =>
"$1ies",
335 '/(hive)$/i' =>
"$1s",
336 '/(?:([^f])fe|([lr])f)$/i' =>
"$1$2ves",
337 '/(shea|lea|loa|thie)f$/i' =>
"$1ves",
339 '/([ti])um$/i' =>
"$1a",
340 '/(tomat|potat|ech|her|vet)o$/i'=>
"$1oes",
341 '/(bu)s$/i' =>
"$1ses",
342 '/(alias)$/i' =>
"$1es",
343 '/(octop)us$/i' =>
"$1i",
344 '/(ax|test)is$/i' =>
"$1es",
345 '/(us)$/i' =>
"$1es",
349 private static $SingularizePatterns = array(
350 '/(quiz)zes$/i' =>
"$1",
351 '/(matr)ices$/i' =>
"$1ix",
352 '/(vert|ind)ices$/i' =>
"$1ex",
353 '/^(ox)en$/i' =>
"$1",
354 '/(alias)es$/i' =>
"$1",
355 '/(octop|vir)i$/i' =>
"$1us",
356 '/(cris|ax|test)es$/i' =>
"$1is",
357 '/(shoe)s$/i' =>
"$1",
359 '/(bus)es$/i' =>
"$1",
360 '/([m|l])ice$/i' =>
"$1ouse",
361 '/(x|ch|ss|sh)es$/i' =>
"$1",
362 '/(m)ovies$/i' =>
"$1ovie",
363 '/(s)eries$/i' =>
"$1eries",
364 '/([^aeiouy]|qu)ies$/i' =>
"$1y",
365 '/([lr])ves$/i' =>
"$1f",
366 '/(tive)s$/i' =>
"$1",
367 '/(hive)s$/i' =>
"$1",
368 '/(li|wi|kni)ves$/i' =>
"$1fe",
369 '/(shea|loa|lea|thie)ves$/i'=>
"$1f",
370 '/(^analy)ses$/i' =>
"$1sis",
371 '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' =>
"$1$2sis",
372 '/([ti])a$/i' =>
"$1um",
373 '/(n)ews$/i' =>
"$1ews",
374 '/(h|bl)ouses$/i' =>
"$1ouse",
375 '/(corpse)s$/i' =>
"$1",
376 '/(us)es$/i' =>
"$1",
379 private static $IrregularWords = array(
384 'child' =>
'children',
389 private static $UncountableWords = array(
static CheckMyCaller($DesiredCaller, $ExceptionMsg=NULL)
Check the caller of the current function.
static SortCompare($A, $B)
Perform compare and return value appropriate for sort function callbacks.
static ZipCodeDistance($ZipA, $ZipB)
Compute the distance between two US ZIP codes.
static GetMyCaller()
Get string with file and line number for call to current function.
static Pluralize($Word)
Pluralize an English word.
static GetLatLngForZipCode($Zip)
Look up the GPS coordinates for a US ZIP code.
Standard utility library.
static ComputeGreatCircleDistance($LatSrc, $LonSrc, $LatDst, $LonDst)
Computes the distance in kilometers between two points, assuming a spherical earth.
static ComputeBearing($LatSrc, $LonSrc, $LatDst, $LonDst)
Computes the initial angle on a course connecting two points, assuming a spherical earth...
const SQL_DATE_FORMAT
Format to feed to date() to get SQL-compatible date/time string.
static Singularize($Word)
Singularize an English word.