home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / Services / Weather / Common.php next >
Encoding:
PHP Script  |  2008-07-02  |  37.5 KB  |  1,139 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
  3.  
  4. /**
  5.  * PEAR::Services_Weather_Common
  6.  *
  7.  * PHP versions 4 and 5
  8.  *
  9.  * <LICENSE>
  10.  * Copyright (c) 2005-2007, Alexander Wirtz
  11.  * All rights reserved.
  12.  *
  13.  * Redistribution and use in source and binary forms, with or without
  14.  * modification, are permitted provided that the following conditions
  15.  * are met:
  16.  * o Redistributions of source code must retain the above copyright notice,
  17.  *   this list of conditions and the following disclaimer.
  18.  * o Redistributions in binary form must reproduce the above copyright notice,
  19.  *   this list of conditions and the following disclaimer in the documentation
  20.  *   and/or other materials provided with the distribution.
  21.  * o Neither the name of the software nor the names of its contributors
  22.  *   may be used to endorse or promote products derived from this software
  23.  *   without specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  26.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  29.  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  30.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  31.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  32.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  33.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  34.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  35.  * POSSIBILITY OF SUCH DAMAGE.
  36.  * </LICENSE>
  37.  *
  38.  * @category    Web Services
  39.  * @package     Services_Weather
  40.  * @author      Alexander Wirtz <alex@pc4p.net>
  41.  * @copyright   2005-2007 Alexander Wirtz
  42.  * @license     http://www.opensource.org/licenses/bsd-license.php  BSD License
  43.  * @version     CVS: $Id: Common.php,v 1.54 2007/04/16 10:36:05 eru Exp $
  44.  * @link        http://pear.php.net/package/Services_Weather
  45.  * @filesource
  46.  */
  47.  
  48. require_once "Services/Weather.php";
  49.  
  50. // {{{ constants
  51. // {{{ natural constants and measures
  52. define("SERVICES_WEATHER_RADIUS_EARTH", 6378.15);
  53. // }}}
  54.  
  55. // {{{ default values for the sun-functions
  56. define("SERVICES_WEATHER_SUNFUNCS_DEFAULT_LATITUDE",  31.7667);
  57. define("SERVICES_WEATHER_SUNFUNCS_DEFAULT_LONGITUDE", 35.2333);
  58. define("SERVICES_WEATHER_SUNFUNCS_SUNRISE_ZENITH",    90.83);
  59. define("SERVICES_WEATHER_SUNFUNCS_SUNSET_ZENITH",     90.83);
  60. // }}}
  61. // }}}
  62.  
  63. // {{{ class Services_Weather_Common
  64. /**
  65.  * Parent class for weather-services. Defines common functions for unit
  66.  * conversions, checks for cache enabling and does other miscellaneous
  67.  * things.
  68.  *
  69.  * @category    Web Services
  70.  * @package     Services_Weather
  71.  * @author      Alexander Wirtz <alex@pc4p.net>
  72.  * @copyright   2005-2007 Alexander Wirtz
  73.  * @license     http://www.opensource.org/licenses/bsd-license.php  BSD License
  74.  * @version     Release: 1.4.3
  75.  * @link        http://pear.php.net/package/Services_Weather
  76.  */
  77. class Services_Weather_Common {
  78.  
  79.     // {{{ properties
  80.     /**
  81.      * Format of the units provided (standard/metric/custom)
  82.      *
  83.      * @var     string                      $_unitsFormat
  84.      * @access  private
  85.      */
  86.     var $_unitsFormat = "s";
  87.  
  88.     /**
  89.      * Custom format of the units
  90.      *
  91.      * @var     array                       $_customUnitsFormat
  92.      * @access  private
  93.      */
  94.     var $_customUnitsFormat = array(
  95.         "temp"   => "f",
  96.         "vis"    => "sm",
  97.         "height" => "ft",
  98.         "wind"   => "mph",
  99.         "pres"   => "in",
  100.         "rain"   => "in"
  101.     );
  102.  
  103.     /**
  104.      * Options for HTTP requests
  105.      *
  106.      * @var     array                       $_httpOptions
  107.      * @access  private
  108.      */
  109.     var $_httpOptions = array();
  110.  
  111.     /**
  112.      * Format of the used dates
  113.      *
  114.      * @var     string                      $_dateFormat
  115.      * @access  private
  116.      */
  117.     var $_dateFormat = "m/d/y";
  118.  
  119.     /**
  120.      * Format of the used times
  121.      *
  122.      * @var     string                      $_timeFormat
  123.      * @access  private
  124.      */
  125.     var $_timeFormat = "G:i A";
  126.  
  127.     /**
  128.      * Object containing the location-data
  129.      *
  130.      * @var     object stdClass             $_location
  131.      * @access  private
  132.      */
  133.     var $_location;
  134.  
  135.     /**
  136.      * Object containing the weather-data
  137.      *
  138.      * @var     object stdClass             $_weather
  139.      * @access  private
  140.      */
  141.     var $_weather;
  142.  
  143.     /**
  144.      * Object containing the forecast-data
  145.      *
  146.      * @var     object stdClass             $_forecast
  147.      * @access  private
  148.      */
  149.     var $_forecast;
  150.  
  151.     /**
  152.      * Cache, containing the data-objects
  153.      *
  154.      * @var     object Cache                $_cache
  155.      * @access  private
  156.      */
  157.     var $_cache;
  158.  
  159.     /**
  160.      * Provides check for Cache
  161.      *
  162.      * @var     bool                        $_cacheEnabled
  163.      * @access  private
  164.      */
  165.     var $_cacheEnabled = false;
  166.     // }}}
  167.  
  168.     // {{{ constructor
  169.     /**
  170.      * Constructor
  171.      *
  172.      * @param   array                       $options
  173.      * @param   mixed                       $error
  174.      * @throws  PEAR_Error
  175.      * @access  private
  176.      */
  177.     function Services_Weather_Common($options, &$error)
  178.     {
  179.         // Set some constants for the case when PHP4 is used, as the
  180.         // date_sunset/sunrise functions are not implemented there
  181.         if (!defined("SUNFUNCS_RET_TIMESTAMP")) {
  182.             define("SUNFUNCS_RET_TIMESTAMP", 0);
  183.             define("SUNFUNCS_RET_STRING",    1);
  184.             define("SUNFUNCS_RET_DOUBLE",    2);
  185.         }
  186.  
  187.         // Set options accordingly
  188.         if (isset($options["cacheType"])) {
  189.             if (isset($options["cacheOptions"])) {
  190.                 $status = $this->setCache($options["cacheType"], $options["cacheOptions"]);
  191.             } else {
  192.                 $status = $this->setCache($options["cacheType"]);
  193.             }
  194.             if (Services_Weather::isError($status)) {
  195.                 $error = $status;
  196.                 return;
  197.             }
  198.         }
  199.  
  200.         if (isset($options["unitsFormat"])) {
  201.             if (isset($options["customUnitsFormat"])) {
  202.                 $this->setUnitsFormat($options["unitsFormat"], $options["customUnitsFormat"]);
  203.             } else {
  204.                 $this->setUnitsFormat($options["unitsFormat"]);
  205.             }
  206.         }
  207.  
  208.         if (isset($options["httpTimeout"])) {
  209.             $this->setHttpTimeout($options["httpTimeout"]);
  210.         } else {
  211.             $this->setHttpTimeout(60);
  212.         }
  213.         if (isset($options["httpProxy"])) {
  214.             $status = $this->setHttpProxy($options["httpProxy"]);
  215.             if (Services_Weather::isError($status)) {
  216.                 $error = $status;
  217.                 return;
  218.             }
  219.         }
  220.  
  221.         if (isset($options["dateFormat"])) {
  222.             $this->setDateTimeFormat($options["dateFormat"], "");
  223.         }
  224.         if (isset($options["timeFormat"])) {
  225.             $this->setDateTimeFormat("", $options["timeFormat"]);
  226.         }
  227.     }
  228.     // }}}
  229.  
  230.     // {{{ setCache()
  231.     /**
  232.      * Enables caching the data, usage strongly recommended
  233.      *
  234.      * Requires Cache to be installed
  235.      *
  236.      * @param   string                      $cacheType
  237.      * @param   array                       $cacheOptions
  238.      * @return  PEAR_Error|bool
  239.      * @throws  PEAR_Error::SERVICES_WEATHER_ERROR_CACHE_INIT_FAILED
  240.      * @access  public
  241.      */
  242.     function setCache($cacheType = "file", $cacheOptions = array())
  243.     {
  244.         // The error handling in Cache is a bit crummy (read: not existent)
  245.         // so we have to do that on our own...
  246.         @include_once "Cache.php";
  247.         @$cache = new Cache($cacheType, $cacheOptions);
  248.         if (is_object($cache) && (strtolower(get_class($cache)) == "cache" || is_subclass_of($cache, "cache"))) {
  249.             $this->_cache        = $cache;
  250.             $this->_cacheEnabled = true;
  251.         } else {
  252.             $this->_cache        = null;
  253.             $this->_cacheEnabled = false;
  254.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_CACHE_INIT_FAILED, __FILE__, __LINE__);
  255.         }
  256.  
  257.         return true;
  258.     }
  259.     // }}}
  260.  
  261.     // {{{ setUnitsFormat()
  262.     /**
  263.      * Changes the representation of the units (standard/metric)
  264.      *
  265.      * @param   string                      $unitsFormat
  266.      * @param   array                       $customUnitsFormat
  267.      * @access  public
  268.      */
  269.     function setUnitsFormat($unitsFormat, $customUnitsFormat = array())
  270.     {
  271.         static $acceptedFormats;
  272.         if (!isset($acceptedFormats)) {
  273.             $acceptedFormats = array(
  274.                 "temp"   => array("c", "f"),
  275.                 "vis"    => array("m", "km", "ft", "sm"),
  276.                 "height" => array("m", "ft"),
  277.                 "wind"   => array("mph", "kmh", "kt", "mps", "fps", "bft"),
  278.                 "pres"   => array("in", "hpa", "mb", "mm", "atm"),
  279.                 "rain"   => array("in", "mm")
  280.             );
  281.         }
  282.  
  283.         if (strlen($unitsFormat) && in_array(strtolower($unitsFormat{0}), array("c", "m", "s"))) {
  284.             $this->_unitsFormat = strtolower($unitsFormat{0});
  285.             if ($this->_unitsFormat == "c" && is_array($customUnitsFormat)) {
  286.                 foreach ($customUnitsFormat as $key => $value) {
  287.                     if (array_key_exists($key, $acceptedFormats) && in_array($value, $acceptedFormats[$key])) {
  288.                         $this->_customUnitsFormat[$key] = $value;
  289.                     }
  290.                 }
  291.             } elseif ($this->_unitsFormat == "c") {
  292.                 $this->_unitsFormat = "s";
  293.             }
  294.         }
  295.     }
  296.     // }}}
  297.  
  298.     // {{{ setHttpOption()
  299.     /**
  300.      * Sets an option for usage in HTTP_Request objects
  301.      *
  302.      * @param   string                      $varName
  303.      * @param   mixed                       $varValue
  304.      * @access  public
  305.      */
  306.     function setHttpOption($varName, $varValue)
  307.     {
  308.         if (is_string($varName) && $varName != "" && !empty($varValue)) {
  309.             $this->_httpOptions[$varName] = $varValue;
  310.         }
  311.     }
  312.     // }}}
  313.  
  314.     // {{{ setHttpTimeout()
  315.     /**
  316.      * Sets the timeout in seconds for HTTP requests
  317.      *
  318.      * @param   int                         $httpTimeout
  319.      * @access  public
  320.      */
  321.     function setHttpTimeout($httpTimeout)
  322.     {
  323.         if (is_int($httpTimeout)) {
  324.             $this->_httpOptions["timeout"] = $httpTimeout;
  325.         }
  326.     }
  327.     // }}}
  328.  
  329.     // {{{ setHttpProxy()
  330.     /**
  331.      * Sets the proxy for HTTP requests
  332.      *
  333.      * @param   string                      $httpProxy
  334.      * @access  public
  335.      */
  336.     function setHttpProxy($httpProxy)
  337.     {
  338.         if (($proxy = parse_url($httpProxy)) !== false && $proxy["scheme"] == "http") {
  339.             if (isset($proxy["user"]) && $proxy["user"] != "") {
  340.                 $this->_httpOptions["proxy_user"] = $proxy["user"];
  341.             }
  342.             if (isset($proxy["pass"]) && $proxy["pass"] != "") {
  343.                 $this->_httpOptions["proxy_pass"] = $proxy["pass"];
  344.             }
  345.             if (isset($proxy["host"]) && $proxy["host"] != "") {
  346.                 $this->_httpOptions["proxy_host"] = $proxy["host"];
  347.             }
  348.             if (isset($proxy["port"]) && $proxy["port"] != "") {
  349.                 $this->_httpOptions["proxy_port"] = $proxy["port"];
  350.             }
  351.  
  352.             return true;
  353.         } else {
  354.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_HTTP_PROXY_INVALID, __FILE__, __LINE__);
  355.         }
  356.     }
  357.     // }}}
  358.  
  359.     // {{{ getUnitsFormat()
  360.     /**
  361.      * Returns the selected units format
  362.      *
  363.      * @param   string                      $unitsFormat
  364.      * @return  array
  365.      * @access  public
  366.      */
  367.     function getUnitsFormat($unitsFormat = "")
  368.     {
  369.         // This is cheap'o stuff
  370.         if (strlen($unitsFormat) && in_array(strtolower($unitsFormat{0}), array("c", "m", "s"))) {
  371.             $unitsFormat = strtolower($unitsFormat{0});
  372.         } else {
  373.             $unitsFormat = $this->_unitsFormat;
  374.         }
  375.  
  376.         $c = $this->_customUnitsFormat;
  377.         $m = array(
  378.             "temp"   => "c",
  379.             "vis"    => "km",
  380.             "height" => "m",
  381.             "wind"   => "kmh",
  382.             "pres"   => "mb",
  383.             "rain"   => "mm"
  384.         );
  385.         $s = array(
  386.             "temp"   => "f",
  387.             "vis"    => "sm",
  388.             "height" => "ft",
  389.             "wind"   => "mph",
  390.             "pres"   => "in",
  391.             "rain"   => "in"
  392.         );
  393.  
  394.         return ${$unitsFormat};
  395.     }
  396.     // }}}
  397.  
  398.     // {{{ setDateTimeFormat()
  399.     /**
  400.      * Changes the representation of time and dates (see http://www.php.net/date)
  401.      *
  402.      * @param   string                      $dateFormat
  403.      * @param   string                      $timeFormat
  404.      * @access  public
  405.      */
  406.     function setDateTimeFormat($dateFormat = "", $timeFormat = "")
  407.     {
  408.         if (strlen($dateFormat)) {
  409.             $this->_dateFormat = $dateFormat;
  410.         }
  411.         if (strlen($timeFormat)) {
  412.             $this->_timeFormat = $timeFormat;
  413.         }
  414.     }
  415.     // }}}
  416.  
  417.     // {{{ convertTemperature()
  418.     /**
  419.      * Convert temperature between f and c
  420.      *
  421.      * @param   float                       $temperature
  422.      * @param   string                      $from
  423.      * @param   string                      $to
  424.      * @return  float
  425.      * @access  public
  426.      */
  427.     function convertTemperature($temperature, $from, $to)
  428.     {
  429.         $from = strtolower($from{0});
  430.         $to   = strtolower($to{0});
  431.  
  432.         $result = array(
  433.             "f" => array(
  434.                 "f" => $temperature,            "c" => ($temperature - 32) / 1.8
  435.             ),
  436.             "c" => array(
  437.                 "f" => 1.8 * $temperature + 32, "c" => $temperature
  438.             )
  439.         );
  440.  
  441.         return round($result[$from][$to], 2);
  442.     }
  443.     // }}}
  444.  
  445.     // {{{ convertSpeed()
  446.     /**
  447.      * Convert speed between mph, kmh, kt, mps, fps and bft
  448.      *
  449.      * Function will return "false" when trying to convert from
  450.      * Beaufort, as it is a scale and not a true measurement
  451.      *
  452.      * @param   float                       $speed
  453.      * @param   string                      $from
  454.      * @param   string                      $to
  455.      * @return  float|int|bool
  456.      * @access  public
  457.      * @link    http://www.spc.noaa.gov/faq/tornado/beaufort.html
  458.      */
  459.     function convertSpeed($speed, $from, $to)
  460.     {
  461.         $from = strtolower($from);
  462.         $to   = strtolower($to);
  463.  
  464.         static $factor;
  465.         static $beaufort;
  466.         if (!isset($factor)) {
  467.             $factor = array(
  468.                 "mph" => array(
  469.                     "mph" => 1,         "kmh" => 1.609344, "kt" => 0.8689762, "mps" => 0.44704,   "fps" => 1.4666667
  470.                 ),
  471.                 "kmh" => array(
  472.                     "mph" => 0.6213712, "kmh" => 1,        "kt" => 0.5399568, "mps" => 0.2777778, "fps" => 0.9113444
  473.                 ),
  474.                 "kt"  => array(
  475.                     "mph" => 1.1507794, "kmh" => 1.852,    "kt" => 1,         "mps" => 0.5144444, "fps" => 1.6878099
  476.                 ),
  477.                 "mps" => array(
  478.                     "mph" => 2.2369363, "kmh" => 3.6,      "kt" => 1.9438445, "mps" => 1,         "fps" => 3.2808399
  479.                 ),
  480.                 "fps" => array(
  481.                     "mph" => 0.6818182, "kmh" => 1.09728,  "kt" => 0.5924838, "mps" => 0.3048,    "fps" => 1
  482.                 )
  483.             );
  484.  
  485.             // Beaufort scale, measurements are in knots
  486.             $beaufort = array(
  487.                   1,   3,   6,  10,
  488.                  16,  21,  27,  33,
  489.                  40,  47,  55,  63
  490.             );
  491.         }
  492.  
  493.         if ($from == "bft") {
  494.             return false;
  495.         } elseif ($to == "bft") {
  496.             $speed = round($speed * $factor[$from]["kt"], 0);
  497.             for ($i = 0; $i < sizeof($beaufort); $i++) {
  498.                 if ($speed <= $beaufort[$i]) {
  499.                     return $i;
  500.                 }
  501.             }
  502.             return sizeof($beaufort);
  503.         } else {
  504.             return round($speed * $factor[$from][$to], 2);
  505.         }
  506.     }
  507.     // }}}
  508.  
  509.     // {{{ convertPressure()
  510.     /**
  511.      * Convert pressure between in, hpa, mb, mm and atm
  512.      *
  513.      * @param   float                       $pressure
  514.      * @param   string                      $from
  515.      * @param   string                      $to
  516.      * @return  float
  517.      * @access  public
  518.      */
  519.     function convertPressure($pressure, $from, $to)
  520.     {
  521.         $from = strtolower($from);
  522.         $to   = strtolower($to);
  523.  
  524.         static $factor;
  525.         if (!isset($factor)) {
  526.             $factor = array(
  527.                 "in"   => array(
  528.                     "in" => 1,         "hpa" => 33.863887, "mb" => 33.863887, "mm" => 25.4,      "atm" => 0.0334213
  529.                 ),
  530.                 "hpa"  => array(
  531.                     "in" => 0.02953,   "hpa" => 1,         "mb" => 1,         "mm" => 0.7500616, "atm" => 0.0009869
  532.                 ),
  533.                 "mb"   => array(
  534.                     "in" => 0.02953,   "hpa" => 1,         "mb" => 1,         "mm" => 0.7500616, "atm" => 0.0009869
  535.                 ),
  536.                 "mm"   => array(
  537.                     "in" => 0.0393701, "hpa" => 1.3332239, "mb" => 1.3332239, "mm" => 1,         "atm" => 0.0013158
  538.                 ),
  539.                 "atm"  => array(
  540.                     "in" => 29,921258, "hpa" => 1013.2501, "mb" => 1013.2501, "mm" => 759.999952, "atm" => 1
  541.                 )
  542.             );
  543.         }
  544.  
  545.         return round($pressure * $factor[$from][$to], 2);
  546.     }
  547.     // }}}
  548.  
  549.     // {{{ convertDistance()
  550.     /**
  551.      * Convert distance between km, ft and sm
  552.      *
  553.      * @param   float                       $distance
  554.      * @param   string                      $from
  555.      * @param   string                      $to
  556.      * @return  float
  557.      * @access  public
  558.      */
  559.     function convertDistance($distance, $from, $to)
  560.     {
  561.         $to   = strtolower($to);
  562.         $from = strtolower($from);
  563.  
  564.         static $factor;
  565.         if (!isset($factor)) {
  566.             $factor = array(
  567.                 "m" => array(
  568.                     "m" => 1,            "km" => 1000,      "ft" => 3.280839895, "sm" => 0.0006213699
  569.                 ),
  570.                 "km" => array(
  571.                     "m" => 0.001,        "km" => 1,         "ft" => 3280.839895, "sm" => 0.6213699
  572.                 ),
  573.                 "ft" => array(
  574.                     "m" => 0.3048,       "km" => 0.0003048, "ft" => 1,           "sm" => 0.0001894
  575.                 ),
  576.                 "sm" => array(
  577.                     "m" => 0.0016093472, "km" => 1.6093472, "ft" => 5280.0106,   "sm" => 1
  578.                 )
  579.             );
  580.         }
  581.  
  582.         return round($distance * $factor[$from][$to], 2);
  583.     }
  584.     // }}}
  585.  
  586.     // {{{ calculateWindChill()
  587.     /**
  588.      * Calculate windchill from temperature and windspeed (enhanced formula)
  589.      *
  590.      * Temperature has to be entered in deg F, speed in mph!
  591.      *
  592.      * @param   float                       $temperature
  593.      * @param   float                       $speed
  594.      * @return  float
  595.      * @access  public
  596.      * @link    http://www.nws.noaa.gov/om/windchill/
  597.      */
  598.     function calculateWindChill($temperature, $speed)
  599.     {
  600.         return round(35.74 + 0.6215 * $temperature - 35.75 * pow($speed, 0.16) + 0.4275 * $temperature * pow($speed, 0.16));
  601.     }
  602.     // }}}
  603.  
  604.     // {{{ calculateHumidity()
  605.     /**
  606.      * Calculate humidity from temperature and dewpoint
  607.      * This is only an approximation, there is no exact formula, this
  608.      * one here is called Magnus-Formula
  609.      *
  610.      * Temperature and dewpoint have to be entered in deg C!
  611.      *
  612.      * @param   float                       $temperature
  613.      * @param   float                       $dewPoint
  614.      * @return  float
  615.      * @access  public
  616.      * @link    http://www.faqs.org/faqs/meteorology/temp-dewpoint/
  617.      */
  618.     function calculateHumidity($temperature, $dewPoint)
  619.     {
  620.         // First calculate saturation steam pressure for both temperatures
  621.         if ($temperature >= 0) {
  622.             $a = 7.5;
  623.             $b = 237.3;
  624.         } else {
  625.             $a = 7.6;
  626.             $b = 240.7;
  627.         }
  628.         $tempSSP = 6.1078 * pow(10, ($a * $temperature) / ($b + $temperature));
  629.  
  630.         if ($dewPoint >= 0) {
  631.             $a = 7.5;
  632.             $b = 237.3;
  633.         } else {
  634.             $a = 7.6;
  635.             $b = 240.7;
  636.         }
  637.         $dewSSP  = 6.1078 * pow(10, ($a * $dewPoint) / ($b + $dewPoint));
  638.  
  639.         return round(100 * $dewSSP / $tempSSP, 1);
  640.     }
  641.     // }}}
  642.  
  643.     // {{{ calculateDewPoint()
  644.     /**
  645.      * Calculate dewpoint from temperature and humidity
  646.      * This is only an approximation, there is no exact formula, this
  647.      * one here is called Magnus-Formula
  648.      *
  649.      * Temperature has to be entered in deg C!
  650.      *
  651.      * @param   float                       $temperature
  652.      * @param   float                       $humidity
  653.      * @return  float
  654.      * @access  public
  655.      * @link    http://www.faqs.org/faqs/meteorology/temp-dewpoint/
  656.      */
  657.     function calculateDewPoint($temperature, $humidity)
  658.     {
  659.         if ($temperature >= 0) {
  660.             $a = 7.5;
  661.             $b = 237.3;
  662.         } else {
  663.             $a = 7.6;
  664.             $b = 240.7;
  665.         }
  666.  
  667.         // First calculate saturation steam pressure for temperature
  668.         $SSP = 6.1078 * pow(10, ($a * $temperature) / ($b + $temperature));
  669.  
  670.         // Steam pressure
  671.         $SP  = $humidity / 100 * $SSP;
  672.  
  673.         $v   = log($SP / 6.1078, 10);
  674.  
  675.         return round($b * $v / ($a - $v), 1);
  676.     }
  677.     // }}}
  678.  
  679.     // {{{ polar2cartesian()
  680.     /**
  681.      * Convert polar coordinates to cartesian coordinates
  682.      *
  683.      * @param   float                       $latitude
  684.      * @param   float                       $longitude
  685.      * @return  array
  686.      * @access  public
  687.      */
  688.     function polar2cartesian($latitude, $longitude)
  689.     {
  690.         $theta = deg2rad($latitude);
  691.         $phi   = deg2rad($longitude);
  692.  
  693.         $x = SERVICES_WEATHER_RADIUS_EARTH * cos($phi) * cos($theta);
  694.         $y = SERVICES_WEATHER_RADIUS_EARTH * sin($phi) * cos($theta);
  695.         $z = SERVICES_WEATHER_RADIUS_EARTH             * sin($theta);
  696.  
  697.         return array($x, $y, $z);
  698.     }
  699.     // }}}
  700.  
  701.  
  702.     // {{{ calculateSunRiseSet()
  703.     /**
  704.      * Calculates sunrise and sunset for a location
  705.      *
  706.      * The sun position algorithm taken from the 'US Naval Observatory's
  707.      * Almanac for Computers', implemented by Ken Bloom <kekabloom[at]ucdavis[dot]edu>
  708.      * for the zmanim project, converted to C by Moshe Doron <mosdoron[at]netvision[dot]net[dot]il>
  709.      * and finally taken from the PHP5 sources and converted to native PHP as a wrapper.
  710.      *
  711.      * The date has to be entered as a timestamp!
  712.      *
  713.      * @param   int                         $date
  714.      * @param   int                         $retformat
  715.      * @param   float                       $latitude
  716.      * @param   float                       $longitude
  717.      * @param   float                       $zenith
  718.      * @param   float                       $gmt_offset
  719.      * @param   bool                        $sunrise
  720.      * @return  PEAR_Error|mixed
  721.      * @throws  PEAR_Error::SERVICES_WEATHER_ERROR_SUNFUNCS_DATE_INVALID
  722.      * @throws  PEAR_Error::SERVICES_WEATHER_ERROR_SUNFUNCS_RETFORM_INVALID
  723.      * @throws  PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_ERROR
  724.      * @access  public
  725.      */
  726.     function calculateSunRiseSet($date, $retformat = null, $latitude = null, $longitude = null, $zenith = null, $gmt_offset = null, $sunrise = true)
  727.     {
  728.         // Date must be timestamp for now
  729.         if (!is_int($date)) {
  730.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_SUNFUNCS_DATE_INVALID, __FILE__, __LINE__);
  731.         }
  732.  
  733.         // Check for proper return format
  734.         if ($retformat === null) {
  735.             $retformat  = SUNFUNCS_RET_STRING;
  736.         } elseif (!in_array($retformat, array(SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING, SUNFUNCS_RET_DOUBLE)) ) {
  737.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_SUNFUNCS_RETFORM_INVALID, __FILE__, __LINE__);
  738.         }
  739.  
  740.         // Set default values for coordinates
  741.         if ($latitude === null) {
  742.             $latitude   = SUNFUNCS_DEFAULT_LATITUDE;
  743.         } else {
  744.             $latitude   = (float) $latitude;
  745.         }
  746.         if ($longitude === null) {
  747.             $longitude  = SUNFUNCS_DEFAULT_LONGITUDE;
  748.         } else {
  749.             $longitude  = (float) $longitude;
  750.         }
  751.         if ($zenith === null) {
  752.             if($sunrise) {
  753.                 $zenith = SUNFUNCS_SUNRISE_ZENITH;
  754.             } else {
  755.                 $zenith = SUNFUNCS_SUNSET_ZENITH;
  756.             }
  757.         } else {
  758.             $zenith     = (float) $zenith;
  759.         }
  760.  
  761.         // Default value for GMT offset
  762.         if ($gmt_offset === null) {
  763.             $gmt_offset = date("Z", $date) / 3600;
  764.         } else {
  765.             $gmt_offset = (float) $gmt_offset;
  766.         }
  767.  
  768.         // If we have PHP5, then act as wrapper for the appropriate functions
  769.         if ($sunrise && function_exists("date_sunrise")) {
  770.             return date_sunrise($date, $retformat, $latitude, $longitude, $zenith, $gmt_offset);
  771.         }
  772.         if (!$sunrise && function_exists("date_sunset")) {
  773.             return date_sunset($date, $retformat, $latitude, $longitude, $zenith, $gmt_offset);
  774.         }
  775.  
  776.         // Apparently we have PHP4, so calculate the neccessary steps in native PHP
  777.         // Step 1: First calculate the day of the year
  778.         $N = date("z", $date) + 1;
  779.  
  780.         // Step 2: Convert the longitude to hour value and calculate an approximate time
  781.         $lngHour = $longitude / 15;
  782.  
  783.         // Use 18 for sunset instead of 6
  784.         if ($sunrise) {
  785.             // Sunrise
  786.             $t = $N + ((6 - $lngHour) / 24);
  787.         } else {
  788.             // Sunset
  789.             $t = $N + ((18 - $lngHour) / 24);
  790.         }
  791.  
  792.         // Step 3: Calculate the sun's mean anomaly
  793.         $M = (0.9856 * $t) - 3.289;
  794.  
  795.         // Step 4: Calculate the sun's true longitude
  796.         $L = $M + (1.916 * sin(deg2rad($M))) + (0.020 * sin(deg2rad(2 * $M))) + 282.634;
  797.  
  798.         while ($L < 0) {
  799.             $Lx = $L + 360;
  800.             assert($Lx != $L); // askingtheguru: really needed?
  801.             $L = $Lx;
  802.         }
  803.  
  804.         while ($L >= 360) {
  805.             $Lx = $L - 360;
  806.             assert($Lx != $L); // askingtheguru: really needed?
  807.             $L = $Lx;
  808.         }
  809.  
  810.         // Step 5a: Calculate the sun's right ascension
  811.         $RA = rad2deg(atan(0.91764 * tan(deg2rad($L))));
  812.  
  813.         while ($RA < 0) {
  814.             $RAx = $RA + 360;
  815.             assert($RAx != $RA); // askingtheguru: really needed?
  816.             $RA = $RAx;
  817.         }
  818.  
  819.         while ($RA >= 360) {
  820.             $RAx = $RA - 360;
  821.             assert($RAx != $RA); // askingtheguru: really needed?
  822.             $RA = $RAx;
  823.         }
  824.  
  825.         // Step 5b: Right ascension value needs to be in the same quadrant as L
  826.         $Lquadrant  = floor($L / 90) * 90;
  827.         $RAquadrant = floor($RA / 90) * 90;
  828.  
  829.         $RA = $RA + ($Lquadrant - $RAquadrant);
  830.  
  831.         // Step 5c: Right ascension value needs to be converted into hours
  832.         $RA /= 15;
  833.  
  834.         // Step 6: Calculate the sun's declination
  835.         $sinDec = 0.39782 * sin(deg2rad($L));
  836.         $cosDec = cos(asin($sinDec));
  837.  
  838.         // Step 7a: Calculate the sun's local hour angle
  839.         $cosH = (cos(deg2rad($zenith)) - ($sinDec * sin(deg2rad($latitude)))) / ($cosDec * cos(deg2rad($latitude)));
  840.  
  841.         // XXX: What's the use of this block.. ?
  842.         // if (sunrise && cosH > 1 || !sunrise && cosH < -1) {
  843.         //     throw doesnthappen();
  844.         // }
  845.  
  846.         // Step 7b: Finish calculating H and convert into hours
  847.         if ($sunrise) {
  848.             // Sunrise
  849.             $H = 360 - rad2deg(acos($cosH));
  850.         } else {
  851.             // Sunset
  852.             $H = rad2deg(acos($cosH));
  853.         }
  854.         $H = $H / 15;
  855.  
  856.         // Step 8: Calculate local mean time
  857.         $T = $H + $RA - (0.06571 * $t) - 6.622;
  858.  
  859.         // Step 9: Convert to UTC
  860.         $UT = $T - $lngHour;
  861.  
  862.         while ($UT < 0) {
  863.             $UTx = $UT + 24;
  864.             assert($UTx != $UT); // askingtheguru: really needed?
  865.             $UT = $UTx;
  866.         }
  867.  
  868.         while ($UT >= 24) {
  869.             $UTx = $UT - 24;
  870.             assert($UTx != $UT); // askingtheguru: really needed?
  871.             $UT = $UTx;
  872.         }
  873.  
  874.         $UT = $UT + $gmt_offset;
  875.  
  876.         // Now bring the result into the chosen format and return
  877.         switch ($retformat) {
  878.             case SUNFUNCS_RET_TIMESTAMP:
  879.                 return intval($date - $date % (24 * 3600) + 3600 * $UT);
  880.             case SUNFUNCS_RET_STRING:
  881.                 $N = floor($UT);
  882.                 return sprintf("%02d:%02d", $N, floor(60 * ($UT - $N)));
  883.             case SUNFUNCS_RET_DOUBLE:
  884.                 return $UT;
  885.             default:
  886.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_ERROR, __FILE__, __LINE__);
  887.         }
  888.     }
  889.     // }}}
  890.  
  891.     // {{{ getWeatherIcon()
  892.     /**
  893.      * Gets a number corresponding to a weather icon.
  894.      *
  895.      * These numbers just happen to correspond with the icons that you get with
  896.      * the weather.com SDK, but open versions of them have been created. Input
  897.      * must be in standard units. For the icons that include day/night, we use
  898.      * the present time and the provided lat/lon to determine if the sun is up.
  899.      * A complete set of icon descriptions can be found here:
  900.      * http://sranshaft.wincustomize.com/Articles.aspx?AID=60165&u=0
  901.      *
  902.      * There are a number of icon sets here:
  903.      * http://www.desktopsidebar.com/forums/index.php?showtopic=2441&st=0
  904.      * http://www.desktopsidebar.com/forums/index.php?showtopic=819
  905.      *
  906.      * @param   string                      $condition      The condition.
  907.      * @param   array                       $clouds         The clouds at various levels.
  908.      * @param   float                       $wind           Wind speed in mph.
  909.      * @param   float                       $temperature    Temperature in deg F.
  910.      * @param   float                       $latitude       Point latitude.
  911.      * @param   float                       $longitude      Point longitude.
  912.      * @author  Seth Price  <seth@pricepages.org>
  913.      * @access  public
  914.      */
  915.     function getWeatherIcon($condition, $clouds = array(), $wind = 5, $temperature = 70, $latitude = -360, $longitude = -360)
  916.     {
  917.         // Search for matches that don't use the time of day
  918.         $hail     = (bool) stristr($condition, "hail");
  919.         $dust     = (bool) stristr($condition, "dust")     || (bool) stristr($condition, "sand");
  920.         $smoke    = (bool) stristr($condition, "smoke")    || (bool) stristr($condition, "volcanic ash");
  921.  
  922.         // Slightly more complex matches that might or might not use the time of day
  923.         $near     = (bool) stristr($condition, "vicinity") || (bool) stristr($condition, "recent");
  924.         $light    = (bool) stristr($condition, "light");
  925.         $heavy    = (bool) stristr($condition, "heavy");
  926.         $ice      = (bool) stristr($condition, "ice")      || (bool) stristr($condition, "pellets");
  927.         $rain     = (bool) stristr($condition, "rain");
  928.         $snow     = (bool) stristr($condition, "snow");
  929.         $fog      = (bool) stristr($condition, "fog")      || (bool) stristr($condition, "spray")        || (bool) stristr($condition, "mist");
  930.         $haze     = (bool) stristr($condition, "haze");
  931.         $ts       = (bool) stristr($condition, "thunderstorm");
  932.         $freezing = (bool) stristr($condition, "freezing");
  933.         $wind     = (bool) stristr($condition, "squall")   || $wind > 25;
  934.         $nsw      = (bool) stristr($condition, "no significant weather");
  935.         $hot      = $temperature > 95;
  936.         $frigid   = $temperature < 5;
  937.  
  938.  
  939.         if ($hail) {
  940.             return 6;  // Hail
  941.         }
  942.         if ($dust) {
  943.             return 19; // Dust
  944.         }
  945.         if ($smoke) {
  946.             return 22; // Smoke
  947.         }
  948.  
  949.         // Get some of the dangerous conditions fist
  950.         if ($rain && $snow && ($ice || $freezing)) {
  951.             return 7;  // Icy/Clouds Rain-Snow
  952.         }
  953.         if (($ts || $rain) && ($ice || $freezing)) {
  954.             return 10; // Icy/Rain
  955.         }
  956.         if (($fog || $haze) && ($ice || $freezing)) {
  957.             return 8;  // Icy/Haze Rain
  958.         }
  959.         if ($rain && $snow) {
  960.             return 5;  // Cloudy/Snow-Rain Mix
  961.         }
  962.         if ($fog && $rain) {
  963.             return 9;  // Haze/Rain
  964.         }
  965.         if ($wind && $rain) {
  966.             return 1;  // Wind/Rain
  967.         }
  968.         if ($wind && $snow) {
  969.             return 43; // Windy/Snow
  970.         }
  971.         if ($snow && $light) {
  972.             return 13; // Flurries
  973.         }
  974.         if ($light && $rain) {
  975.             return 11; // Light Rain
  976.         }
  977.  
  978.         // Get the maximum coverage of the clouds at any height. For most
  979.         // people, overcast at 1000ft is the same as overcast at 10000ft.
  980.         //
  981.         // 0 == clear, 1 == hazey, 2 == partly cloudy, 3 == mostly cloudy, 4 == overcast
  982.         $coverage = 0;
  983.         foreach ($clouds as $layer) {
  984.             if ($coverage < 1 && stristr($layer["amount"], "few")) {
  985.                 $coverage = 1;
  986.             } elseif ($coverage < 2 && stristr($layer["amount"], "scattered")) {
  987.                 $coverage = 2;
  988.             } elseif ($coverage < 3 && (stristr($layer["amount"], "broken") || stristr($layer["amount"], "cumulus"))) {
  989.                 $coverage = 3;
  990.             } elseif ($coverage < 4 && stristr($layer["amount"], "overcast")) {
  991.                 $coverage = 4;
  992.             }
  993.         }
  994.  
  995.         // Check if it is day or not. 0 is night, 2 is day, and 1 is unknown
  996.         // or twilight (~(+|-)1 hour of sunrise/sunset). Note that twilight isn't
  997.         // always accurate because of issues wrapping around the 24hr clock. Oh well...
  998.         if ($latitude < 90 && $latitude > -90 && $longitude < 180 && $longitude > -180) {
  999.             // Calculate sunrise/sunset and current time in GMT
  1000.             $sunrise   = $this->calculateSunRiseSet(gmmktime(), SUNFUNCS_RET_TIMESTAMP, $latitude, $longitude, SERVICES_WEATHER_SUNFUNCS_SUNRISE_ZENITH, 0, true);
  1001.             $sunset    = $this->calculateSunRiseSet(gmmktime(), SUNFUNCS_RET_TIMESTAMP, $latitude, $longitude, SERVICES_WEATHER_SUNFUNCS_SUNRISE_ZENITH, 0, false);
  1002.             $timeOfDay = gmmktime();
  1003.  
  1004.             // Now that we have the sunrise/sunset times and the current time,
  1005.             // we need to figure out if it is day, night, or twilight. Wrapping
  1006.             // these times around the 24hr clock is a pain.
  1007.             if ($sunrise < $sunset) {
  1008.                 if ($timeOfDay > ($sunrise + 3600) && $timeOfDay < ($sunset - 3600)) {
  1009.                     $isDay = 2;
  1010.                 } elseif ($timeOfDay > ($sunrise - 3600) && $timeOfDay < ($sunset + 3600)) {
  1011.                     $isDay = 1;
  1012.                 } else {
  1013.                     $isDay = 0;
  1014.                 }
  1015.             } else {
  1016.                 if ($timeOfDay < ($sunrise - 3600) && $timeOfDay > ($sunset + 3600)) {
  1017.                     $isDay = 0;
  1018.                 } elseif ($timeOfDay < ($sunrise + 3600) && $timeOfDay > ($sunset - 3600)) {
  1019.                     $isDay = 1;
  1020.                 } else {
  1021.                     $isDay = 2;
  1022.                 }
  1023.             }
  1024.         } else {
  1025.             // Default to twilight because it tends to have neutral icons.
  1026.             $isDay = 1;
  1027.         }
  1028.  
  1029.         // General precipitation
  1030.         if ($ts && $near) {
  1031.             switch ($isDay) {
  1032.                 case 0:
  1033.                 case 1:
  1034.                     return 38; // Lightning
  1035.                 case 2:
  1036.                     return 37; // Lightning/Day
  1037.             }
  1038.         }
  1039.         if ($ts) {
  1040.             switch ($isDay) {
  1041.                 case 0:
  1042.                     return 47; // Thunderstorm/Night
  1043.                 case 1:
  1044.                 case 2:
  1045.                     return 0;  // Rain/Lightning
  1046.             }
  1047.         }
  1048.         if ($snow) {
  1049.             switch ($isDay) {
  1050.                 case 0:
  1051.                     return 46; // Snow/Night
  1052.                 case 1:
  1053.                 case 2:
  1054.                     return 41; // Snow
  1055.             }
  1056.         }
  1057.         if ($rain) {
  1058.             switch ($isDay) {
  1059.                 case 0:
  1060.                     return 45; // Rain/Night
  1061.                 case 1:
  1062.                     return 40; // Rain
  1063.                 case 2:
  1064.                     return 39; // Rain/Day
  1065.             }
  1066.         }
  1067.  
  1068.         // Cloud conditions near the ground
  1069.         if ($fog) {
  1070.             return 20; // Fog
  1071.         }
  1072.         if ($haze) {
  1073.             return 21; // Haze
  1074.         }
  1075.  
  1076.         // Cloud conditions
  1077.         if ($coverage == 4) {
  1078.             return 26; // Mostly Cloudy
  1079.         }
  1080.         if ($coverage == 3) {
  1081.             switch ($isDay) {
  1082.                 case 0:
  1083.                     return 27; // Mostly Cloudy/Night
  1084.                 case 1:
  1085.                     return 26; // Mostly Cloudy
  1086.                 case 2:
  1087.                     return 28; // Mostly Cloudy/Day
  1088.             }
  1089.         }
  1090.         if ($coverage == 2) {
  1091.             switch ($isDay) {
  1092.                 case 0:
  1093.                     return 29; // Partly Cloudy/Night
  1094.                 case 1:
  1095.                     return 26; // Mostly Cloudy
  1096.                 case 2:
  1097.                     return 30; // Partly Cloudy/Day
  1098.             }
  1099.         }
  1100.         if ($coverage == 1) {
  1101.             switch ($isDay) {
  1102.                 case 0:
  1103.                 case 1:
  1104.                     return 33; // Hazy/Night
  1105.                 case 2:
  1106.                     return 34; // Hazy/Day
  1107.             }
  1108.         }
  1109.  
  1110.         // Catch-alls
  1111.         if ($wind) {
  1112.             return 23; // Wind
  1113.         }
  1114.         if ($hot) {
  1115.             return 36; // Hot!
  1116.         }
  1117.         if ($frigid) {
  1118.             return 25; // Frigid
  1119.         }
  1120.  
  1121.         if ($nsw) {
  1122.             switch ($isDay) {
  1123.                 case 0:
  1124.                 case 1:
  1125.                     // Use night for twilight because the moon is generally
  1126.                     // out then, so it will match with most icon sets.
  1127.                     return 31; // Clear Night
  1128.                 case 2:
  1129.                     return 32; // Clear Day
  1130.             }
  1131.         }
  1132.  
  1133.         return "na";
  1134.     }
  1135.     // }}}
  1136. }
  1137. // }}}
  1138. ?>
  1139.