Services_Weather
[ class tree: Services_Weather ] [ index: Services_Weather ] [ all elements ]

Source for file Weatherdotcom.php

Documentation is available at Weatherdotcom.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available through the world-wide-web at                              |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Alexander Wirtz <alex@pc4p.net>                             |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Weatherdotcom.php,v 1.43 2004/07/10 11:12:44 eru Exp $
  20.  
  21. /**
  22. @package      Services_Weather
  23. @filesource
  24. */
  25.  
  26. /**
  27. */
  28. require_once "Services/Weather/Common.php";
  29.  
  30. // {{{ class Services_Weather_Weatherdotcom
  31. /**
  32. * PEAR::Services_Weather_Weatherdotcom
  33. *
  34. * This class acts as an interface to the xml service of weather.com. It
  35. * searches for given locations and retrieves current weather data as well
  36. * as forecast for up to 10 days.
  37. *
  38. * For using the weather.com xml-service please visit
  39. *     http://www.weather.com/services/xmloap.html
  40. * and follow the link to sign up, it's free! You will receive an email
  41. * where to download the SDK with the needed images and guidelines how to
  42. * publish live data from weather.com. Unfortunately the guidelines are a
  43. * bit harsh, that's why there's no actual data-representation in this
  44. * class, just the raw data. Also weather.com demands active caching, so I'd
  45. * strongly recommend enabling the caching implemented in this class. It
  46. * obeys to the times as written down in the guidelines.
  47. *
  48. * For working examples, please take a look at
  49. *     docs/Services_Weather/examples/weather.com-basic.php
  50. *     docs/Services_Weather/examples/weather.com-extensive.php
  51. *
  52. @author       Alexander Wirtz <alex@pc4p.net>
  53. @link         http://www.weather.com/services/xmloap.html
  54. @example      examples/weather.com-basic.php      weather.com-basic.php
  55. @example      examples/weather.com-extensive.php  weather.com-extensive.php
  56. @package      Services_Weather
  57. @license      http://www.php.net/license/2_02.txt
  58. @version      1.3
  59. */
  60.  
  61.     // {{{ properties
  62.     /**
  63.     * Partner-ID at weather.com
  64.     *
  65.     * @var      string                      $_partnerID 
  66.     * @access   private
  67.     */
  68.     var $_partnerID "";
  69.  
  70.     /**
  71.     * License key at weather.com
  72.     *
  73.     * @var      string                      $_licenseKey 
  74.     * @access   private
  75.     */
  76.     var $_licenseKey "";
  77.  
  78.     /**
  79.     * Object containing the promotional links-data
  80.     *
  81.     * @var      object stdClass             $_links 
  82.     * @access   private
  83.     */
  84.     var $_links;
  85.  
  86.     /**
  87.     * XML_Unserializer, used for processing the xml
  88.     *
  89.     * @var      object XML_Unserializer     $_unserializer 
  90.     * @access   private
  91.     */
  92.     var $_unserializer;
  93.     // }}}
  94.  
  95.     // {{{ constructor
  96.     /**
  97.     * Constructor
  98.     *
  99.     * Requires XML_Serializer to be installed
  100.     *
  101.     * @param    array                       $options 
  102.     * @param    mixed                       $error 
  103.     * @throws   PEAR_Error
  104.     * @see      Science_Weather::Science_Weather
  105.     * @access   private
  106.     */
  107.     function Services_Weather_Weatherdotcom($options&$error)
  108.     {
  109.         $perror = null;
  110.         $this->Services_Weather_Common($options$perror);
  111.         if (Services_Weather::isError($perror)) {
  112.             $error $perror;
  113.             return;
  114.         }
  115.  
  116.         // Set options accordingly
  117.         if (isset($options["partnerID"])) {
  118.             $this->setAccountData($options["partnerID"]);
  119.         }
  120.         if (isset($options["licenseKey"])) {
  121.             $this->setAccountData(""$options["licenseKey"]);
  122.         }
  123.         
  124.         include_once "XML/Unserializer.php";
  125.         $unserializer &new XML_Unserializer(array("complexType" => "object""keyAttribute" => "type"));
  126.         if (Services_Weather::isError($unserializer)) {
  127.             $error $unserializer;
  128.             return;
  129.         else {
  130.             $this->_unserializer $unserializer;
  131.         }
  132.         
  133.         // Can't acquire an object here, has to be clean on every request
  134.         include_once "HTTP/Request.php";
  135.     }
  136.     // }}}
  137.  
  138.     // {{{ setAccountData()
  139.     /**
  140.     * Sets the neccessary account-information for weather.com, you'll
  141.     * receive them after registering for the XML-stream
  142.     *
  143.     * @param    string                      $partnerID 
  144.     * @param    string                      $licenseKey 
  145.     * @access   public
  146.     */
  147.     function setAccountData($partnerID$licenseKey)
  148.     {
  149.         if (strlen($partnerID&& ctype_digit($partnerID)) {
  150.             $this->_partnerID  $partnerID;
  151.         }
  152.         if (strlen($licenseKey&& ctype_alnum($licenseKey)) {
  153.             $this->_licenseKey $licenseKey;
  154.         }
  155.     }
  156.     // }}}
  157.  
  158.     // {{{ _checkLocationID()
  159.     /**
  160.     * Checks the id for valid values and thus prevents silly requests to
  161.     * weather.com server
  162.     *
  163.     * @param    string                      $id 
  164.     * @return   PEAR_Error|bool
  165.     * @throws   PEAR_Error::SERVICES_WEATHER_ERROR_NO_LOCATION
  166.     * @throws   PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION
  167.     * @access   private
  168.     */
  169.     function _checkLocationID($id)
  170.     {
  171.         if (is_array($id|| is_object($id|| !strlen($id)) {
  172.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_NO_LOCATION__FILE____LINE__);
  173.         elseif (!ctype_alnum($id|| (strlen($id> 8)) {
  174.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_INVALID_LOCATION__FILE____LINE__);
  175.         }
  176.  
  177.         return true;
  178.     }
  179.     // }}}
  180.  
  181.     // {{{ _parseWeatherData()
  182.     /**
  183.     * Parses the data returned by the provided URL and caches it
  184.     *
  185.     * @param    string                      $id 
  186.     * @param    string                      $url 
  187.     * @return   PEAR_Error|bool
  188.     * @throws   PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA
  189.     * @throws   PEAR_Error
  190.     * @access   private
  191.     */
  192.     function _parseWeatherData($id$url)
  193.     {
  194.         // Get data from URL...
  195.         $request &new HTTP_Request($urlarray("timeout" => $this->_httpTimeout));
  196.         $status $request->sendRequest();
  197.         if (Services_Weather::isError($status)) {
  198.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA__FILE____LINE__);
  199.         }
  200.         $data $request->getResponseBody();
  201.         
  202.         // ...and unserialize
  203.         $status $this->_unserializer->unserialize($data);
  204.  
  205.         if (Services_Weather::isError($status)) {
  206.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA__FILE____LINE__);
  207.         else {
  208.             $root $this->_unserializer->getRootName();
  209.             $data $this->_unserializer->getUnserializedData();
  210.  
  211.             if (Services_Weather::isError($root)) {
  212.                 // Something wrong here...
  213.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA__FILE____LINE__);
  214.             elseif ($root == "error"{
  215.                 // We got an error back from weather.com
  216.                 $errno  key(get_object_vars($data));
  217.                 return Services_Weather::raiseError($errno__FILE____LINE__);
  218.             else {
  219.                 // Valid data, lets get started
  220.                 // Loop through the different sub-parts of the data fro processing
  221.                 foreach (get_object_vars($dataas $key => $val{
  222.                     switch ($key{
  223.                         case "head":
  224.                             continue 2;
  225.                             break;
  226.                         case "prmo":
  227.                             $varname  "links";
  228.                             break;
  229.                         case "loc":
  230.                             $varname  "location";
  231.                             break;
  232.                         case "cc":
  233.                             $varname  "weather";
  234.                             break;
  235.                         case "dayf":
  236.                             $varname  "forecast";
  237.                             break;
  238.                     }
  239.                     // Save data in object
  240.                     $this->{"_".$varname$val;
  241.                     if ($this->_cacheEnabled{
  242.                         // ...and cache if possible
  243.                         $expire constant("SERVICES_WEATHER_EXPIRES_".strtoupper($varname));
  244.                         $this->_cache->extSave($id$val""$expire$varname);
  245.                     }
  246.                 }
  247.             }
  248.         }
  249.  
  250.         return true;
  251.     }
  252.     // }}}
  253.  
  254.     // {{{ searchLocation()
  255.     /**
  256.     * Searches IDs for given location, returns array of possible locations
  257.     * or single ID
  258.     *
  259.     * @param    string                      $location 
  260.     * @param    bool                        $useFirst       If set, first ID of result-array is returned
  261.     * @return   PEAR_Error|array|string
  262.     * @throws   PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA
  263.     * @throws   PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION
  264.     * @access   public
  265.     */
  266.     function searchLocation($location$useFirst = false)
  267.     {
  268.         // Get search data from server and unserialize
  269.         $searchURL "http://xoap.weather.com/search/search?where=".urlencode(trim($location));
  270.         $status $this->_unserializer->unserialize($searchURLtruearray("overrideOptions" => true"complexType" => "array""keyAttribute" => "id"));
  271.  
  272.         if (Services_Weather::isError($status)) {
  273.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA__FILE____LINE__);
  274.         else {
  275.             $search $this->_unserializer->getUnserializedData();
  276.  
  277.             if (Services_Weather::isError($search)) {
  278.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA__FILE____LINE__);
  279.             elseif (!is_array($search|| !sizeof($search)) {
  280.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION__FILE____LINE__);
  281.             else {
  282.                 if (!$useFirst && (sizeof($search> 1)) {
  283.                     $searchReturn $search;
  284.                 elseif ($useFirst || (sizeof($search== 1)) {
  285.                     $searchReturn key($search);
  286.                 }
  287.             }
  288.         }
  289.  
  290.         return $searchReturn;
  291.     }
  292.     // }}}
  293.  
  294.     // {{{ searchLocationByCountry()
  295.     /**
  296.     * Returns only false, as weather.com offers no country listing via
  297.     * its XML services
  298.     *
  299.     * @param    string                      $country 
  300.     * @return   bool 
  301.     * @access   public
  302.     * @deprecated
  303.     */
  304.     function searchLocationByCountry($country "")
  305.     {
  306.         return false;
  307.     }
  308.     // }}}
  309.  
  310.     // {{{ getLinks()
  311.     /**
  312.     * Returns the data for the promotional links belonging to the ID
  313.     *
  314.     * @param    string                      $id 
  315.     * @return   PEAR_Error|array
  316.     * @throws   PEAR_Error
  317.     * @access   public
  318.     */
  319.     function getLinks($id "")
  320.     {
  321.         $status $this->_checkLocationID($id);
  322.  
  323.         if (Services_Weather::isError($status)) {
  324.             return $status;
  325.         }
  326.  
  327.         $linksReturn = array();
  328.         $linksURL    "http://xoap.weather.com/weather/local/".$id."?prod=xoap&par=".$this->_partnerID."&key=".$this->_licenseKey."&link=xoap";
  329.  
  330.         if ($this->_cacheEnabled && ($links $this->_cache->get($id"links"))) {
  331.             // Get data from cache
  332.             $this->_links = $links;
  333.             $linksReturn["cache""HIT";
  334.         else {
  335.             // Same as in the function above...
  336.             $status $this->_parseWeatherData($id$linksURL);
  337.  
  338.             if (Services_Weather::isError($status)) {
  339.                 return $status;
  340.             }
  341.             $linksReturn["cache""MISS";
  342.         }
  343.  
  344.         $linksReturn["promo"= array();
  345.         for ($i = 0; $i sizeof($this->_links->link)$i++{
  346.             $linksReturn["promo"][$i= array();
  347.             $linksReturn["promo"][$i]["title"$this->_links->link[$i]->t;
  348.             // B0rked response (returned is &par=xoap, should be &prod=xoap), fix it
  349.             $linksReturn["promo"][$i]["link"]  str_replace("par=""prod="$this->_links->link[$i]->l);
  350.             $linksReturn["promo"][$i]["link".= "&par=".$this->_partnerID;
  351.         }   
  352.  
  353.         return $linksReturn;
  354.     }
  355.     // }}}
  356.  
  357.     // {{{ getLocation()
  358.     /**
  359.     * Returns the data for the location belonging to the ID
  360.     *
  361.     * @param    string                      $id 
  362.     * @return   PEAR_Error|array
  363.     * @throws   PEAR_Error
  364.     * @access   public
  365.     */
  366.     function getLocation($id "")
  367.     {
  368.         $status $this->_checkLocationID($id);
  369.  
  370.         if (Services_Weather::isError($status)) {
  371.             return $status;
  372.         }
  373.  
  374.         $locationReturn = array();
  375.         $locationURL    "http://xoap.weather.com/weather/local/".$id."?prod=xoap&par=".$this->_partnerID."&key=".$this->_licenseKey;
  376.  
  377.         if ($this->_cacheEnabled && ($location $this->_cache->get($id"location"))) {
  378.             // Get data from cache
  379.             $this->_location = $location;
  380.             $locationReturn["cache""HIT";
  381.         else {
  382.             // Same as in the function above...
  383.             $status $this->_parseWeatherData($id$locationURL);
  384.  
  385.             if (Services_Weather::isError($status)) {
  386.                 return $status;
  387.             }
  388.             $locationReturn["cache""MISS";
  389.         }
  390.  
  391.         $locationReturn["name"]      $this->_location->dnam;
  392.         $locationReturn["time"]      date($this->_timeFormatstrtotime($this->_location->tm));
  393.         $locationReturn["latitude"]  $this->_location->lat;
  394.         $locationReturn["longitude"$this->_location->lon;
  395.         $locationReturn["sunrise"]   date($this->_timeFormatstrtotime($this->_location->sunr));
  396.         $locationReturn["sunset"]    date($this->_timeFormatstrtotime($this->_location->suns));
  397.         $locationReturn["timezone"]  $this->_location->zone;
  398.  
  399.         return $locationReturn;
  400.     }
  401.     // }}}
  402.  
  403.     // {{{ getWeather()
  404.     /**
  405.     * Returns the weather-data for the supplied location
  406.     *
  407.     * @param    string                      $id 
  408.     * @param    string                      $unitsFormat 
  409.     * @return   PEAR_Error|array
  410.     * @throws   PEAR_Error
  411.     * @access   public
  412.     */
  413.     function getWeather($id ""$unitsFormat "")
  414.     {
  415.         $status $this->_checkLocationID($id);
  416.  
  417.         if (Services_Weather::isError($status)) {
  418.             return $status;
  419.         }
  420.  
  421.         // Get other data
  422.         $units    $this->getUnitsFormat($unitsFormat);
  423.  
  424.         $weatherReturn = array();
  425.         $weatherURL    "http://xoap.weather.com/weather/local/".$id."?cc=*&prod=xoap&par=".$this->_partnerID."&key=".$this->_licenseKey."&unit=s";
  426.  
  427.         if ($this->_cacheEnabled && ($weather $this->_cache->get($id"weather"))) {
  428.             // Same procedure...
  429.             $this->_weather = $weather;
  430.             $weatherReturn["cache""HIT";
  431.         else {
  432.             // ...as last function
  433.             $status $this->_parseWeatherData($id$weatherURL);
  434.  
  435.             if (Services_Weather::isError($status)) {
  436.                 return $status;
  437.             }
  438.             $weatherReturn["cache""MISS";
  439.         }
  440.         
  441.         // Some explanation for the next two lines:
  442.         // weather.com isn't always supplying the timezone in the update string, but
  443.         // uses "Local Time" as reference, which is imho utterly stupid, because it's
  444.         // inconsistent. Well, what I do here is check for this string and if I can
  445.         // find it, I calculate the difference between the timezone at the location
  446.         // and this computers timezone. This amount of seconds is then subtracted from
  447.         // the time the update-string has delivered.
  448.         $update   str_replace("Local Time"""$this->_weather->lsup);
  449.         $adjustTZ ($update == $this->_weather->lsup? 0 : $this->_location->zone * 3600 - date("Z");
  450.         $weatherReturn["update"]            gmdate(trim($this->_dateFormat." ".$this->_timeFormat)strtotime($update$adjustTZ);
  451.         $weatherReturn["updateRaw"]         $this->_weather->lsup;
  452.         $weatherReturn["station"]           $this->_weather->obst;
  453.         $weatherReturn["temperature"]       $this->convertTemperature($this->_weather->tmp"f"$units["temp"]);
  454.         $weatherReturn["feltTemperature"]   $this->convertTemperature($this->_weather->flik"f"$units["temp"]);
  455.         $weatherReturn["condition"]         $this->_weather->t;
  456.         $weatherReturn["conditionIcon"]     $this->_weather->icon;
  457.         $weatherReturn["pressure"]          $this->convertPressure($this->_weather->bar->r"in"$units["pres"]);
  458.         $weatherReturn["pressureTrend"]     $this->_weather->bar->d;
  459.         $weatherReturn["wind"]              $this->convertSpeed($this->_weather->wind->s"mph"$units["wind"]);
  460.         $weatherReturn["windGust"]            $this->convertSpeed($this->_weather->wind->gust"mph"$units["wind"]);        
  461.         $weatherReturn["windDegrees"]       $this->_weather->wind->d;
  462.         $weatherReturn["windDirection"]     $this->_weather->wind->t;
  463.         $weatherReturn["humidity"]          $this->_weather->hmid;
  464.         if (is_numeric($this->_weather->vis)) {
  465.             $weatherReturn["visibility"]    $this->convertDistance($this->_weather->vis"sm"$units["vis"]);
  466.         else {
  467.             $weatherReturn["visibility"]    $this->_weather->vis;
  468.         }
  469.         $weatherReturn["uvIndex"]           $this->_weather->uv->i;
  470.         $weatherReturn["uvText"]            $this->_weather->uv->t;
  471.         $weatherReturn["dewPoint"]          $this->convertTemperature($this->_weather->dewp"f"$units["temp"]);
  472.  
  473.         return $weatherReturn;
  474.     }
  475.     // }}}
  476.  
  477.     // {{{ getForecast()
  478.     /**
  479.     * Get the forecast for the next days
  480.     *
  481.     * @param    string                      $id 
  482.     * @param    int                         $days           Values between 1 and 10
  483.     * @param    string                      $unitsFormat 
  484.     * @return   PEAR_Error|array
  485.     * @throws   PEAR_Error
  486.     * @access   public
  487.     */
  488.     function getForecast($id ""$days = 2$unitsFormat "")
  489.     {
  490.         $status $this->_checkLocationID($id);
  491.  
  492.         if (Services_Weather::isError($status)) {
  493.             return $status;
  494.         }
  495.         if (!in_array($daysrange(110))) {
  496.             $days = 2;
  497.         }
  498.         
  499.         // Get other data
  500.         $units    $this->getUnitsFormat($unitsFormat);
  501.  
  502.         $forecastReturn = array();
  503.         $forecastURL "http://xoap.weather.com/weather/local/".$id."?dayf=10&prod=xoap&par=".$this->_partnerID."&key=".$this->_licenseKey."&unit=s";
  504.  
  505.         if ($this->_cacheEnabled && ($forecast $this->_cache->get($id"forecast"))) {
  506.             // Encore...
  507.             $this->_forecast = $forecast;
  508.             $forecastReturn["cache""HIT";
  509.         else {
  510.             // ...
  511.             $status $this->_parseWeatherData($id$forecastURL$days);
  512.  
  513.             if (Services_Weather::isError($status)) {
  514.                 return $status;
  515.             }
  516.             $forecastReturn["cache""MISS";
  517.         }
  518.  
  519.         // Some explanation for the next two lines: (same as above)
  520.         // weather.com isn't always supplying the timezone in the update string, but
  521.         // uses "Local Time" as reference, which is imho utterly stupid, because it's
  522.         // inconsistent. Well, what I do here is check for this string and if I can
  523.         // find it, I calculate the difference between the timezone at the location
  524.         // and this computers timezone. This amount of seconds is then subtracted from
  525.         // the time the update-string has delivered.
  526.         $update   str_replace("Local Time"""$this->_forecast->lsup);
  527.         $adjustTZ ($update == $this->_forecast->lsup? 0 : $this->_location->zone * 3600 - date("Z");
  528.         $forecastReturn["update"]    gmdate($this->_dateFormat." ".$this->_timeFormatstrtotime($update$adjustTZ);
  529.         $forecastReturn["updateRaw"$this->_forecast->lsup;
  530.         $forecastReturn["days"]      = array();
  531.  
  532.         for ($i = 0; $i $days$i++{
  533.             $day = array(
  534.                 "temperatureHigh" => $this->convertTemperature($this->_forecast->day[$i]->hi"f"$units["temp"]),
  535.                 "temperatureLow"  => $this->convertTemperature($this->_forecast->day[$i]->low"f"$units["temp"]),
  536.                 "sunrise"         => date($this->_timeFormatstrtotime($this->_forecast->day[$i]->sunr)),
  537.                 "sunset"          => date($this->_timeFormatstrtotime($this->_forecast->day[$i]->suns)),
  538.                 "day" => array(
  539.                     "condition"     => $this->_forecast->day[$i]->part[0]->t,
  540.                     "conditionIcon" => $this->_forecast->day[$i]->part[0]->icon,
  541.                     "wind"          => $this->convertSpeed($this->_forecast->day[$i]->part[0]->wind->s"mph"$units["wind"]),
  542.                     "windGust"      => $this->convertSpeed($this->_forecast->day[$i]->part[0]->wind->gust"mph"$units["wind"]),
  543.                     "windDegrees"   => $this->_forecast->day[$i]->part[0]->wind->d,
  544.                     "windDirection" => $this->_forecast->day[$i]->part[0]->wind->t,
  545.                     "precipitation" => $this->_forecast->day[$i]->part[0]->ppcp,
  546.                     "humidity"      => $this->_forecast->day[$i]->part[0]->hmid
  547.                 ),
  548.                 "night" => array (
  549.                     "condition"     => $this->_forecast->day[$i]->part[1]->t,
  550.                     "conditionIcon" => $this->_forecast->day[$i]->part[1]->icon,
  551.                     "wind"          => $this->convertSpeed($this->_forecast->day[$i]->part[1]->wind->s"mph"$units["wind"]),
  552.                     "windGust"      => $this->convertSpeed($this->_forecast->day[$i]->part[1]->wind->gust"mph"$units["wind"]),
  553.                     "windDegrees"   => $this->_forecast->day[$i]->part[1]->wind->d,
  554.                     "windDirection" => $this->_forecast->day[$i]->part[1]->wind->t,
  555.                     "precipitation" => $this->_forecast->day[$i]->part[1]->ppcp,
  556.                     "humidity"      => $this->_forecast->day[$i]->part[1]->hmid
  557.                 )
  558.             );
  559.  
  560.             $forecastReturn["days"][$day;
  561.         }
  562.  
  563.         return $forecastReturn;
  564.     }
  565.     // }}}
  566. }
  567. // }}}
  568. ?>

Documentation generated on Mon, 11 Mar 2019 13:56:24 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.