pax_global_header00006660000000000000000000000064125033220360014506gustar00rootroot0000000000000052 comment=b2dd62a758da8b05544bd1c95c50d2566ff89621 php-services-weather-1.4.7/000077500000000000000000000000001250332203600155645ustar00rootroot00000000000000php-services-weather-1.4.7/Services_Weather-1.4.7/000077500000000000000000000000001250332203600214735ustar00rootroot00000000000000php-services-weather-1.4.7/Services_Weather-1.4.7/Weather.php000066400000000000000000000261171250332203600236120ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/Services_Weather * @filesource */ // {{{ constants // {{{ cache times define("SERVICES_WEATHER_EXPIRES_UNITS", 900); // 15M define("SERVICES_WEATHER_EXPIRES_LOCATION", 43200); // 12H define("SERVICES_WEATHER_EXPIRES_WEATHER", 1800); // 30M define("SERVICES_WEATHER_EXPIRES_FORECAST", 7200); // 2H define("SERVICES_WEATHER_EXPIRES_LINKS", 43200); // 12H define("SERVICES_WEATHER_EXPIRES_SEARCH", 2419200); // 28D // }}} // {{{ error codes define("SERVICES_WEATHER_ERROR_SERVICE_NOT_FOUND", 10); define("SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION", 11); define("SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA", 12); define("SERVICES_WEATHER_ERROR_CACHE_INIT_FAILED", 13); define("SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED", 14); define("SERVICES_WEATHER_ERROR_HTTP_PROXY_INVALID", 15); define("SERVICES_WEATHER_ERROR_SUNFUNCS_DATE_INVALID", 16); define("SERVICES_WEATHER_ERROR_SUNFUNCS_RETFORM_INVALID", 17); define("SERVICES_WEATHER_ERROR_METAR_SOURCE_INVALID", 18); define("SERVICES_WEATHER_ERROR_MOONFUNCS_DATE_INVALID", 19); // }}} // {{{ error codes defined by weather.com define("SERVICES_WEATHER_ERROR_UNKNOWN_ERROR", 0); define("SERVICES_WEATHER_ERROR_NO_LOCATION", 1); define("SERVICES_WEATHER_ERROR_INVALID_LOCATION", 2); define("SERVICES_WEATHER_ERROR_INVALID_PARTNER_ID", 100); define("SERVICES_WEATHER_ERROR_INVALID_PRODUCT_CODE", 101); define("SERVICES_WEATHER_ERROR_INVALID_LICENSE_KEY", 102); // }}} // }}} // {{{ class Services_Weather /** * This class acts as an interface to various online weather-services. * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version Release: 1.4.7 * @link http://pear.php.net/package/Services_Weather */ class Services_Weather { // {{{ &service() /** * Factory for creating the services-objects * * Usable keys for the options array are: * o debug enables debugging output * --- Common Options * o cacheType defines what type of cache to use * o cacheOptions passes cache options * o unitsFormat use (US)-standard, metric or custom units * o customUnitsFormat defines the customized units format * o httpTimeout sets timeout for HTTP requests * o httpProxy sets proxy for HTTP requests, please use the * notation http://[user[:pass]@]host[:port] * o dateFormat string to use for date output * o timeFormat string to use for time output * --- EJSE Options * o none * --- GlobalWeather Options * o none * --- METAR/TAF Options * o dsn String for defining the DB connection * o dbOptions passes DB options * o sourceMetar http, ftp or file - type of data-source for METAR * o sourcePathMetar where to look for the source, URI or filepath, * of METAR information * o sourceTaf http, ftp or file - type of data-source for TAF * o sourcePathTaf where to look for the source, URI or filepath, * of TAF information * --- weather.com Options * o partnerID You'll receive these keys after registering * o licenseKey with the weather.com XML-service * o preFetch Enables pre-fetching of data in one single request * * @param string $service * @param array $options * @return PEAR_Error|object * @throws PEAR_Error * @throws PEAR_Error::SERVICES_WEATHER_ERROR_SERVICE_NOT_FOUND * @access public */ function &service($service, $options = null) { $service = ucfirst(strtolower($service)); $classname = "Services_Weather_".$service; // Check for debugging-mode and set stuff accordingly if (is_array($options) && isset($options["debug"]) && $options["debug"] >= 2) { if (!defined("SERVICES_WEATHER_DEBUG")) { define("SERVICES_WEATHER_DEBUG", true); } include_once("Services/Weather/".$service.".php"); } else { if (!defined("SERVICES_WEATHER_DEBUG")) { define("SERVICES_WEATHER_DEBUG", false); } @include_once("Services/Weather/".$service.".php"); } // No such service... bail out if (!class_exists($classname)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_SERVICE_NOT_FOUND, __FILE__, __LINE__); } // Create service and return $error = null; @$obj = &new $classname($options, $error); if (Services_Weather::isError($error)) { return $error; } else { return $obj; } } // }}} // {{{ apiVersion() /** * For your convenience, when I come up with changes in the API... * * @return string * @access public */ function apiVersion() { return "1.4"; } // }}} // {{{ _errorMessage() /** * Returns the message for a certain error code * * @param PEAR_Error|int $value * @return string * @access private */ function _errorMessage($value) { static $errorMessages; if (!isset($errorMessages)) { $errorMessages = array( SERVICES_WEATHER_ERROR_SERVICE_NOT_FOUND => "Requested service could not be found.", SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION => "Unknown location provided.", SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA => "Server data wrong or not available.", SERVICES_WEATHER_ERROR_CACHE_INIT_FAILED => "Cache init was not completed.", SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED => "MetarDB is not connected.", SERVICES_WEATHER_ERROR_HTTP_PROXY_INVALID => "The given proxy is not valid, please use the notation http://[user[:pass]@]host[:port].", SERVICES_WEATHER_ERROR_SUNFUNCS_DATE_INVALID => "The date you've provided for calculation of sunrise/sunset is not a timestamp.", SERVICES_WEATHER_ERROR_SUNFUNCS_RETFORM_INVALID => "The return format you've provided for calculation of sunrise/sunset is not valid.", SERVICES_WEATHER_ERROR_METAR_SOURCE_INVALID => "The METAR/TAF source you've provided has an invalid type or path.", SERVICES_WEATHER_ERROR_UNKNOWN_ERROR => "An unknown error has occured.", SERVICES_WEATHER_ERROR_NO_LOCATION => "No location provided.", SERVICES_WEATHER_ERROR_INVALID_LOCATION => "Invalid location provided.", SERVICES_WEATHER_ERROR_INVALID_PARTNER_ID => "Invalid partner id.", SERVICES_WEATHER_ERROR_INVALID_PRODUCT_CODE => "Invalid product code.", SERVICES_WEATHER_ERROR_INVALID_LICENSE_KEY => "Invalid license key." ); } if (Services_Weather::isError($value)) { $value = $value->getCode(); } return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[SERVICES_WEATHER_ERROR_UNKNOWN_ERROR]; } // }}} // {{{ isError() /** * Checks for an error object, same as in PEAR * * @param PEAR_Error|mixed $value * @return bool * @access public */ function isError($value) { return (is_object($value) && (strtolower(get_class($value)) == "pear_error" || is_subclass_of($value, "pear_error"))); } // }}} // {{{ &raiseError() /** * Creates error, same as in PEAR with a customized flavor * * @param int $code * @param string $file * @param int $line * @return PEAR_Error * @access private */ function &raiseError($code = SERVICES_WEATHER_ERROR_UNKNOWN_ERROR, $file = "", $line = 0) { // This should improve the performance of the script, as PEAR is only included, when // really needed. include_once "PEAR.php"; $message = "Services_Weather"; if ($file != "" && $line > 0) { $message .= " (".basename($file).":".$line.")"; } $message .= ": ".Services_Weather::_errorMessage($code); $error = PEAR::raiseError($message, $code, PEAR_ERROR_RETURN, E_USER_NOTICE, "Services_Weather_Error", null, false); return $error; } // }}} } // }}} ?> php-services-weather-1.4.7/Services_Weather-1.4.7/Weather/000077500000000000000000000000001250332203600230725ustar00rootroot00000000000000php-services-weather-1.4.7/Services_Weather-1.4.7/Weather/Common.php000066400000000000000000001355151250332203600250450ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/Services_Weather * @filesource */ require_once "Services/Weather.php"; // {{{ constants // {{{ natural constants and measures define("SERVICES_WEATHER_RADIUS_EARTH", 6378.15); // }}} // {{{ default values for the sun-functions define("SERVICES_WEATHER_SUNFUNCS_DEFAULT_LATITUDE", 31.7667); define("SERVICES_WEATHER_SUNFUNCS_DEFAULT_LONGITUDE", 35.2333); define("SERVICES_WEATHER_SUNFUNCS_SUNRISE_ZENITH", 90.83); define("SERVICES_WEATHER_SUNFUNCS_SUNSET_ZENITH", 90.83); // }}} // }}} // {{{ class Services_Weather_Common /** * Parent class for weather-services. Defines common functions for unit * conversions, checks for cache enabling and does other miscellaneous * things. * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version Release: 1.4.7 * @link http://pear.php.net/package/Services_Weather */ class Services_Weather_Common { // {{{ properties /** * Format of the units provided (standard/metric/custom) * * @var string $_unitsFormat * @access private */ var $_unitsFormat = "s"; /** * Custom format of the units * * @var array $_customUnitsFormat * @access private */ var $_customUnitsFormat = array( "temp" => "f", "vis" => "sm", "height" => "ft", "wind" => "mph", "pres" => "in", "rain" => "in" ); /** * Options for HTTP requests * * @var array $_httpOptions * @access private */ var $_httpOptions = array(); /** * Format of the used dates * * @var string $_dateFormat * @access private */ var $_dateFormat = "m/d/y"; /** * Format of the used times * * @var string $_timeFormat * @access private */ var $_timeFormat = "G:i A"; /** * Object containing the location-data * * @var object stdClass $_location * @access private */ var $_location; /** * Object containing the weather-data * * @var object stdClass $_weather * @access private */ var $_weather; /** * Object containing the forecast-data * * @var object stdClass $_forecast * @access private */ var $_forecast; /** * Cache, containing the data-objects * * @var object Cache $_cache * @access private */ var $_cache; /** * Provides check for Cache * * @var bool $_cacheEnabled * @access private */ var $_cacheEnabled = false; // }}} // {{{ constructor /** * Constructor * * @param array $options * @param mixed $error * @throws PEAR_Error * @access private */ function Services_Weather_Common($options, &$error) { // Set some constants for the case when PHP4 is used, as the // date_sunset/sunrise functions are not implemented there if (!defined("SUNFUNCS_RET_TIMESTAMP")) { define("SUNFUNCS_RET_TIMESTAMP", 0); define("SUNFUNCS_RET_STRING", 1); define("SUNFUNCS_RET_DOUBLE", 2); } // Set options accordingly if (isset($options["cacheType"])) { if (isset($options["cacheOptions"])) { $status = $this->setCache($options["cacheType"], $options["cacheOptions"]); } else { $status = $this->setCache($options["cacheType"]); } if (Services_Weather::isError($status)) { $error = $status; return; } } if (isset($options["unitsFormat"])) { if (isset($options["customUnitsFormat"])) { $this->setUnitsFormat($options["unitsFormat"], $options["customUnitsFormat"]); } else { $this->setUnitsFormat($options["unitsFormat"]); } } if (isset($options["httpTimeout"])) { $this->setHttpTimeout($options["httpTimeout"]); } else { $this->setHttpTimeout(60); } if (isset($options["httpProxy"])) { $status = $this->setHttpProxy($options["httpProxy"]); if (Services_Weather::isError($status)) { $error = $status; return; } } if (isset($options["dateFormat"])) { $this->setDateTimeFormat($options["dateFormat"], ""); } if (isset($options["timeFormat"])) { $this->setDateTimeFormat("", $options["timeFormat"]); } } // }}} // {{{ setCache() /** * Enables caching the data, usage strongly recommended * * Requires Cache to be installed * * @param string $cacheType * @param array $cacheOptions * @return PEAR_Error|bool * @throws PEAR_Error::SERVICES_WEATHER_ERROR_CACHE_INIT_FAILED * @access public */ function setCache($cacheType = "file", $cacheOptions = array()) { if ($cacheType == "lite") { if ((@include_once "Cache/Lite.php") == false) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_CACHE_INIT_FAILED, __FILE__, __LINE__); } else { $cacheOptions["automaticSerialization"] = true; $cacheOptions["pearErrorMode"] = CACHE_LITE_ERROR_RETURN; $cacheOptions["lifeTime"] = null; @$cache = new Cache_Lite($cacheOptions); } } else { // The error handling in Cache is a bit crummy (read: not existent) // so we have to do that on our own... if ((@include_once "Cache.php") === false) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_CACHE_INIT_FAILED, __FILE__, __LINE__); } else { @$cache = new Cache($cacheType, $cacheOptions); } } if (is_object($cache) && (strtolower(get_class($cache)) == "cache_lite" || strtolower(get_class($cache)) == "cache" || is_subclass_of($cache, "cache"))) { $this->_cache = $cache; $this->_cacheEnabled = true; } else { $this->_cache = null; $this->_cacheEnabled = false; return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_CACHE_INIT_FAILED, __FILE__, __LINE__); } return true; } // }}} // {{{ _getCache() /** * Wrapper to retrieve cached data * * Requires Cache to be installed * * @param string $id * @param string $type * @return array|bool * @access private */ function _getCache($id, $type) { if ($this->_cacheEnabled) { if (strtolower(get_class($this->_cache)) == "cache_lite") { $this->_cache->setLifeTime(constant("SERVICES_WEATHER_EXPIRES_".strtoupper($type))); $cache = $this->_cache->get($id, $type); } else { $cache = $this->_cache->get($id, $type); } return $cache; } else { return false; } } // }}} // {{{ _getUserCache() /** * Wrapper to retrieve cached user-data * * Requires Cache to be installed * * @param string $id * @param string $type * @return array|bool * @access private */ function _getUserCache($id, $type) { if ($this->_cacheEnabled) { if (strtolower(get_class($this->_cache)) == "cache_lite") { $this->_cache->setLifeTime(constant("SERVICES_WEATHER_EXPIRES_".strtoupper($type))); $cache = $this->_cache->get($id, $type."_user"); } else { $cache = $this->_cache->getUserdata($id, $type); } return $cache; } else { return false; } } // }}} // {{{ _saveCache() /** * Wrapper to save data to cache * * Requires Cache to be installed * * @param string $id * @param mixed $data * @param mixed $userData * @param string $type * @return array|bool * @access private */ function _saveCache($id, $data, $userData, $type) { if ($this->_cacheEnabled) { if (strtolower(get_class($this->_cache)) == "cache_lite") { $this->_cache->setLifeTime(null); return ($this->_cache->save($data, $id, $type) && $this->_cache->save($userData, $id, $type."_user")); } else { return $this->_cache->extSave($id, $data, $userData, constant("SERVICES_WEATHER_EXPIRES_".strtoupper($type)), $type); } } else { return false; } } // }}} // {{{ setUnitsFormat() /** * Changes the representation of the units (standard/metric) * * @param string $unitsFormat * @param array $customUnitsFormat * @access public */ function setUnitsFormat($unitsFormat, $customUnitsFormat = array()) { static $acceptedFormats; if (!isset($acceptedFormats)) { $acceptedFormats = array( "temp" => array("c", "f"), "vis" => array("m", "km", "ft", "sm"), "height" => array("m", "ft"), "wind" => array("mph", "kmh", "kt", "mps", "fps", "bft"), "pres" => array("in", "hpa", "mb", "mm", "atm"), "rain" => array("in", "mm") ); } if (strlen($unitsFormat) && in_array(strtolower($unitsFormat{0}), array("c", "m", "s"))) { $this->_unitsFormat = strtolower($unitsFormat{0}); if ($this->_unitsFormat == "c" && is_array($customUnitsFormat)) { foreach ($customUnitsFormat as $key => $value) { if (array_key_exists($key, $acceptedFormats) && in_array($value, $acceptedFormats[$key])) { $this->_customUnitsFormat[$key] = $value; } } } elseif ($this->_unitsFormat == "c") { $this->_unitsFormat = "s"; } } } // }}} // {{{ setHttpOption() /** * Sets an option for usage in HTTP_Request objects * * @param string $varName * @param mixed $varValue * @access public */ function setHttpOption($varName, $varValue) { if (is_string($varName) && $varName != "" && !empty($varValue)) { $this->_httpOptions[$varName] = $varValue; } } // }}} // {{{ setHttpTimeout() /** * Sets the timeout in seconds for HTTP requests * * @param int $httpTimeout * @access public */ function setHttpTimeout($httpTimeout) { if (is_int($httpTimeout)) { $this->_httpOptions["timeout"] = $httpTimeout; } } // }}} // {{{ setHttpProxy() /** * Sets the proxy for HTTP requests * * @param string $httpProxy * @access public */ function setHttpProxy($httpProxy) { if (($proxy = parse_url($httpProxy)) !== false && $proxy["scheme"] == "http") { if (isset($proxy["user"]) && $proxy["user"] != "") { $this->_httpOptions["proxy_user"] = $proxy["user"]; } if (isset($proxy["pass"]) && $proxy["pass"] != "") { $this->_httpOptions["proxy_pass"] = $proxy["pass"]; } if (isset($proxy["host"]) && $proxy["host"] != "") { $this->_httpOptions["proxy_host"] = $proxy["host"]; } if (isset($proxy["port"]) && $proxy["port"] != "") { $this->_httpOptions["proxy_port"] = $proxy["port"]; } return true; } else { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_HTTP_PROXY_INVALID, __FILE__, __LINE__); } } // }}} // {{{ getUnitsFormat() /** * Returns the selected units format * * @param string $unitsFormat * @return array * @access public */ function getUnitsFormat($unitsFormat = "") { // This is cheap'o stuff if (strlen($unitsFormat) && in_array(strtolower($unitsFormat{0}), array("c", "m", "s"))) { $unitsFormat = strtolower($unitsFormat{0}); } else { $unitsFormat = $this->_unitsFormat; } $c = $this->_customUnitsFormat; $m = array( "temp" => "c", "vis" => "km", "height" => "m", "wind" => "kmh", "pres" => "mb", "rain" => "mm" ); $s = array( "temp" => "f", "vis" => "sm", "height" => "ft", "wind" => "mph", "pres" => "in", "rain" => "in" ); return ${$unitsFormat}; } // }}} // {{{ setDateTimeFormat() /** * Changes the representation of time and dates (see http://www.php.net/date) * * @param string $dateFormat * @param string $timeFormat * @access public */ function setDateTimeFormat($dateFormat = "", $timeFormat = "") { if (strlen($dateFormat)) { $this->_dateFormat = $dateFormat; } if (strlen($timeFormat)) { $this->_timeFormat = $timeFormat; } } // }}} // {{{ convertTemperature() /** * Convert temperature between f and c * * @param float $temperature * @param string $from * @param string $to * @return float * @access public */ function convertTemperature($temperature, $from, $to) { if ($temperature == "N/A") { return $temperature; } $from = strtolower($from{0}); $to = strtolower($to{0}); $result = array( "f" => array( "f" => $temperature, "c" => ($temperature - 32) / 1.8 ), "c" => array( "f" => 1.8 * $temperature + 32, "c" => $temperature ) ); return $result[$from][$to]; } // }}} // {{{ convertSpeed() /** * Convert speed between mph, kmh, kt, mps, fps and bft * * Function will return "false" when trying to convert from * Beaufort, as it is a scale and not a true measurement * * @param float $speed * @param string $from * @param string $to * @return float|int|bool * @access public * @link http://www.spc.noaa.gov/faq/tornado/beaufort.html */ function convertSpeed($speed, $from, $to) { $from = strtolower($from); $to = strtolower($to); static $factor; static $beaufort; if (!isset($factor)) { $factor = array( "mph" => array( "mph" => 1, "kmh" => 1.609344, "kt" => 0.8689762, "mps" => 0.44704, "fps" => 1.4666667 ), "kmh" => array( "mph" => 0.6213712, "kmh" => 1, "kt" => 0.5399568, "mps" => 0.2777778, "fps" => 0.9113444 ), "kt" => array( "mph" => 1.1507794, "kmh" => 1.852, "kt" => 1, "mps" => 0.5144444, "fps" => 1.6878099 ), "mps" => array( "mph" => 2.2369363, "kmh" => 3.6, "kt" => 1.9438445, "mps" => 1, "fps" => 3.2808399 ), "fps" => array( "mph" => 0.6818182, "kmh" => 1.09728, "kt" => 0.5924838, "mps" => 0.3048, "fps" => 1 ) ); // Beaufort scale, measurements are in knots $beaufort = array( 1, 3, 6, 10, 16, 21, 27, 33, 40, 47, 55, 63 ); } if ($from == "bft") { return false; } elseif ($to == "bft") { $speed = round($speed * $factor[$from]["kt"], 0); for ($i = 0; $i < sizeof($beaufort); $i++) { if ($speed <= $beaufort[$i]) { return $i; } } return sizeof($beaufort); } else { return ($speed * $factor[$from][$to]); } } // }}} // {{{ convertPressure() /** * Convert pressure between in, hpa, mb, mm and atm * * @param float $pressure * @param string $from * @param string $to * @return float * @access public */ function convertPressure($pressure, $from, $to) { $from = strtolower($from); $to = strtolower($to); static $factor; if (!isset($factor)) { $factor = array( "in" => array( "in" => 1, "hpa" => 33.863887, "mb" => 33.863887, "mm" => 25.4, "atm" => 0.0334213 ), "hpa" => array( "in" => 0.02953, "hpa" => 1, "mb" => 1, "mm" => 0.7500616, "atm" => 0.0009869 ), "mb" => array( "in" => 0.02953, "hpa" => 1, "mb" => 1, "mm" => 0.7500616, "atm" => 0.0009869 ), "mm" => array( "in" => 0.0393701, "hpa" => 1.3332239, "mb" => 1.3332239, "mm" => 1, "atm" => 0.0013158 ), "atm" => array( "in" => 29,921258, "hpa" => 1013.2501, "mb" => 1013.2501, "mm" => 759.999952, "atm" => 1 ) ); } return ($pressure * $factor[$from][$to]); } // }}} // {{{ convertDistance() /** * Convert distance between km, ft and sm * * @param float $distance * @param string $from * @param string $to * @return float * @access public */ function convertDistance($distance, $from, $to) { $to = strtolower($to); $from = strtolower($from); static $factor; if (!isset($factor)) { $factor = array( "m" => array( "m" => 1, "km" => 1000, "ft" => 3.280839895, "sm" => 0.0006213699 ), "km" => array( "m" => 0.001, "km" => 1, "ft" => 3280.839895, "sm" => 0.6213699 ), "ft" => array( "m" => 0.3048, "km" => 0.0003048, "ft" => 1, "sm" => 0.0001894 ), "sm" => array( "m" => 0.0016093472, "km" => 1.6093472, "ft" => 5280.0106, "sm" => 1 ) ); } return ($distance * $factor[$from][$to]); } // }}} // {{{ calculateWindChill() /** * Calculate windchill from temperature and windspeed (enhanced formula) * * Temperature has to be entered in deg F, speed in mph! * * @param float $temperature * @param float $speed * @return float * @access public * @link http://www.nws.noaa.gov/om/windchill/ */ function calculateWindChill($temperature, $speed) { return (35.74 + 0.6215 * $temperature - 35.75 * pow($speed, 0.16) + 0.4275 * $temperature * pow($speed, 0.16)); } // }}} // {{{ calculateHumidity() /** * Calculate humidity from temperature and dewpoint * This is only an approximation, there is no exact formula, this * one here is called Magnus-Formula * * Temperature and dewpoint have to be entered in deg C! * * @param float $temperature * @param float $dewPoint * @return float * @access public * @link http://www.faqs.org/faqs/meteorology/temp-dewpoint/ */ function calculateHumidity($temperature, $dewPoint) { // First calculate saturation steam pressure for both temperatures if ($temperature >= 0) { $a = 7.5; $b = 237.3; } else { $a = 7.6; $b = 240.7; } $tempSSP = 6.1078 * pow(10, ($a * $temperature) / ($b + $temperature)); if ($dewPoint >= 0) { $a = 7.5; $b = 237.3; } else { $a = 7.6; $b = 240.7; } $dewSSP = 6.1078 * pow(10, ($a * $dewPoint) / ($b + $dewPoint)); return (100 * $dewSSP / $tempSSP); } // }}} // {{{ calculateDewPoint() /** * Calculate dewpoint from temperature and humidity * This is only an approximation, there is no exact formula, this * one here is called Magnus-Formula * * Temperature has to be entered in deg C! * * @param float $temperature * @param float $humidity * @return float * @access public * @link http://www.faqs.org/faqs/meteorology/temp-dewpoint/ */ function calculateDewPoint($temperature, $humidity) { if ($temperature >= 0) { $a = 7.5; $b = 237.3; } else { $a = 7.6; $b = 240.7; } // First calculate saturation steam pressure for temperature $SSP = 6.1078 * pow(10, ($a * $temperature) / ($b + $temperature)); // Steam pressure $SP = $humidity / 100 * $SSP; $v = log($SP / 6.1078, 10); return ($b * $v / ($a - $v)); } // }}} // {{{ polar2cartesian() /** * Convert polar coordinates to cartesian coordinates * * @param float $latitude * @param float $longitude * @return array * @access public */ function polar2cartesian($latitude, $longitude) { $theta = deg2rad($latitude); $phi = deg2rad($longitude); $x = SERVICES_WEATHER_RADIUS_EARTH * cos($phi) * cos($theta); $y = SERVICES_WEATHER_RADIUS_EARTH * sin($phi) * cos($theta); $z = SERVICES_WEATHER_RADIUS_EARTH * sin($theta); return array($x, $y, $z); } // }}} // {{{ calculateMoonPhase() /** * Calculates the moon age and phase * * The algorithms for this functions were taken from the German Wikipedia * entry on Julian Daycount for getting the accurate JD to the second and * the overall moon calculation were done according to * Stephen R. Schmitt's website, which is cited multiple times on the web * for this kind of calculation. * * The date has to be entered as a timestamp! * * @param int $date * @return PEAR_Error|array * @throws PEAR_Error::SERVICES_WEATHER_ERROR_MOONFUNCS_DATE_INVALID * @access public * @link http://de.wikipedia.org/wiki/Julianisches_Datum * @link http://mysite.verizon.net/res148h4j/javascript/script_moon_phase.html */ function calculateMoonPhase($date) { // Date must be timestamp for now if (!is_int($date)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_MOONFUNCS_DATE_INVALID, __FILE__, __LINE__); } $moon = array(); $year = date("Y", $date); $month = date("n", $date); $day = date("j", $date); $hour = date("G", $date); $min = date("i", $date); $sec = date("s", $date); $age = 0.0; // Moon's age in days from New Moon $distance = 0.0; // Moon's distance in Earth radii $latitude = 0.0; // Moon's ecliptic latitude in degrees $longitude = 0.0; // Moon's ecliptic longitude in degrees $phase = ""; // Moon's phase $zodiac = ""; // Moon's zodiac $icon = ""; // The icon to represent the moon phase $YY = 0; $MM = 0; $DD = 0; $HH = 0; $A = 0; $B = 0; $JD = 0; $IP = 0.0; $DP = 0.0; $NP = 0.0; $RP = 0.0; // Calculate Julian Daycount to the second if ($month > 2) { $YY = $year; $MM = $month; } else { $YY = $year - 1; $MM = $month + 12; } $DD = $day; $HH = $hour/24 + $min/1440 + $sec/86400; // Check for Gregorian date and adjust JD appropriately if (($year*10000 + $month*100 + $day) >= 15821015) { $A = floor($YY/100); $B = 2 - $A + floor($A/4); } $JD = floor(365.25*($YY+4716)) + floor(30.6001*($MM+1)) + $DD + $HH + $B - 1524.5; // Calculate moon's age in days $IP = ($JD - 2451550.1) / 29.530588853; if (($IP = $IP - floor($IP)) < 0) $IP++; $age = $IP * 29.530588853; switch ($age) { case ($age < 1.84566): $phase = "New"; break; case ($age < 5.53699): $phase = "Waxing Crescent"; break; case ($age < 9.22831): $phase = "First Quarter"; break; case ($age < 12.91963): $phase = "Waxing Gibbous"; break; case ($age < 16.61096): $phase = "Full"; break; case ($age < 20.30228): $phase = "Waning Gibbous"; break; case ($age < 23.99361): $phase = "Last Quarter"; break; case ($age < 27.68493): $phase = "Waning Crescent"; break; default: $phase = "New"; } // Convert phase to radians $IP = $IP * 2 * pi(); // Calculate moon's distance $DP = ($JD - 2451562.2) / 27.55454988; if (($DP = $DP - floor($DP)) < 0) $DP++; $DP = $DP * 2 * pi(); $distance = 60.4 - 3.3 * cos($DP) - 0.6 * cos(2 * $IP - $DP) - 0.5 * cos(2 * $IP); // Calculate moon's ecliptic latitude $NP = ($JD - 2451565.2) / 27.212220817; if (($NP = $NP - floor($NP)) < 0) $NP++; $NP = $NP * 2 * pi(); $latitude = 5.1 * sin($NP); // Calculate moon's ecliptic longitude $RP = ($JD - 2451555.8) / 27.321582241; if (($RP = $RP - floor($RP)) < 0) $RP++; $longitude = 360 * $RP + 6.3 * sin($DP) + 1.3 * sin(2 * $IP - $DP) + 0.7 * sin(2 * $IP); if ($longitude >= 360) $longitude -= 360; switch ($longitude) { case ($longitude < 33.18): $zodiac = "Pisces"; break; case ($longitude < 51.16): $zodiac = "Aries"; break; case ($longitude < 93.44): $zodiac = "Taurus"; break; case ($longitude < 119.48): $zodiac = "Gemini"; break; case ($longitude < 135.30): $zodiac = "Cancer"; break; case ($longitude < 173.34): $zodiac = "Leo"; break; case ($longitude < 224.17): $zodiac = "Virgo"; break; case ($longitude < 242.57): $zodiac = "Libra"; break; case ($longitude < 271.26): $zodiac = "Scorpio"; break; case ($longitude < 302.49): $zodiac = "Sagittarius"; break; case ($longitude < 311.72): $zodiac = "Capricorn"; break; case ($longitude < 348.58): $zodiac = "Aquarius"; break; default: $zodiac = "Pisces"; } $moon["age"] = round($age, 2); $moon["distance"] = round($distance, 2); $moon["latitude"] = round($latitude, 2); $moon["longitude"] = round($longitude, 2); $moon["zodiac"] = $zodiac; $moon["phase"] = $phase; $moon["icon"] = (floor($age) - 1).""; return $moon; } // }}} // {{{ calculateSunRiseSet() /** * Calculates sunrise and sunset for a location * * The sun position algorithm taken from the 'US Naval Observatory's * Almanac for Computers', implemented by Ken Bloom * for the zmanim project, converted to C by Moshe Doron * and finally taken from the PHP5 sources and converted to native PHP as a wrapper. * * The date has to be entered as a timestamp! * * @param int $date * @param int $retformat * @param float $latitude * @param float $longitude * @param float $zenith * @param float $gmt_offset * @param bool $sunrise * @return PEAR_Error|mixed * @throws PEAR_Error::SERVICES_WEATHER_ERROR_SUNFUNCS_DATE_INVALID * @throws PEAR_Error::SERVICES_WEATHER_ERROR_SUNFUNCS_RETFORM_INVALID * @throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_ERROR * @access public */ function calculateSunRiseSet($date, $retformat = null, $latitude = null, $longitude = null, $zenith = null, $gmt_offset = null, $sunrise = true) { // Date must be timestamp for now if (!is_int($date)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_SUNFUNCS_DATE_INVALID, __FILE__, __LINE__); } // Check for proper return format if ($retformat === null) { $retformat = SUNFUNCS_RET_STRING; } elseif (!in_array($retformat, array(SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING, SUNFUNCS_RET_DOUBLE)) ) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_SUNFUNCS_RETFORM_INVALID, __FILE__, __LINE__); } // Set default values for coordinates if ($latitude === null) { $latitude = SUNFUNCS_DEFAULT_LATITUDE; } else { $latitude = (float) $latitude; } if ($longitude === null) { $longitude = SUNFUNCS_DEFAULT_LONGITUDE; } else { $longitude = (float) $longitude; } if ($zenith === null) { if($sunrise) { $zenith = SUNFUNCS_SUNRISE_ZENITH; } else { $zenith = SUNFUNCS_SUNSET_ZENITH; } } else { $zenith = (float) $zenith; } // Default value for GMT offset if ($gmt_offset === null) { $gmt_offset = date("Z", $date) / 3600; } else { $gmt_offset = (float) $gmt_offset; } // If we have PHP5, then act as wrapper for the appropriate functions if ($sunrise && function_exists("date_sunrise")) { return date_sunrise($date, $retformat, $latitude, $longitude, $zenith, $gmt_offset); } if (!$sunrise && function_exists("date_sunset")) { return date_sunset($date, $retformat, $latitude, $longitude, $zenith, $gmt_offset); } // Apparently we have PHP4, so calculate the neccessary steps in native PHP // Step 1: First calculate the day of the year $N = date("z", $date) + 1; // Step 2: Convert the longitude to hour value and calculate an approximate time $lngHour = $longitude / 15; // Use 18 for sunset instead of 6 if ($sunrise) { // Sunrise $t = $N + ((6 - $lngHour) / 24); } else { // Sunset $t = $N + ((18 - $lngHour) / 24); } // Step 3: Calculate the sun's mean anomaly $M = (0.9856 * $t) - 3.289; // Step 4: Calculate the sun's true longitude $L = $M + (1.916 * sin(deg2rad($M))) + (0.020 * sin(deg2rad(2 * $M))) + 282.634; while ($L < 0) { $Lx = $L + 360; assert($Lx != $L); // askingtheguru: really needed? $L = $Lx; } while ($L >= 360) { $Lx = $L - 360; assert($Lx != $L); // askingtheguru: really needed? $L = $Lx; } // Step 5a: Calculate the sun's right ascension $RA = rad2deg(atan(0.91764 * tan(deg2rad($L)))); while ($RA < 0) { $RAx = $RA + 360; assert($RAx != $RA); // askingtheguru: really needed? $RA = $RAx; } while ($RA >= 360) { $RAx = $RA - 360; assert($RAx != $RA); // askingtheguru: really needed? $RA = $RAx; } // Step 5b: Right ascension value needs to be in the same quadrant as L $Lquadrant = floor($L / 90) * 90; $RAquadrant = floor($RA / 90) * 90; $RA = $RA + ($Lquadrant - $RAquadrant); // Step 5c: Right ascension value needs to be converted into hours $RA /= 15; // Step 6: Calculate the sun's declination $sinDec = 0.39782 * sin(deg2rad($L)); $cosDec = cos(asin($sinDec)); // Step 7a: Calculate the sun's local hour angle $cosH = (cos(deg2rad($zenith)) - ($sinDec * sin(deg2rad($latitude)))) / ($cosDec * cos(deg2rad($latitude))); // XXX: What's the use of this block.. ? // if (sunrise && cosH > 1 || !sunrise && cosH < -1) { // throw doesnthappen(); // } // Step 7b: Finish calculating H and convert into hours if ($sunrise) { // Sunrise $H = 360 - rad2deg(acos($cosH)); } else { // Sunset $H = rad2deg(acos($cosH)); } $H = $H / 15; // Step 8: Calculate local mean time $T = $H + $RA - (0.06571 * $t) - 6.622; // Step 9: Convert to UTC $UT = $T - $lngHour; while ($UT < 0) { $UTx = $UT + 24; assert($UTx != $UT); // askingtheguru: really needed? $UT = $UTx; } while ($UT >= 24) { $UTx = $UT - 24; assert($UTx != $UT); // askingtheguru: really needed? $UT = $UTx; } $UT = $UT + $gmt_offset; // Now bring the result into the chosen format and return switch ($retformat) { case SUNFUNCS_RET_TIMESTAMP: return intval($date - $date % (24 * 3600) + 3600 * $UT); case SUNFUNCS_RET_STRING: $N = floor($UT); return sprintf("%02d:%02d", $N, floor(60 * ($UT - $N))); case SUNFUNCS_RET_DOUBLE: return $UT; default: return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_ERROR, __FILE__, __LINE__); } } // }}} // {{{ getWeatherIcon() /** * Gets a number corresponding to a weather icon. * * These numbers just happen to correspond with the icons that you get with * the weather.com SDK, but open versions of them have been created. Input * must be in standard units. For the icons that include day/night, we use * the present time and the provided lat/lon to determine if the sun is up. * A complete set of icon descriptions can be found here: * http://sranshaft.wincustomize.com/Articles.aspx?AID=60165&u=0 * * There are a number of icon sets here: * http://www.desktopsidebar.com/forums/index.php?showtopic=2441&st=0 * http://www.desktopsidebar.com/forums/index.php?showtopic=819 * * @param string $condition The condition. * @param array $clouds The clouds at various levels. * @param float $wind Wind speed in mph. * @param float $temperature Temperature in deg F. * @param float $latitude Point latitude. * @param float $longitude Point longitude. * @param int $reportTime The time when the weather report was generated. * @author Seth Price * @access public */ function getWeatherIcon($condition, $clouds = array(), $wind = 5, $temperature = 70, $latitude = -360, $longitude = -360, $reportTime = "") { // Search for matches that don't use the time of day $hail = (bool) stristr($condition, "hail"); $dust = (bool) stristr($condition, "dust") || (bool) stristr($condition, "sand"); $smoke = (bool) stristr($condition, "smoke") || (bool) stristr($condition, "volcanic ash"); // Slightly more complex matches that might or might not use the time of day $near = (bool) stristr($condition, "vicinity") || (bool) stristr($condition, "recent"); $light = (bool) stristr($condition, "light"); $heavy = (bool) stristr($condition, "heavy"); $ice = (bool) stristr($condition, "ice") || (bool) stristr($condition, "pellets"); // Have to add a space to prevent matching on "snow grains" $rain = (bool) stristr($condition, " rain"); $snow = (bool) stristr($condition, "snow"); $fog = (bool) stristr($condition, "fog") || (bool) stristr($condition, "spray") || (bool) stristr($condition, "mist"); $haze = (bool) stristr($condition, "haze"); $ts = (bool) stristr($condition, "thunderstorm"); $freezing = (bool) stristr($condition, "freezing"); $wind = (bool) stristr($condition, "squall") || $wind > 25; $nsw = (bool) stristr($condition, "no significant weather"); $hot = $temperature > 95; $frigid = $temperature < 5; if ($hail) { return 6; // Hail } if ($dust) { return 19; // Dust } if ($smoke) { return 22; // Smoke } // Get some of the dangerous conditions fist if ($rain && $snow && ($ice || $freezing)) { return 7; // Icy/Clouds Rain-Snow } if (($ts || $rain) && ($ice || $freezing)) { return 10; // Icy/Rain } if (($fog || $haze) && ($ice || $freezing)) { return 8; // Icy/Haze Rain } if ($rain && $snow) { return 5; // Cloudy/Snow-Rain Mix } if ($fog && $rain) { return 9; // Haze/Rain } if ($wind && $rain) { return 1; // Wind/Rain } if ($wind && $snow) { return 43; // Windy/Snow } if ($snow && $light) { return 13; // Flurries } if ($light && $rain) { return 11; // Light Rain } // Get the maximum coverage of the clouds at any height. For most // people, overcast at 1000ft is the same as overcast at 10000ft. // // 0 == clear, 1 == hazey, 2 == partly cloudy, 3 == mostly cloudy, 4 == overcast $coverage = 0; foreach ($clouds as $layer) { if ($coverage < 1 && stristr($layer["amount"], "few")) { $coverage = 1; } elseif ($coverage < 2 && stristr($layer["amount"], "scattered")) { $coverage = 2; } elseif ($coverage < 3 && (stristr($layer["amount"], "broken") || stristr($layer["amount"], "cumulus"))) { $coverage = 3; } elseif ($coverage < 4 && stristr($layer["amount"], "overcast")) { $coverage = 4; } } // Check if it is day or not. 0 is night, 2 is day, and 1 is unknown // or twilight (~(+|-)1 hour of sunrise/sunset). Note that twilight isn't // always accurate because of issues wrapping around the 24hr clock. Oh well... if ($latitude < 90 && $latitude > -90 && $longitude < 180 && $longitude > -180) { // Use provided time by report if available, otherwise use current GMT time if ($reportTime <> "" && is_numeric($reportTime)) { $timeOfDay = $reportTime; } else { $timeOfDay = gmmktime(); } // Calculate sunrise/sunset and current time in GMT $sunrise = $this->calculateSunRiseSet($timeOfDay, SUNFUNCS_RET_TIMESTAMP, $latitude, $longitude, SERVICES_WEATHER_SUNFUNCS_SUNRISE_ZENITH, 0, true); $sunset = $this->calculateSunRiseSet($timeOfDay, SUNFUNCS_RET_TIMESTAMP, $latitude, $longitude, SERVICES_WEATHER_SUNFUNCS_SUNRISE_ZENITH, 0, false); // Now that we have the sunrise/sunset times and the current time, // we need to figure out if it is day, night, or twilight. Wrapping // these times around the 24hr clock is a pain. if ($sunrise < $sunset) { if ($timeOfDay > ($sunrise + 3600) && $timeOfDay < ($sunset - 3600)) { $isDay = 2; } elseif ($timeOfDay > ($sunrise - 3600) && $timeOfDay < ($sunset + 3600)) { $isDay = 1; } else { $isDay = 0; } } else { if ($timeOfDay < ($sunrise - 3600) && $timeOfDay > ($sunset + 3600)) { $isDay = 0; } elseif ($timeOfDay < ($sunrise + 3600) && $timeOfDay > ($sunset - 3600)) { $isDay = 1; } else { $isDay = 2; } } } else { // Default to twilight because it tends to have neutral icons. $isDay = 1; } // General precipitation if ($ts && $near) { switch ($isDay) { case 0: case 1: return 38; // Lightning case 2: return 37; // Lightning/Day } } if ($ts) { switch ($isDay) { case 0: return 47; // Thunderstorm/Night case 1: case 2: return 0; // Rain/Lightning } } if ($snow) { switch ($isDay) { case 0: return 46; // Snow/Night case 1: case 2: return 41; // Snow } } if ($rain) { switch ($isDay) { case 0: return 45; // Rain/Night case 1: return 40; // Rain case 2: return 39; // Rain/Day } } // Cloud conditions near the ground if ($fog) { return 20; // Fog } if ($haze) { return 21; // Haze } // Cloud conditions if ($coverage == 4) { return 26; // Mostly Cloudy } if ($coverage == 3) { switch ($isDay) { case 0: return 27; // Mostly Cloudy/Night case 1: return 26; // Mostly Cloudy case 2: return 28; // Mostly Cloudy/Day } } if ($coverage == 2) { switch ($isDay) { case 0: return 29; // Partly Cloudy/Night case 1: return 26; // Mostly Cloudy case 2: return 30; // Partly Cloudy/Day } } if ($coverage == 1) { switch ($isDay) { case 0: case 1: return 33; // Hazy/Night case 2: return 34; // Hazy/Day } } // Catch-alls if ($wind) { return 23; // Wind } if ($hot) { return 36; // Hot! } if ($frigid) { return 25; // Frigid } if ($nsw) { switch ($isDay) { case 0: case 1: // Use night for twilight because the moon is generally // out then, so it will match with most icon sets. return 31; // Clear Night case 2: return 32; // Clear Day } } return "na"; } // }}} } // }}} ?> php-services-weather-1.4.7/Services_Weather-1.4.7/Weather/Ejse.php000066400000000000000000000441061250332203600244760ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/Services_Weather * @link http://www.ejse.com/services/weather_xml_web_services.htm * @example examples/ejse-basic.php ejse-basic.php * @filesource */ require_once "Services/Weather/Common.php"; // {{{ class Services_Weather_Ejse /** * This class acts as an interface to the soap service of EJSE. It retrieves * current weather data and forecasts based on postal codes (ZIP). * * Currently this service is only available for US territory. * * For a working example, please take a look at * docs/Services_Weather/examples/ejse-basic.php * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version Release: 1.4.7 * @link http://pear.php.net/package/Services_Weather * @link http://www.ejse.com/services/weather_xml_web_services.htm * @example examples/ejse-basic.php ejse-basic.php */ class Services_Weather_Ejse extends Services_Weather_Common { // {{{ properties /** * Username at ejse.com * * @var string $_username * @access private */ var $_username = ""; /** * Password key at ejse.com * * @var string $_password * @access private */ var $_password = ""; /** * WSDL object, provided by EJSE * * @var object $_wsdl * @access private */ var $_wsdl; /** * SOAP object to access weather data, provided by EJSE * * @var object $_weaterSoap * @access private */ var $_weatherSoap; // }}} // {{{ constructor /** * Constructor * * Requires SOAP to be installed * * @param array $options * @param mixed $error * @throws PEAR_Error * @access private */ function Services_Weather_Ejse($options, &$error) { $perror = null; $this->Services_Weather_Common($options, $perror); if (Services_Weather::isError($perror)) { $error = $perror; } } // }}} // {{{ _connectServer() /** * Connects to the SOAP server and retrieves the WSDL data * * @return PEAR_Error|bool * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @access private */ function _connectServer() { include_once "SOAP/Client.php"; $this->_wsdl = new SOAP_WSDL("http://www.ejse.com/WeatherService/Service.asmx?WSDL", $this->_httpOptions); if (isset($this->_wsdl->fault) && Services_Weather::isError($this->_wsdl->fault)) { $error = Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); return $error; } eval($this->_wsdl->generateAllProxies()); if (!class_exists("WebService_Service_ServiceSoap")) { $error = Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); return $error; } $this->_weatherSoap = &new WebService_Service_ServiceSoap; return true; } // }}} // {{{ setAccountData() /** * Sets the neccessary account-information for ejse.com, you'll * receive them after registering for the service * * @param string $username * @param string $password * @access public */ function setAccountData($username, $password) { if (strlen($username)) { $this->_username = $username; } if (strlen($password) && ctype_alnum($password)) { $this->_password = $password; } } // }}} // {{{ _checkLocationID() /** * Checks the id for valid values and thus prevents silly requests to EJSE server * * @param string $id * @return PEAR_Error|bool * @throws PEAR_Error::SERVICES_WEATHER_ERROR_NO_LOCATION * @throws PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION * @access private */ function _checkLocationID($id) { if (is_array($id) || is_object($id) || !strlen($id)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_NO_LOCATION, __FILE__, __LINE__); } elseif (!ctype_digit($id) || (strlen($id) != 5)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_INVALID_LOCATION, __FILE__, __LINE__); } return true; } // }}} // {{{ searchLocation() /** * EJSE offers no search function to date, so this function is disabled. * Maybe this is the place to interface to some online postcode service... * * @param string $location * @param bool $useFirst * @return bool * @access public * @deprecated */ function searchLocation($location = null, $useFirst = null) { return false; } // }}} // {{{ searchLocationByCountry() /** * EJSE offers no search function to date, so this function is disabled. * Maybe this is the place to interface to some online postcode service... * * @param string $country * @return bool * @access public * @deprecated */ function searchLocationByCountry($country = null) { return false; } // }}} // {{{ getLocation() /** * Returns the data for the location belonging to the ID * * @param string $id * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getLocation($id = "") { $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } $locationReturn = array(); if ($this->_cacheEnabled && ($weather = $this->_getCache($id, "weather"))) { // Get data from cache $this->_weather = $weather; $locationReturn["cache"] = "HIT"; } else { // Check, if the weatherSoap-Object is present. If not, connect to the Server and retrieve the WDSL data if (!$this->_weatherSoap) { $status = $this->_connectServer(); if (Services_Weather::isError($status)) { return $status; } } $weather = $this->_weatherSoap->getWeatherInfo2($this->_username, $this->_password, $id); if (Services_Weather::isError($weather)) { return $weather; } $this->_weather = $weather; if ($this->_cacheEnabled) { // ...and cache it $this->_saveCache($id, $this->_weather, "", "weather"); } $locationReturn["cache"] = "MISS"; } $locationReturn["name"] = $this->_weather->Location; return $locationReturn; } // }}} // {{{ getWeather() /** * Returns the weather-data for the supplied location * * @param string $id * @param string $unitsFormat * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getWeather($id = "", $unitsFormat = "") { $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } // Get other data $units = $this->getUnitsFormat($unitsFormat); $weatherReturn = array(); if ($this->_cacheEnabled && ($weather = $this->_getCache($id, "weather"))) { // Same procedure... $this->_weather = $weather; $weatherReturn["cache"] = "HIT"; } else { // Check, if the weatherSoap-Object is present. If not, connect to the Server and retrieve the WDSL data if (!$this->_weatherSoap) { $status = $this->_connectServer(); if (Services_Weather::isError($status)) { return $status; } } // ...as last function $weather = $this->_weatherSoap->getWeatherInfo2($this->_username, $this->_password, $id); if (Services_Weather::isError($weather)) { return $weather; } $this->_weather = $weather; if ($this->_cacheEnabled) { // ...and cache it $this->_saveCache($id, $this->_weather, "", "weather"); } $weatherReturn["cache"] = "MISS"; } if (!isset($compass)) { // Yes, NNE and the likes are multiples of 22.5, but as the other // services return integers for this value, these directions are // rounded up $compass = array( "north" => array("N", 0), "north northeast" => array("NNE", 23), "northeast" => array("NE", 45), "east northeast" => array("ENE", 68), "east" => array("E", 90), "east southeast" => array("ESE", 113), "southeast" => array("SE", 135), "south southeast" => array("SSE", 158), "south" => array("S", 180), "south southwest" => array("SSW", 203), "southwest" => array("SW", 225), "west southwest" => array("WSW", 248), "west" => array("W", 270), "west northwest" => array("WNW", 293), "northwest" => array("NW", 315), "north northwest" => array("NNW", 338) ); } // Initialize some arrays $update = array(); $temperature = array(); $feltTemperature = array(); $visibility = array(); $pressure = array(); $dewPoint = array(); $uvIndex = array(); $wind = array(); if (preg_match("/(\w+) (\d+), (\d+), at (\d+:\d+ \wM) [^\(]+(\(([^\)]+)\))?/", $this->_weather->LastUpdated, $update)) { if (isset($update[5])) { $timestring = $update[6]; } else { $timestring = $update[2]." ".$update[1]." ".$update[3]." ".$update[4]." EST"; } $weatherReturn["update"] = gmdate(trim($this->_dateFormat." ".$this->_timeFormat), strtotime($timestring)); } else { $weatherReturn["update"] = ""; } $weatherReturn["updateRaw"] = $this->_weather->LastUpdated; $weatherReturn["station"] = $this->_weather->ReportedAt; $weatherReturn["conditionIcon"] = $this->_weather->IconIndex; preg_match("/(-?\d+)\D+/", $this->_weather->Temprature, $temperature); $weatherReturn["temperature"] = $this->convertTemperature($temperature[1], "f", $units["temp"]); preg_match("/(-?\d+)\D+/", $this->_weather->FeelsLike, $feltTemperature); $weatherReturn["feltTemperature"] = $this->convertTemperature($feltTemperature[1], "f", $units["temp"]); $weatherReturn["condition"] = $this->_weather->Forecast; if (preg_match("/([\d\.]+)\D+/", $this->_weather->Visibility, $visibility)) { $weatherReturn["visibility"] = $this->convertDistance($visibility[1], "sm", $units["vis"]); } else { $weatherReturn["visibility"] = trim($this->_weather->Visibility); } preg_match("/([\d\.]+) inches and (\w+)/", $this->_weather->Pressure, $pressure); $weatherReturn["pressure"] = $this->convertPressure($pressure[1], "in", $units["pres"]); $weatherReturn["pressureTrend"] = $pressure[2]; preg_match("/(-?\d+)\D+/", $this->_weather->DewPoint, $dewPoint); $weatherReturn["dewPoint"] = $this->convertTemperature($dewPoint[1], "f", $units["temp"]); preg_match("/(\d+) (\w+)/", $this->_weather->UVIndex, $uvIndex); $weatherReturn["uvIndex"] = $uvIndex[1]; $weatherReturn["uvText"] = $uvIndex[2]; $weatherReturn["humidity"] = str_replace("%", "", $this->_weather->Humidity); if (preg_match("/From the ([\w\ ]+) at ([\d\.]+) (gusting to ([\d\.]+) )?mph/", $this->_weather->Wind, $wind)) { $weatherReturn["wind"] = $this->convertSpeed($wind[2], "mph", $units["wind"]); if (isset($wind[4])) { $weatherReturn["windGust"] = $this->convertSpeed($wind[4], "mph", $units["wind"]); } $weatherReturn["windDegrees"] = $compass[strtolower($wind[1])][1]; $weatherReturn["windDirection"] = $compass[strtolower($wind[1])][0]; } elseif (strtolower($this->_weather->Wind) == "calm") { $weatherReturn["wind"] = 0; $weatherReturn["windDegrees"] = 0; $weatherReturn["windDirection"] = "CALM"; } return $weatherReturn; } // }}} // {{{ getForecast() /** * Get the forecast for the next days * * @param string $int * @param int $days Values between 1 and 9 * @param string $unitsFormat * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getForecast($id = "", $days = 2, $unitsFormat = "") { $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } if (!in_array($days, range(1, 9))) { $days = 2; } // Get other data $units = $this->getUnitsFormat($unitsFormat); $forecastReturn = array(); if ($this->_cacheEnabled && ($forecast = $this->_getCache($id, "forecast"))) { // Same procedure... $this->_forecast = $forecast; $forecastReturn["cache"] = "HIT"; } else { // Check, if the weatherSoap-Object is present. If not, connect to the Server and retrieve the WDSL data if (!$this->_weatherSoap) { $status = $this->_connectServer(); if (Services_Weather::isError($status)) { return $status; } } // ...as last function $forecast = $this->_weatherSoap->GetNineDayForecastInfo2($this->_username, $this->_password, $id); if (Services_Weather::isError($forecast)) { return $forecast; } $this->_forecast = $forecast; if ($this->_cacheEnabled) { // ...and cache it $this->_saveCache($id, $this->_forecast, "", "forecast"); } $forecastReturn["cache"] = "MISS"; } $forecastReturn["days"] = array(); // Initialize some arrays $temperatureHigh = array(); $temperatureLow = array(); for ($i = 1; $i <= $days; $i++) { preg_match("/(-?\d+)\D+/", $this->_forecast->{"Day".$i}->High, $temperatureHigh); preg_match("/(-?\d+)\D+/", $this->_forecast->{"Day".$i}->Low, $temperatureLow); $day = array( "tempertureHigh" => $this->convertTemperature($temperatureHigh[1], "f", $units["temp"]), "temperatureLow" => $this->convertTemperature($temperatureLow[1], "f", $units["temp"]), "day" => array( "condition" => $this->_forecast->{"Day".$i}->Forecast, "conditionIcon" => $this->_forecast->{"Day".$i}->IconIndex, "precipitation" => trim(str_replace("%", "", $this->_forecast->{"Day".$i}->PrecipChance)) ) ); $forecastReturn["days"][] = $day; } return $forecastReturn; } // }}} } // }}} ?> php-services-weather-1.4.7/Services_Weather-1.4.7/Weather/Globalweather.php000066400000000000000000000466071250332203600264000ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/Services_Weather * @link http://www.capescience.com/webservices/globalweather/index.shtml * @example examples/globalweather-basic.php globalweather-basic.php * @filesource */ require_once "Services/Weather/Common.php"; // {{{ class Services_Weather_Globalweather /** * This class acts as an interface to the soap service of capescience.com. * It searches for given locations and retrieves current weather data. * * GlobalWeather is a SOAP frontend for METAR data, provided by CapeScience. * If you want to use METAR, you should try this class first, as it is much * more comfortable (and also a bit faster) than the native METAR-class * provided by this package. On the other hand, this service won't supply * TAFs, the forecast system accompanying METAR, so you have to make * the call here... * * For a working example, please take a look at * docs/Services_Weather/examples/globalweather-basic.php * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version Release: 1.4.7 * @link http://pear.php.net/package/Services_Weather * @link http://www.capescience.com/webservices/globalweather/index.shtml * @example examples/globalweather-basic.php globalweather-basic.php */ class Services_Weather_Globalweather extends Services_Weather_Common { // {{{ properties /** * WSDL object, provided by CapeScience * * @var object $_wsdl * @access private */ var $_wsdl; /** * SOAP object to access station data, provided by CapeScience * * @var object $_stationSoap * @access private */ var $_stationSoap; /** * SOAP object to access weather data, provided by CapeScience * * @var object $_weatherSoap * @access private */ var $_weatherSoap; // }}} // {{{ constructor /** * Constructor * * Requires SOAP to be installed * * @param array $options * @param mixed $error * @throws PEAR_Error * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @access private */ function Services_Weather_Globalweather($options, &$error) { $perror = null; $this->Services_Weather_Common($options, $perror); if (Services_Weather::isError($perror)) { $error = $perror; } } // }}} // {{{ _connectServer() /** * Connects to the SOAP server and retrieves the WSDL data * * @return PEAR_Error|bool * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @access private */ function _connectServer() { include_once "SOAP/Client.php"; $this->_wsdl = new SOAP_WSDL("http://live.capeclear.com/ccx/GlobalWeather?wsdl", $this->_httpOptions); if (isset($this->_wsdl->fault) && Services_Weather::isError($this->_wsdl->fault)) { $error = Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); return $error; } eval($this->_wsdl->generateAllProxies()); if (!class_exists("WebService_GlobalWeather_StationInfo") || !class_exists("WebService_GlobalWeather_GlobalWeather")) { $error = Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); return $error; } $this->_stationSoap = &new WebService_GlobalWeather_StationInfo; $this->_weatherSoap = &new WebService_GlobalWeather_GlobalWeather; return true; } // }}} // {{{ _checkLocationID() /** * Checks the id for valid values and thus prevents silly requests to * GlobalWeather server * * @param string $id * @return PEAR_Error|bool * @throws PEAR_Error::SERVICES_WEATHER_ERROR_NO_LOCATION * @throws PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION * @access private */ function _checkLocationID($id) { // Check, if the stationSoap-Object is present. If not, connect to the Server and retrieve the WDSL data if (!$this->_stationSoap) { $status = $this->_connectServer(); if (Services_Weather::isError($status)) { return $status; } } if (is_array($id) || is_object($id) || !strlen($id)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_NO_LOCATION, __FILE__, __LINE__); } elseif ($this->_stationSoap->isValidCode($id) === false) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_INVALID_LOCATION, __FILE__, __LINE__); } return true; } // }}} // {{{ searchLocation() /** * Searches IDs for given location, returns array of possible locations * or single ID * * @param string $location * @param bool $useFirst If set, first ID of result-array is returned * @return PEAR_Error|array|string * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION * @access public */ function searchLocation($location, $useFirst = false) { // Check, if the stationSoap-Object is present. If not, connect to the Server and retrieve the WDSL data if (!$this->_stationSoap) { $status = $this->_connectServer(); if (Services_Weather::isError($status)) { return $status; } } // Get search data from server and unserialize $search = $this->_stationSoap->searchByName($location); if (Services_Weather::isError($search)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } else { if (!is_array($search) || !sizeof($search)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION, __FILE__, __LINE__); } else { if (!$useFirst && (sizeof($search) > 1)) { $searchReturn = array(); for ($i = 0; $i < sizeof($search); $i++) { $searchReturn[$search[$i]->icao] = $search[$i]->name.", ".$search[$i]->country; } } elseif ($useFirst || (sizeof($search) == 1)) { $searchReturn = $search[0]->icao; } } } return $searchReturn; } // }}} // {{{ searchLocationByCountry() /** * Returns IDs with location-name for a given country or all available * countries, if no value was given * * @param string $country * @return PEAR_Error|array * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION * @access public */ function searchLocationByCountry($country = "") { // Check, if the stationSoap-Object is present. If not, connect to the Server and retrieve the WDSL data if (!$this->_stationSoap) { $status = $this->_connectServer(); if (Services_Weather::isError($status)) { return $status; } } // Return the available countries as no country was given if (!strlen($country)) { $countries = $this->_stationSoap->listCountries(""); if (Services_Weather::isError($countries)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } return $countries; } // Now for the real search $countryLocs = $this->_stationSoap->searchByCountry($country); // Check result for validity if (Services_Weather::isError($countryLocs)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } elseif (!is_array($countryLocs)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION, __FILE__, __LINE__); } // Construct the result $locations = array(); foreach ($countryLocs as $location) { $locations[$location->icao] = $location->name.", ".$location->country; } asort($locations); return $locations; } // }}} // {{{ getLocation() /** * Returns the data for the location belonging to the ID * * @param string $id * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getLocation($id = "") { $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } $locationReturn = array(); if ($this->_cacheEnabled && ($location = $this->_getCache("GW-".$id, "location"))) { // Get data from cache $this->_location = $location; $locationReturn["cache"] = "HIT"; } else { // Check, if the stationSoap-Object is present. If not, connect to the Server and retrieve the WDSL data if (!$this->_stationSoap) { $status = $this->_connectServer(); if (Services_Weather::isError($status)) { return $status; } } $location = $this->_stationSoap->getStation($id); if (Services_Weather::isError($location)) { return $location; } $this->_location = $location; if ($this->_cacheEnabled) { // ...and cache it $this->_saveCache("GW-".$id, $this->_location, "", "location"); } $locationReturn["cache"] = "MISS"; } if (strlen($this->_location->region) && strlen($this->_location->country)) { $locname = $this->_location->name.", ".$this->_location->region.", ".$this->_location->country; } elseif (strlen($this->_location->country)) { $locname = $this->_location->name.", ".$this->_location->country; } else { $locname = $this->_location->name; } $locationReturn["name"] = $locname; $locationReturn["latitude"] = $this->_location->latitude; $locationReturn["longitude"] = $this->_location->longitude; $locationReturn["sunrise"] = date($this->_timeFormat, $this->calculateSunRiseSet(gmmktime(), SUNFUNCS_RET_TIMESTAMP, $this->_location->latitude, $this->_location->longitude, SERVICES_WEATHER_SUNFUNCS_SUNRISE_ZENITH, 0, true)); $locationReturn["sunset"] = date($this->_timeFormat, $this->calculateSunRiseSet(gmmktime(), SUNFUNCS_RET_TIMESTAMP, $this->_location->latitude, $this->_location->longitude, SERVICES_WEATHER_SUNFUNCS_SUNSET_ZENITH, 0, false)); $locationReturn["elevation"] = $this->_location->elevation; return $locationReturn; } // }}} // {{{ getWeather() /** * Returns the weather-data for the supplied location * * @param string $id * @param string $unitsFormat * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getWeather($id = "", $unitsFormat = "") { static $clouds; if (!isset($clouds)) { $clouds = array( "sky clear", "few", "scattered", "broken", "overcast", ); } $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } // Get other data $units = $this->getUnitsFormat($unitsFormat); $weatherReturn = array(); if ($this->_cacheEnabled && ($weather = $this->_getCache("GW-".$id, "weather"))) { // Same procedure... $this->_weather = $weather; $weatherReturn["cache"] = "HIT"; } else { // Check, if the weatherSoap-Object is present. If not, connect to the Server and retrieve the WDSL data if (!$this->_weatherSoap) { $status = $this->_connectServer(); if (Services_Weather::isError($status)) { return $status; } } // ...as last function $weather = $this->_weatherSoap->getWeatherReport($id); if (Services_Weather::isError($weather)) { return $weather; } $this->_weather = $weather; if ($this->_cacheEnabled) { // ...and cache it $this->_saveCache("GW-".$id, $this->_weather, "", "weather"); } $weatherReturn["cache"] = "MISS"; } $update = trim(str_replace(array("T", "Z"), " ", $this->_weather->timestamp))." GMT"; $weatherReturn["update"] = gmdate(trim($this->_dateFormat." ".$this->_timeFormat), strtotime($update)); $weatherReturn["updateRaw"] = $this->_weather->timestamp; if (strlen($this->_weather->station->region) && strlen($this->_weather->station->country)) { $locname = $this->_weather->station->name.", ".$this->_weather->station->region.", ".$this->_weather->station->country; } elseif (strlen($this->_weather->station->country)) { $locname = $this->_weather->station->name.", ".$this->_weather->station->country; } else { $locname = $this->_weather->station->name; } $weatherReturn["station"] = $locname; $weatherReturn["wind"] = $this->convertSpeed($this->_weather->wind->prevailing_speed, "mps", $units["wind"]); $weatherReturn["windDegrees"] = $this->_weather->wind->prevailing_direction->degrees; $weatherReturn["windDirection"] = $this->_weather->wind->prevailing_direction->compass; if ($this->_weather->wind->prevailing_speed != $this->_weather->wind->gust_speed) { $weatherReturn["windGust"] = $this->convertSpeed($this->_weather->wind->gust_speed, "mps", $units["wind"]); } if ($this->_weather->wind->varying_from_direction != "" && $this->_weather->wind->varying_to_direction != "") { $weatherReturn["windVar"] = array ( "from" => $this->_weather->wind->varying_from_direction, "to" => $this->_weather->wind->varying_to_direction ); } $weatherReturn["visibility"] = $this->convertDistance($this->_weather->visibility->distance / 1000, "km", $units["vis"]); $weatherReturn["visQualifier"] = $this->_weather->visibility->qualifier; $condition = array(); for ($i = 0; $i < sizeof($this->_weather->phenomena); $i++) { $condition[] = $this->_weather->phenomena[$i]->string; } $weatherReturn["condition"] = implode(", ", $condition); if (is_array($this->_weather->sky->layers)) { $layers = array(); for ($i = 0; $i < sizeof($this->_weather->sky->layers); $i++) { if (strtoupper($this->_weather->sky->layers[$i]->type) != "CLEAR") { $layers[$i] = array(); $layers[$i]["amount"] = $clouds[$this->_weather->sky->layers[$i]->extent]; $layers[$i]["height"] = $this->convertDistance($this->_weather->sky->layers[$i]->altitude, "m", $units["height"]); if (strtoupper($this->_weather->sky->layers[$i]->type) != "CLOUD") { $layers[$i]["type"] = ucwords(str_replace("_", "", $this->_weather->sky->layers[$i]->type)); } } } if (sizeof($layers)) { $weatherReturn["clouds"] = $layers; } } $weatherReturn["temperature"] = $this->convertTemperature($this->_weather->temperature->ambient, "c", $units["temp"]); $feltTemperature = $this->calculateWindChill($this->convertTemperature($weatherReturn["temperature"], $units["temp"], "f"), $this->convertSpeed($weatherReturn["wind"], $units["wind"], "mph")); $weatherReturn["feltTemperature"] = $this->convertTemperature($feltTemperature, "f", $units["temp"]); $weatherReturn["dewPoint"] = $this->convertTemperature($this->_weather->temperature->dewpoint, "c", $units["temp"]); $weatherReturn["humidity"] = $this->_weather->temperature->relative_humidity; $weatherReturn["pressure"] = $this->convertPressure($this->_weather->pressure->altimeter, "hpa", $units["pres"]); return $weatherReturn; } // }}} // {{{ getForecast() /** * GlobalWeather has no forecast per se, so this function is just for * compatibility purposes. * * @param string $int * @param int $days * @param string $unitsFormat * @return bool * @access public * @deprecated */ function getForecast($id = null, $days = null, $unitsFormat = null) { return false; } // }}} } // }}} ?> php-services-weather-1.4.7/Services_Weather-1.4.7/Weather/Metar.php000066400000000000000000002646011250332203600246640ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/Services_Weather * @link http://weather.noaa.gov/weather/metar.shtml * @link http://weather.noaa.gov/weather/taf.shtml * @example examples/metar-basic.php metar-basic.php * @example examples/metar-extensive.php metar-extensive.php * @filesource */ require_once "Services/Weather/Common.php"; require_once "DB.php"; // {{{ class Services_Weather_Metar /** * This class acts as an interface to the METAR/TAF service of * weather.noaa.gov. It searches for locations given in ICAO notation and * retrieves the current weather data. * * Of course the parsing of the METAR-data has its limitations, as it * follows the Federal Meteorological Handbook No.1 with modifications to * accomodate for non-US reports, so if the report deviates from these * standards, you won't get it parsed correctly. * Anything that is not parsed, is saved in the "noparse" array-entry, * returned by getWeather(), so you can do your own parsing afterwards. This * limitation is specifically given for remarks, as the class is not * processing everything mentioned there, but you will get the most common * fields like precipitation and temperature-changes. Again, everything not * parsed, goes into "noparse". * * If you think, some important field is missing or not correctly parsed, * please file a feature-request/bugreport at http://pear.php.net/ and be * sure to provide the METAR (or TAF) report with a _detailed_ explanation! * * For working examples, please take a look at * docs/Services_Weather/examples/metar-basic.php * docs/Services_Weather/examples/metar-extensive.php * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version Release: 1.4.7 * @link http://pear.php.net/package/Services_Weather * @link http://weather.noaa.gov/weather/metar.shtml * @link http://weather.noaa.gov/weather/taf.shtml * @example examples/metar-basic.php metar-basic.php * @example examples/metar-extensive.php metar-extensive.php */ class Services_Weather_Metar extends Services_Weather_Common { // {{{ properties /** * Information to access the location DB * * @var object DB $_db * @access private */ var $_db; /** * The source METAR uses * * @var string $_sourceMetar * @access private */ var $_sourceMetar; /** * The source TAF uses * * @var string $_sourceTaf * @access private */ var $_sourceTaf; /** * This path is used to find the METAR data * * @var string $_sourcePathMetar * @access private */ var $_sourcePathMetar; /** * This path is used to find the TAF data * * @var string $_sourcePathTaf * @access private */ var $_sourcePathTaf; // }}} // {{{ constructor /** * Constructor * * @param array $options * @param mixed $error * @throws PEAR_Error * @access private */ function Services_Weather_Metar($options, &$error) { $perror = null; $this->Services_Weather_Common($options, $perror); if (Services_Weather::isError($perror)) { $error = $perror; return; } // Set options accordingly $status = null; if (isset($options["dsn"])) { if (isset($options["dbOptions"])) { $status = $this->setMetarDB($options["dsn"], $options["dbOptions"]); } else { $status = $this->setMetarDB($options["dsn"]); } } if (Services_Weather::isError($status)) { $error = $status; return; } // Setting the data sources for METAR and TAF - have to watch out for older API usage if (($source = isset($options["source"])) || isset($options["sourceMetar"])) { $sourceMetar = $source ? $options["source"] : $options["sourceMetar"]; if (($sourcePath = isset($options["sourcePath"])) || isset($options["sourcePathMetar"])) { $sourcePathMetar = $sourcePath ? $options["sourcePath"] : $options["sourcePathMetar"]; } else { $sourcePathMetar = ""; } } else { $sourceMetar = "http"; $sourcePathMetar = ""; } if (isset($options["sourceTaf"])) { $sourceTaf = $options["sourceTaf"]; if (isset($option["sourcePathTaf"])) { $sourcePathTaf = $options["sourcePathTaf"]; } else { $soucePathTaf = ""; } } else { $sourceTaf = "http"; $sourcePathTaf = ""; } $status = $this->setMetarSource($sourceMetar, $sourcePathMetar, $sourceTaf, $sourcePathTaf); if (Services_Weather::isError($status)) { $error = $status; return; } } // }}} // {{{ setMetarDB() /** * Sets the parameters needed for connecting to the DB, where the * location-search is fetching its data from. You need to build a DB * with the external tool buildMetarDB first, it fetches the locations * and airports from a NOAA-website. * * @param string $dsn * @param array $dbOptions * @return DB_Error|bool * @throws DB_Error * @see DB::parseDSN * @access public */ function setMetarDB($dsn, $dbOptions = array()) { $dsninfo = DB::parseDSN($dsn); if (is_array($dsninfo) && !isset($dsninfo["mode"])) { $dsninfo["mode"]= 0644; } // Initialize connection to DB and store in object if successful $db = DB::connect($dsninfo, $dbOptions); if (DB::isError($db)) { return $db; } $this->_db = $db; return true; } // }}} // {{{ setMetarSource() /** * Sets the source, where the class tries to locate the METAR/TAF data * * Source can be http, ftp or file. * Alternate sourcepaths can be provided. * * @param string $sourceMetar * @param string $sourcePathMetar * @param string $sourceTaf * @param string $sourcePathTaf * @return PEAR_ERROR|bool * @throws PEAR_Error::SERVICES_WEATHER_ERROR_METAR_SOURCE_INVALID * @access public */ function setMetarSource($sourceMetar, $sourcePathMetar = "", $sourceTaf = "", $sourcePathTaf = "") { if (in_array($sourceMetar, array("http", "ftp", "file"))) { $this->_sourceMetar = $sourceMetar; } else { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_METAR_SOURCE_INVALID, __FILE__, __LINE__); } // Check for a proper METAR source if parameter is set, if not set use defaults clearstatcache(); if (strlen($sourcePathMetar)) { if (($this->_sourceMetar == "file" && is_dir($sourcePathMetar)) || ($this->_sourceMetar != "file" && parse_url($sourcePathMetar))) { $this->_sourcePathMetar = $sourcePathMetar; } else { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_METAR_SOURCE_INVALID, __FILE__, __LINE__); } } else { switch ($sourceMetar) { case "http": $this->_sourcePathMetar = "http://weather.noaa.gov/pub/data/observations/metar/stations"; break; case "ftp": $this->_sourcePathMetar = "ftp://weather.noaa.gov/data/observations/metar/stations"; break; case "file": $this->_sourcePathMetar = "."; break; } } if (in_array($sourceTaf, array("http", "ftp", "file"))) { $this->_sourceTaf = $sourceTaf; } elseif ($sourceTaf != "") { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_METAR_SOURCE_INVALID, __FILE__, __LINE__); } // Check for a proper TAF source if parameter is set, if not set use defaults clearstatcache(); if (strlen($sourcePathTaf)) { if (($this->_sourceTaf == "file" && is_dir($sourcePathTaf)) || ($this->_sourceTaf != "file" && parse_url($sourcePathTaf))) { $this->_sourcePathTaf = $sourcePathTaf; } else { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_METAR_SOURCE_INVALID, __FILE__, __LINE__); } } else { switch ($sourceTaf) { case "http": $this->_sourcePathTaf = "http://weather.noaa.gov/pub/data/forecasts/taf/stations"; break; case "ftp": $this->_sourcePathTaf = "ftp://weather.noaa.gov/data/forecasts/taf/stations"; break; case "file": $this->_sourcePathTaf = "."; break; } } return true; } // }}} // {{{ _checkLocationID() /** * Checks the id for valid values and thus prevents silly requests to * METAR server * * @param string $id * @return PEAR_Error|bool * @throws PEAR_Error::SERVICES_WEATHER_ERROR_NO_LOCATION * @throws PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION * @access private */ function _checkLocationID($id) { if (is_array($id) || is_object($id) || !strlen($id)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_NO_LOCATION, __FILE__, __LINE__); } elseif (!ctype_alnum($id) || (strlen($id) > 4)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_INVALID_LOCATION, __FILE__, __LINE__); } return true; } // }}} /** * Downloads the weather- or forecast-data for an id from the server dependant on the datatype and returns it * * @param string $id * @param string $dataType * @return PEAR_Error|array * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @access private */ // {{{ _retrieveServerData() function _retrieveServerData($id, $dataType) { switch($this->{"_source".ucfirst($dataType)}) { case "file": // File source is used, get file and read as-is into a string $source = realpath($this->{"_sourcePath".ucfirst($dataType)}."/".$id.".TXT"); $data = @file_get_contents($source); if ($data === false) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } break; case "http": // HTTP used, acquire request object and fetch data from webserver. Return body of reply include_once "HTTP/Request.php"; $request = &new HTTP_Request($this->{"_sourcePath".ucfirst($dataType)}."/".$id.".TXT", $this->_httpOptions); $status = $request->sendRequest(); if (Services_Weather::isError($status) || (int) $request->getResponseCode() <> 200) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } $data = $request->getResponseBody(); break; case "ftp": // FTP as source, acquire neccessary object first include_once "Net/FTP.php"; // Parse source to get the server data $server = parse_url($this->{"_sourcePath".ucfirst($dataType)}."/".$id.".TXT"); // If neccessary options are not set, use defaults if (!isset($server["port"]) || $server["port"] == "" || $server["port"] == 0) { $server["port"] = 21; } if (!isset($server["user"]) || $server["user"] == "") { $server["user"] = "ftp"; } if (!isset($server["pass"]) || $server["pass"] == "") { $server["pass"] = "ftp@"; } // Instantiate object and connect to server $ftp = &new Net_FTP($server["host"], $server["port"], $this->_httpOptions["timeout"]); $status = $ftp->connect(); if (Services_Weather::isError($status)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } // Login to server... $status = $ftp->login($server["user"], $server["pass"]); if (Services_Weather::isError($status)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } // ...and retrieve the data into a temporary file $tempfile = tempnam("./", "Services_Weather_Metar"); $status = $ftp->get($server["path"], $tempfile, true, FTP_ASCII); if (Services_Weather::isError($status)) { unlink($tempfile); return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } // Disconnect FTP server, and read data from temporary file $ftp->disconnect(); $data = @file_get_contents($tempfile); unlink($tempfile); break; } // Split data into an array and return return preg_split("/\n|\r\n|\n\r/", $data); } // }}} // {{{ _parseWeatherData() /** * Parses the data and caches it * * METAR KPIT 091955Z COR 22015G25KT 3/4SM R28L/2600FT TSRA OVC010CB * 18/16 A2992 RMK SLP045 T01820159 * * @param array $data * @return PEAR_Error|array * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION * @access private */ function _parseWeatherData($data) { static $compass; static $clouds; static $cloudtypes; static $conditions; static $sensors; if (!isset($compass)) { $compass = array( "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW" ); $clouds = array( "skc" => "sky clear", "nsc" => "no significant cloud", "few" => "few", "sct" => "scattered", "bkn" => "broken", "ovc" => "overcast", "vv" => "vertical visibility", "tcu" => "Towering Cumulus", "cb" => "Cumulonimbus", "clr" => "clear below 12,000 ft" ); $cloudtypes = array( "low" => array( "/" => "Overcast", "0" => "None", "1" => "Cumulus (fair weather)", "2" => "Cumulus (towering)", "3" => "Cumulonimbus (no anvil)", "4" => "Stratocumulus (from Cumulus)", "5" => "Stratocumulus (not Cumulus)", "6" => "Stratus or Fractostratus (fair)", "7" => "Fractocumulus/Fractostratus (bad weather)", "8" => "Cumulus and Stratocumulus", "9" => "Cumulonimbus (thunderstorm)" ), "middle" => array( "/" => "Overcast", "0" => "None", "1" => "Altostratus (thin)", "2" => "Altostratus (thick)", "3" => "Altocumulus (thin)", "4" => "Altocumulus (patchy)", "5" => "Altocumulus (thickening)", "6" => "Altocumulus (from Cumulus)", "7" => "Altocumulus (w/ Altocumulus, Altostratus, Nimbostratus)", "8" => "Altocumulus (w/ turrets)", "9" => "Altocumulus (chaotic)" ), "high" => array( "/" => "Overcast", "0" => "None", "1" => "Cirrus (filaments)", "2" => "Cirrus (dense)", "3" => "Cirrus (often w/ Cumulonimbus)", "4" => "Cirrus (thickening)", "5" => "Cirrus/Cirrostratus (low in sky)", "6" => "Cirrus/Cirrostratus (high in sky)", "7" => "Cirrostratus (entire sky)", "8" => "Cirrostratus (partial)", "9" => "Cirrocumulus or Cirrocumulus/Cirrus/Cirrostratus" ) ); $conditions = array( "+" => "heavy", "-" => "light", "vc" => "vicinity", "re" => "recent", "nsw" => "no significant weather", "mi" => "shallow", "bc" => "patches", "pr" => "partial", "ts" => "thunderstorm", "bl" => "blowing", "sh" => "showers", "dr" => "low drifting", "fz" => "freezing", "dz" => "drizzle", "ra" => "rain", "sn" => "snow", "sg" => "snow grains", "ic" => "ice crystals", "pe" => "ice pellets", "pl" => "ice pellets", "gr" => "hail", "gs" => "small hail/snow pellets", "up" => "unknown precipitation", "br" => "mist", "fg" => "fog", "fu" => "smoke", "va" => "volcanic ash", "sa" => "sand", "hz" => "haze", "py" => "spray", "du" => "widespread dust", "sq" => "squall", "ss" => "sandstorm", "ds" => "duststorm", "po" => "well developed dust/sand whirls", "fc" => "funnel cloud", "+fc" => "tornado/waterspout" ); $sensors = array( "rvrno" => "Runway Visual Range Detector offline", "pwino" => "Present Weather Identifier offline", "pno" => "Tipping Bucket Rain Gauge offline", "fzrano" => "Freezing Rain Sensor offline", "tsno" => "Lightning Detection System offline", "visno" => "2nd Visibility Sensor offline", "chino" => "2nd Ceiling Height Indicator offline" ); } $metarCode = array( "report" => "METAR|SPECI", "station" => "\w{4}", "update" => "(\d{2})?(\d{4})Z", "type" => "AUTO|COR", "wind" => "(\d{3}|VAR|VRB)(\d{2,3})(G(\d{2,3}))?(FPS|KPH|KT|KTS|MPH|MPS)", "windVar" => "(\d{3})V(\d{3})", "visFrac" => "(\d{1})", "visibility" => "(\d{4})|((M|P)?((\d{1,2}|((\d) )?(\d)\/(\d))(SM|KM)))|(CAVOK)", "runway" => "R(\d{2})(\w)?\/(P|M)?(\d{4})(FT)?(V(P|M)?(\d{4})(FT)?)?(\w)?", "condition" => "(-|\+|VC|RE|NSW)?(MI|BC|PR|TS|BL|SH|DR|FZ)?((DZ)|(RA)|(SN)|(SG)|(IC)|(PE)|(PL)|(GR)|(GS)|(UP))*(BR|FG|FU|VA|DU|SA|HZ|PY)?(PO|SQ|FC|SS|DS)?", "clouds" => "(SKC|CLR|NSC|((FEW|SCT|BKN|OVC|VV)(\d{3}|\/{3})(TCU|CB)?))", "temperature" => "(M)?(\d{2})\/((M)?(\d{2})|XX|\/\/)?", "pressure" => "(A)(\d{4})|(Q)(\d{4})", "trend" => "NOSIG|TEMPO|BECMG", "remark" => "RMK" ); $remarks = array( "nospeci" => "NOSPECI", "autostation" => "AO(1|2)", "presschg" => "PRES(R|F)R", "seapressure" => "SLP(\d{3}|NO)", "precip" => "(P|6|7)(\d{4}|\/{4})", "snowdepth" => "4\/(\d{3})", "snowequiv" => "933(\d{3})", "cloudtypes" => "8\/(\d|\/)(\d|\/)(\d|\/)", "sunduration" => "98(\d{3})", "1htempdew" => "T(0|1)(\d{3})((0|1)(\d{3}))?", "6hmaxtemp" => "1(0|1)(\d{3})", "6hmintemp" => "2(0|1)(\d{3})", "24htemp" => "4(0|1)(\d{3})(0|1)(\d{3})", "3hpresstend" => "5([0-8])(\d{3})", "sensors" => "RVRNO|PWINO|PNO|FZRANO|TSNO|VISNO|CHINO", "maintain" => "[\$]" ); if (SERVICES_WEATHER_DEBUG) { for ($i = 0; $i < sizeof($data); $i++) { echo $data[$i]."\n"; } } // Eliminate trailing information for ($i = 0; $i < sizeof($data); $i++) { if (strpos($data[$i], "=") !== false) { $data[$i] = substr($data[$i], 0, strpos($data[$i], "=")); $data = array_slice($data, 0, $i + 1); break; } } // Start with parsing the first line for the last update $weatherData = array(); $weatherData["station"] = ""; $weatherData["dataRaw"] = implode(" ", $data); $weatherData["update"] = strtotime(trim($data[0])." GMT"); $weatherData["updateRaw"] = trim($data[0]); // and prepare the rest for stepping through array_shift($data); $metar = explode(" ", preg_replace("/\s{2,}/", " ", implode(" ", $data))); // Add a few local variables for data processing $trendCount = 0; // If we have trends, we need this $pointer =& $weatherData; // Pointer to the array we add the data to for ($i = 0; $i < sizeof($metar); $i++) { // Check for whitespace and step loop, if nothing's there $metar[$i] = trim($metar[$i]); if (!strlen($metar[$i])) { continue; } if (SERVICES_WEATHER_DEBUG) { $tab = str_repeat("\t", 3 - floor((strlen($metar[$i]) + 2) / 8)); echo "\"".$metar[$i]."\"".$tab."-> "; } // Initialize some arrays $result = array(); $resultVF = array(); $lresult = array(); $found = false; foreach ($metarCode as $key => $regexp) { // Check if current code matches current metar snippet if (($found = preg_match("/^".$regexp."$/i", $metar[$i], $result)) == true) { switch ($key) { case "station": $pointer["station"] = $result[0]; unset($metarCode["station"]); break; case "wind": // Parse wind data, first the speed, convert from kt to chosen unit if ($result[5] == "KTS") { $result[5] = "KT"; } $pointer["wind"] = $this->convertSpeed($result[2], $result[5], "mph"); if ($result[1] == "VAR" || $result[1] == "VRB") { // Variable winds $pointer["windDegrees"] = "Variable"; $pointer["windDirection"] = "Variable"; } else { // Save wind degree and calc direction $pointer["windDegrees"] = intval($result[1]); $pointer["windDirection"] = $compass[round($result[1] / 22.5) % 16]; } if (is_numeric($result[4])) { // Wind with gusts... $pointer["windGust"] = $this->convertSpeed($result[4], $result[5], "mph"); } break; case "windVar": // Once more wind, now variability around the current wind-direction $pointer["windVariability"] = array("from" => intval($result[1]), "to" => intval($result[2])); break; case "visFrac": // Possible fractional visibility here. Check if it matches with the next METAR piece for visibility if (!isset($metar[$i + 1]) || !preg_match("/^".$metarCode["visibility"]."$/i", $result[1]." ".$metar[$i + 1], $resultVF)) { // No next METAR piece available or not matching. Match against next METAR code $found = false; break; } else { // Match. Hand over result and advance METAR if (SERVICES_WEATHER_DEBUG) { echo $key."\n"; echo "\"".$result[1]." ".$metar[$i + 1]."\"".str_repeat("\t", 2 - floor((strlen($result[1]." ".$metar[$i + 1]) + 2) / 8))."-> "; } $key = "visibility"; $result = $resultVF; $i++; } case "visibility": $pointer["visQualifier"] = "AT"; if (is_numeric($result[1]) && ($result[1] == 9999)) { // Upper limit of visibility range $visibility = $this->convertDistance(10, "km", "sm"); $pointer["visQualifier"] = "BEYOND"; } elseif (is_numeric($result[1])) { // 4-digit visibility in m $visibility = $this->convertDistance(($result[1]/1000), "km", "sm"); } elseif (!isset($result[11]) || $result[11] != "CAVOK") { if ($result[3] == "M") { $pointer["visQualifier"] = "BELOW"; } elseif ($result[3] == "P") { $pointer["visQualifier"] = "BEYOND"; } if (is_numeric($result[5])) { // visibility as one/two-digit number $visibility = $this->convertDistance($result[5], $result[10], "sm"); } else { // the y/z part, add if we had a x part (see visibility1) if (is_numeric($result[7])) { $visibility = $this->convertDistance($result[7] + $result[8] / $result[9], $result[10], "sm"); } else { $visibility = $this->convertDistance($result[8] / $result[9], $result[10], "sm"); } } } else { $pointer["visQualifier"] = "BEYOND"; $visibility = $this->convertDistance(10, "km", "sm"); $pointer["clouds"] = array(array("amount" => "Clear below", "height" => 5000)); $pointer["condition"] = "no significant weather"; } $pointer["visibility"] = $visibility; break; case "condition": // First some basic setups if (!isset($pointer["condition"])) { $pointer["condition"] = ""; } elseif (strlen($pointer["condition"]) > 0) { $pointer["condition"] .= ","; } if (in_array(strtolower($result[0]), $conditions)) { // First try matching the complete string $pointer["condition"] .= " ".$conditions[strtolower($result[0])]; } else { // No luck, match part by part array_shift($result); $result = array_unique($result); foreach ($result as $condition) { if (strlen($condition) > 0) { $pointer["condition"] .= " ".$conditions[strtolower($condition)]; } } } $pointer["condition"] = trim($pointer["condition"]); break; case "clouds": if (!isset($pointer["clouds"])) { $pointer["clouds"] = array(); } if (sizeof($result) == 5) { // Only amount and height $cloud = array("amount" => $clouds[strtolower($result[3])]); if ($result[4] == "///") { $cloud["height"] = "station level or below"; } else { $cloud["height"] = $result[4] * 100; } } elseif (sizeof($result) == 6) { // Amount, height and type $cloud = array("amount" => $clouds[strtolower($result[3])], "type" => $clouds[strtolower($result[5])]); if ($result[4] == "///") { $cloud["height"] = "station level or below"; } else { $cloud["height"] = $result[4] * 100; } } else { // SKC or CLR or NSC $cloud = array("amount" => $clouds[strtolower($result[0])]); } $pointer["clouds"][] = $cloud; break; case "temperature": // normal temperature in first part // negative value if ($result[1] == "M") { $result[2] *= -1; } $pointer["temperature"] = $this->convertTemperature($result[2], "c", "f"); if (sizeof($result) > 4) { // same for dewpoint if ($result[4] == "M") { $result[5] *= -1; } $pointer["dewPoint"] = $this->convertTemperature($result[5], "c", "f"); $pointer["humidity"] = $this->calculateHumidity($result[2], $result[5]); } if (isset($pointer["wind"])) { // Now calculate windchill from temperature and windspeed $pointer["feltTemperature"] = $this->calculateWindChill($pointer["temperature"], $pointer["wind"]); } break; case "pressure": if ($result[1] == "A") { // Pressure provided in inches $pointer["pressure"] = $result[2] / 100; } elseif ($result[3] == "Q") { // ... in hectopascal $pointer["pressure"] = $this->convertPressure($result[4], "hpa", "in"); } break; case "trend": // We may have a trend here... extract type and set pointer on // created new array if (!isset($weatherData["trend"])) { $weatherData["trend"] = array(); $weatherData["trend"][$trendCount] = array(); } $pointer =& $weatherData["trend"][$trendCount]; $trendCount++; $pointer["type"] = $result[0]; while (isset($metar[$i + 1]) && preg_match("/^(FM|TL|AT)(\d{2})(\d{2})$/i", $metar[$i + 1], $lresult)) { if ($lresult[1] == "FM") { $pointer["from"] = $lresult[2].":".$lresult[3]; } elseif ($lresult[1] == "TL") { $pointer["to"] = $lresult[2].":".$lresult[3]; } else { $pointer["at"] = $lresult[2].":".$lresult[3]; } // As we have just extracted the time for this trend // from our METAR, increase field-counter $i++; } break; case "remark": // Remark part begins $metarCode = $remarks; $weatherData["remark"] = array(); break; case "autostation": // Which autostation do we have here? if ($result[1] == 0) { $weatherData["remark"]["autostation"] = "Automatic weatherstation w/o precipitation discriminator"; } else { $weatherData["remark"]["autostation"] = "Automatic weatherstation w/ precipitation discriminator"; } unset($metarCode["autostation"]); break; case "presschg": // Decoding for rapid pressure changes if (strtolower($result[1]) == "r") { $weatherData["remark"]["presschg"] = "Pressure rising rapidly"; } else { $weatherData["remark"]["presschg"] = "Pressure falling rapidly"; } unset($metarCode["presschg"]); break; case "seapressure": // Pressure at sea level (delivered in hpa) // Decoding is a bit obscure as 982 gets 998.2 // whereas 113 becomes 1113 -> no real rule here if (strtolower($result[1]) != "no") { if ($result[1] > 500) { $press = 900 + round($result[1] / 100, 1); } else { $press = 1000 + $result[1]; } $weatherData["remark"]["seapressure"] = $this->convertPressure($press, "hpa", "in"); } unset($metarCode["seapressure"]); break; case "precip": // Precipitation in inches static $hours; if (!isset($weatherData["precipitation"])) { $weatherData["precipitation"] = array(); $hours = array("P" => "1", "6" => "3/6", "7" => "24"); } if (!is_numeric($result[2])) { $precip = "indeterminable"; } elseif ($result[2] == "0000") { $precip = "traceable"; } else { $precip = $result[2] / 100; } $weatherData["precipitation"][] = array( "amount" => $precip, "hours" => $hours[$result[1]] ); break; case "snowdepth": // Snow depth in inches $weatherData["remark"]["snowdepth"] = $result[1]; unset($metarCode["snowdepth"]); break; case "snowequiv": // Same for equivalent in Water... (inches) $weatherData["remark"]["snowequiv"] = $result[1] / 10; unset($metarCode["snowequiv"]); break; case "cloudtypes": // Cloud types $weatherData["remark"]["cloudtypes"] = array( "low" => $cloudtypes["low"][$result[1]], "middle" => $cloudtypes["middle"][$result[2]], "high" => $cloudtypes["high"][$result[3]] ); unset($metarCode["cloudtypes"]); break; case "sunduration": // Duration of sunshine (in minutes) $weatherData["remark"]["sunduration"] = "Total minutes of sunshine: ".$result[1]; unset($metarCode["sunduration"]); break; case "1htempdew": // Temperatures in the last hour in C if ($result[1] == "1") { $result[2] *= -1; } $weatherData["remark"]["1htemp"] = $this->convertTemperature($result[2] / 10, "c", "f"); if (sizeof($result) > 3) { // same for dewpoint if ($result[4] == "1") { $result[5] *= -1; } $weatherData["remark"]["1hdew"] = $this->convertTemperature($result[5] / 10, "c", "f"); } unset($metarCode["1htempdew"]); break; case "6hmaxtemp": // Max temperature in the last 6 hours in C if ($result[1] == "1") { $result[2] *= -1; } $weatherData["remark"]["6hmaxtemp"] = $this->convertTemperature($result[2] / 10, "c", "f"); unset($metarCode["6hmaxtemp"]); break; case "6hmintemp": // Min temperature in the last 6 hours in C if ($result[1] == "1") { $result[2] *= -1; } $weatherData["remark"]["6hmintemp"] = $this->convertTemperature($result[2] / 10, "c", "f"); unset($metarCode["6hmintemp"]); break; case "24htemp": // Max/Min temperatures in the last 24 hours in C if ($result[1] == "1") { $result[2] *= -1; } $weatherData["remark"]["24hmaxtemp"] = $this->convertTemperature($result[2] / 10, "c", "f"); if ($result[3] == "1") { $result[4] *= -1; } $weatherData["remark"]["24hmintemp"] = $this->convertTemperature($result[4] / 10, "c", "f"); unset($metarCode["24htemp"]); break; case "3hpresstend": // Pressure tendency of the last 3 hours // no special processing, just passing the data $weatherData["remark"]["3hpresstend"] = array( "presscode" => $result[1], "presschng" => $this->convertPressure($result[2] / 10, "hpa", "in") ); unset($metarCode["3hpresstend"]); break; case "nospeci": // No change during the last hour $weatherData["remark"]["nospeci"] = "No changes in weather conditions"; unset($metarCode["nospeci"]); break; case "sensors": // We may have multiple broken sensors, so do not unset if (!isset($weatherData["remark"]["sensors"])) { $weatherData["remark"]["sensors"] = array(); } $weatherData["remark"]["sensors"][strtolower($result[0])] = $sensors[strtolower($result[0])]; break; case "maintain": $weatherData["remark"]["maintain"] = "Maintainance needed"; unset($metarCode["maintain"]); break; default: // Do nothing, just prevent further matching unset($metarCode[$key]); break; } if ($found && !SERVICES_WEATHER_DEBUG) { break; } elseif ($found && SERVICES_WEATHER_DEBUG) { echo $key."\n"; break; } } } if (!$found) { if (SERVICES_WEATHER_DEBUG) { echo "n/a\n"; } if (!isset($weatherData["noparse"])) { $weatherData["noparse"] = array(); } $weatherData["noparse"][] = $metar[$i]; } } if (isset($weatherData["noparse"])) { $weatherData["noparse"] = implode(" ", $weatherData["noparse"]); } return $weatherData; } // }}} // {{{ _parseForecastData() /** * Parses the data and caches it * * TAF KLGA 271734Z 271818 11007KT P6SM -RA SCT020 BKN200 * FM2300 14007KT P6SM SCT030 BKN150 * FM0400 VRB03KT P6SM SCT035 OVC080 PROB30 0509 P6SM -RA BKN035 * FM0900 VRB03KT 6SM -RA BR SCT015 OVC035 * TEMPO 1215 5SM -RA BR SCT009 BKN015 * BECMG 1517 16007KT P6SM NSW SCT015 BKN070 * * @param array $data * @return PEAR_Error|array * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION * @access private */ function _parseForecastData($data) { static $compass; static $clouds; static $conditions; static $sensors; if (!isset($compass)) { $compass = array( "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW" ); $clouds = array( "skc" => "sky clear", "nsc" => "no significant cloud", "few" => "few", "sct" => "scattered", "bkn" => "broken", "ovc" => "overcast", "vv" => "vertical visibility", "tcu" => "Towering Cumulus", "cb" => "Cumulonimbus", "clr" => "clear below 12,000 ft" ); $conditions = array( "+" => "heavy", "-" => "light", "vc" => "vicinity", "re" => "recent", "nsw" => "no significant weather", "mi" => "shallow", "bc" => "patches", "pr" => "partial", "ts" => "thunderstorm", "bl" => "blowing", "sh" => "showers", "dr" => "low drifting", "fz" => "freezing", "dz" => "drizzle", "ra" => "rain", "sn" => "snow", "sg" => "snow grains", "ic" => "ice crystals", "pe" => "ice pellets", "pl" => "ice pellets", "gr" => "hail", "gs" => "small hail/snow pellets", "up" => "unknown precipitation", "br" => "mist", "fg" => "fog", "fu" => "smoke", "va" => "volcanic ash", "sa" => "sand", "hz" => "haze", "py" => "spray", "du" => "widespread dust", "sq" => "squall", "ss" => "sandstorm", "ds" => "duststorm", "po" => "well developed dust/sand whirls", "fc" => "funnel cloud", "+fc" => "tornado/waterspout" ); } $tafCode = array( "report" => "TAF|AMD", "station" => "\w{4}", "update" => "(\d{2})?(\d{4})Z", "valid" => "(\d{2})(\d{2})\/(\d{2})(\d{2})", "wind" => "(\d{3}|VAR|VRB)(\d{2,3})(G(\d{2,3}))?(FPS|KPH|KT|KTS|MPH|MPS)", "visFrac" => "(\d{1})", "visibility" => "(\d{4})|((M|P)?((\d{1,2}|((\d) )?(\d)\/(\d))(SM|KM)))|(CAVOK)", "condition" => "(-|\+|VC|RE|NSW)?(MI|BC|PR|TS|BL|SH|DR|FZ)?((DZ)|(RA)|(SN)|(SG)|(IC)|(PE)|(PL)|(GR)|(GS)|(UP))*(BR|FG|FU|VA|DU|SA|HZ|PY)?(PO|SQ|FC|SS|DS)?", "clouds" => "(SKC|CLR|NSC|((FEW|SCT|BKN|OVC|VV)(\d{3}|\/{3})(TCU|CB)?))", "windshear" => "WS(\d{3})\/(\d{3})(\d{2,3})(FPS|KPH|KT|KTS|MPH|MPS)", "tempmax" => "TX(\d{2})\/(\d{2})(\w)", "tempmin" => "TN(\d{2})\/(\d{2})(\w)", "tempmaxmin" => "TX(\d{2})\/(\d{2})(\w)TN(\d{2})\/(\d{2})(\w)", "from" => "FM(\d{2})(\d{2})(\d{2})?Z?", "fmc" => "(PROB|BECMG|TEMPO)(\d{2})?" ); if (SERVICES_WEATHER_DEBUG) { for ($i = 0; $i < sizeof($data); $i++) { echo $data[$i]."\n"; } } // Eliminate trailing information for ($i = 0; $i < sizeof($data); $i++) { if (strpos($data[$i], "=") !== false) { $data[$i] = substr($data[$i], 0, strpos($data[$i], "=")); $data = array_slice($data, 0, $i + 1); break; } } // Ok, we have correct data, start with parsing the first line for the last update $forecastData = array(); $forecastData["station"] = ""; $forecastData["dataRaw"] = implode(" ", $data); $forecastData["update"] = strtotime(trim($data[0])." GMT"); $forecastData["updateRaw"] = trim($data[0]); // and prepare the rest for stepping through array_shift($data); $taf = explode(" ", preg_replace("/\s{2,}/", " ", implode(" ", $data))); // Add a few local variables for data processing $fromTime = ""; // The timeperiod the data gets added to $fmcCount = 0; // If we have FMCs (Forecast Meteorological Conditions), we need this $pointer =& $forecastData; // Pointer to the array we add the data to for ($i = 0; $i < sizeof($taf); $i++) { // Check for whitespace and step loop, if nothing's there $taf[$i] = trim($taf[$i]); if (!strlen($taf[$i])) { continue; } if (SERVICES_WEATHER_DEBUG) { $tab = str_repeat("\t", 3 - floor((strlen($taf[$i]) + 2) / 8)); echo "\"".$taf[$i]."\"".$tab."-> "; } // Initialize some arrays $result = array(); $resultVF = array(); $lresult = array(); $found = false; foreach ($tafCode as $key => $regexp) { // Check if current code matches current taf snippet if (($found = preg_match("/^".$regexp."$/i", $taf[$i], $result)) == true) { $insert = array(); switch ($key) { case "station": $pointer["station"] = $result[0]; unset($tafCode["station"]); break; case "valid": $pointer["validRaw"] = $result[0]; // Generates the timeperiod the report is valid for list($year, $month, $day) = explode("-", gmdate("Y-m-d", $forecastData["update"])); // Date is in next month if ($result[1] < $day) { $month++; } $pointer["validFrom"] = gmmktime($result[2], 0, 0, $month, $result[1], $year); $pointer["validTo"] = gmmktime($result[4], 0, 0, $month, $result[3], $year); unset($tafCode["valid"]); // Now the groups will start, so initialize the time groups $pointer["time"] = array(); $fromTime = $result[2].":00"; $pointer["time"][$fromTime] = array(); // Set pointer to the first timeperiod $pointer =& $pointer["time"][$fromTime]; break; case "wind": // Parse wind data, first the speed, convert from kt to chosen unit if ($result[5] == "KTS") { $result[5] = "KT"; } $pointer["wind"] = $this->convertSpeed($result[2], $result[5], "mph"); if ($result[1] == "VAR" || $result[1] == "VRB") { // Variable winds $pointer["windDegrees"] = "Variable"; $pointer["windDirection"] = "Variable"; } else { // Save wind degree and calc direction $pointer["windDegrees"] = $result[1]; $pointer["windDirection"] = $compass[round($result[1] / 22.5) % 16]; } if (is_numeric($result[4])) { // Wind with gusts... $pointer["windGust"] = $this->convertSpeed($result[4], $result[5], "mph"); } if (isset($probability)) { $pointer["windProb"] = $probability; unset($probability); } break; case "visFrac": // Possible fractional visibility here. Check if it matches with the next TAF piece for visibility if (!isset($taf[$i + 1]) || !preg_match("/^".$tafCode["visibility"]."$/i", $result[1]." ".$taf[$i + 1], $resultVF)) { // No next TAF piece available or not matching. Match against next TAF code $found = false; break; } else { // Match. Hand over result and advance TAF if (SERVICES_WEATHER_DEBUG) { echo $key."\n"; echo "\"".$result[1]." ".$taf[$i + 1]."\"".str_repeat("\t", 2 - floor((strlen($result[1]." ".$taf[$i + 1]) + 2) / 8))."-> "; } $key = "visibility"; $result = $resultVF; $i++; } case "visibility": $pointer["visQualifier"] = "AT"; if (is_numeric($result[1]) && ($result[1] == 9999)) { // Upper limit of visibility range $visibility = $this->convertDistance(10, "km", "sm"); $pointer["visQualifier"] = "BEYOND"; } elseif (is_numeric($result[1])) { // 4-digit visibility in m $visibility = $this->convertDistance(($result[1]/1000), "km", "sm"); } elseif (!isset($result[11]) || $result[11] != "CAVOK") { if ($result[3] == "M") { $pointer["visQualifier"] = "BELOW"; } elseif ($result[3] == "P") { $pointer["visQualifier"] = "BEYOND"; } if (is_numeric($result[5])) { // visibility as one/two-digit number $visibility = $this->convertDistance($result[5], $result[10], "sm"); } else { // the y/z part, add if we had a x part (see visibility1) if (is_numeric($result[7])) { $visibility = $this->convertDistance($result[7] + $result[8] / $result[9], $result[10], "sm"); } else { $visibility = $this->convertDistance($result[8] / $result[9], $result[10], "sm"); } } } else { $pointer["visQualifier"] = "BEYOND"; $visibility = $this->convertDistance(10, "km", "sm"); $pointer["clouds"] = array(array("amount" => "Clear below", "height" => 5000)); $pointer["condition"] = "no significant weather"; } if (isset($probability)) { $pointer["visProb"] = $probability; unset($probability); } $pointer["visibility"] = $visibility; break; case "condition": // First some basic setups if (!isset($pointer["condition"])) { $pointer["condition"] = ""; } elseif (strlen($pointer["condition"]) > 0) { $pointer["condition"] .= ","; } if (in_array(strtolower($result[0]), $conditions)) { // First try matching the complete string $pointer["condition"] .= " ".$conditions[strtolower($result[0])]; } else { // No luck, match part by part array_shift($result); $result = array_unique($result); foreach ($result as $condition) { if (strlen($condition) > 0) { $pointer["condition"] .= " ".$conditions[strtolower($condition)]; } } } $pointer["condition"] = trim($pointer["condition"]); if (isset($probability)) { $pointer["condition"] .= " (".$probability."% prob.)"; unset($probability); } break; case "clouds": if (!isset($pointer["clouds"])) { $pointer["clouds"] = array(); } if (sizeof($result) == 5) { // Only amount and height $cloud = array("amount" => $clouds[strtolower($result[3])]); if ($result[4] == "///") { $cloud["height"] = "station level or below"; } else { $cloud["height"] = $result[4] * 100; } } elseif (sizeof($result) == 6) { // Amount, height and type $cloud = array("amount" => $clouds[strtolower($result[3])], "type" => $clouds[strtolower($result[5])]); if ($result[4] == "///") { $cloud["height"] = "station level or below"; } else { $cloud["height"] = $result[4] * 100; } } else { // SKC or CLR or NSC $cloud = array("amount" => $clouds[strtolower($result[0])]); } if (isset($probability)) { $cloud["prob"] = $probability; unset($probability); } $pointer["clouds"][] = $cloud; break; case "windshear": // Parse windshear, if available if ($result[4] == "KTS") { $result[4] = "KT"; } $pointer["windshear"] = $this->convertSpeed($result[3], $result[4], "mph"); $pointer["windshearHeight"] = $result[1] * 100; $pointer["windshearDegrees"] = $result[2]; $pointer["windshearDirection"] = $compass[round($result[2] / 22.5) % 16]; break; case "tempmax": $forecastData["temperatureHigh"] = $this->convertTemperature($result[1], "c", "f"); break; case "tempmin": // Parse max/min temperature $forecastData["temperatureLow"] = $this->convertTemperature($result[1], "c", "f"); break; case "tempmaxmin": $forecastData["temperatureHigh"] = $this->convertTemperature($result[1], "c", "f"); $forecastData["temperatureLow"] = $this->convertTemperature($result[4], "c", "f"); break; case "from": // Next timeperiod is coming up, prepare array and // set pointer accordingly if (sizeof($result) > 2) { // The ICAO way $fromTime = $result[2].":".$result[3]; } else { // The Australian way (Hey mates!) $fromTime = $result[1].":00"; } $forecastData["time"][$fromTime] = array(); $fmcCount = 0; $pointer =& $forecastData["time"][$fromTime]; break; case "fmc"; // Test, if this is a probability for the next FMC if (isset($result[2]) && preg_match("/^BECMG|TEMPO$/i", $taf[$i + 1], $lresult)) { // Set type to BECMG or TEMPO $type = $lresult[0]; // Set probability $probability = $result[2]; // Now extract time for this group if (preg_match("/^(\d{2})(\d{2})$/i", $taf[$i + 2], $lresult)) { $from = $lresult[1].":00"; $to = $lresult[2].":00"; $to = ($to == "24:00") ? "00:00" : $to; // As we now have type, probability and time for this FMC // from our TAF, increase field-counter $i += 2; } else { // No timegroup present, so just increase field-counter by one $i += 1; } } elseif (preg_match("/^(\d{2})(\d{2})\/(\d{2})(\d{2})$/i", $taf[$i + 1], $lresult)) { // Normal group, set type and use extracted time $type = $result[1]; // Check for PROBdd if (isset($result[2])) { $probability = $result[2]; } $from = $lresult[2].":00"; $to = $lresult[4].":00"; $to = ($to == "24:00") ? "00:00" : $to; // Same as above, we have a time for this FMC from our TAF, // increase field-counter $i += 1; } elseif (isset($result[2])) { // This is either a PROBdd or a malformed TAF with missing timegroup $probability = $result[2]; } // Handle the FMC, generate neccessary array if it's the first... if (isset($type)) { if (!isset($forecastData["time"][$fromTime]["fmc"])) { $forecastData["time"][$fromTime]["fmc"] = array(); } $forecastData["time"][$fromTime]["fmc"][$fmcCount] = array(); // ...and set pointer. $pointer =& $forecastData["time"][$fromTime]["fmc"][$fmcCount]; $fmcCount++; // Insert data $pointer["type"] = $type; unset($type); if (isset($from)) { $pointer["from"] = $from; $pointer["to"] = $to; unset($from, $to); } if (isset($probability)) { $pointer["probability"] = $probability; unset($probability); } } break; default: // Do nothing break; } if ($found && !SERVICES_WEATHER_DEBUG) { break; } elseif ($found && SERVICES_WEATHER_DEBUG) { echo $key."\n"; break; } } } if (!$found) { if (SERVICES_WEATHER_DEBUG) { echo "n/a\n"; } if (!isset($forecastData["noparse"])) { $forecastData["noparse"] = array(); } $forecastData["noparse"][] = $taf[$i]; } } if (isset($forecastData["noparse"])) { $forecastData["noparse"] = implode(" ", $forecastData["noparse"]); } return $forecastData; } // }}} // {{{ _convertReturn() /** * Converts the data in the return array to the desired units and/or * output format. * * @param array $target * @param string $units * @param string $location * @access private */ function _convertReturn(&$target, $units, $location) { if (is_array($target)) { foreach ($target as $key => $val) { if (is_array($val)) { // Another array detected, so recurse into it to convert the units $this->_convertReturn($target[$key], $units, $location); } else { switch ($key) { case "station": $newVal = $location["name"]; break; case "update": case "validFrom": case "validTo": $newVal = gmdate(trim($this->_dateFormat." ".$this->_timeFormat), $val); break; case "wind": case "windGust": case "windshear": $newVal = round($this->convertSpeed($val, "mph", $units["wind"]), 2); break; case "visibility": $newVal = round($this->convertDistance($val, "sm", $units["vis"], 2)); break; case "height": case "windshearHeight": if (is_numeric($val)) { $newVal = round($this->convertDistance($val, "ft", $units["height"]), 2); } else { $newVal = $val; } break; case "temperature": case "temperatureHigh": case "temperatureLow": case "dewPoint": case "feltTemperature": $newVal = round($this->convertTemperature($val, "f", $units["temp"]), 2); break; case "pressure": case "seapressure": case "presschng": $newVal = round($this->convertPressure($val, "in", $units["pres"]), 2); break; case "amount": case "snowdepth": case "snowequiv": if (is_numeric($val)) { $newVal = round($this->convertPressure($val, "in", $units["rain"]), 2); } else { $newVal = $val; } break; case "1htemp": case "1hdew": case "6hmaxtemp": case "6hmintemp": case "24hmaxtemp": case "24hmintemp": $newVal = round($this->convertTemperature($val, "f", $units["temp"]), 2); break; case "humidity": $newVal = round($val, 1); break; default: continue 2; } $target[$key] = $newVal; } } } } // }}} // {{{ searchLocation() /** * Searches IDs for given location, returns array of possible locations * or single ID * * @param string|array $location * @param bool $useFirst If set, first ID of result-array is returned * @return PEAR_Error|array|string * @throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION * @throws PEAR_Error::SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED * @throws PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION * @access public */ function searchLocation($location, $useFirst = false) { if (!isset($this->_db) || !DB::isConnection($this->_db)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED, __FILE__, __LINE__); } if (is_string($location)) { // Try to part search string in name, state and country part // and build where clause from it for the select $location = explode(",", $location); // Trim, caps-low and quote the strings for ($i = 0; $i < sizeof($location); $i++) { $location[$i] = $this->_db->quote("%".strtolower(trim($location[$i]))."%"); } if (sizeof($location) == 1) { $where = "LOWER(name) LIKE ".$location[0]; } elseif (sizeof($location) == 2) { $where = "LOWER(name) LIKE ".$location[0]; $where .= " AND LOWER(country) LIKE ".$location[1]; } elseif (sizeof($location) == 3) { $where = "LOWER(name) LIKE ".$location[0]; $where .= " AND LOWER(state) LIKE ".$location[1]; $where .= " AND LOWER(country) LIKE ".$location[2]; } elseif (sizeof($location) == 4) { $where = "LOWER(name) LIKE ".substr($location[0], 0, -2).", ".substr($location[1], 2); $where .= " AND LOWER(state) LIKE ".$location[2]; $where .= " AND LOWER(country) LIKE ".$location[3]; } // Create select, locations with ICAO first $select = "SELECT icao, name, state, country, latitude, longitude ". "FROM metarLocations ". "WHERE ".$where." ". "ORDER BY icao DESC"; $result = $this->_db->query($select); // Check result for validity if (DB::isError($result)) { return $result; } elseif (strtolower(get_class($result)) != "db_result" || $result->numRows() == 0) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION, __FILE__, __LINE__); } // Result is valid, start preparing the return $icao = array(); while (($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) != null) { $locicao = $row["icao"]; // First the name of the location if (!strlen($row["state"])) { $locname = $row["name"].", ".$row["country"]; } else { $locname = $row["name"].", ".$row["state"].", ".$row["country"]; } if ($locicao != "----") { // We have a location with ICAO $icao[$locicao] = $locname; } else { // No ICAO, try finding the nearest airport $locicao = $this->searchAirport($row["latitude"], $row["longitude"]); if (!isset($icao[$locicao])) { $icao[$locicao] = $locname; } } } // Only one result? Return as string if (sizeof($icao) == 1 || $useFirst) { $icao = key($icao); } } elseif (is_array($location)) { // Location was provided as coordinates, search nearest airport $icao = $this->searchAirport($location[0], $location[1]); } else { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_INVALID_LOCATION, __FILE__, __LINE__); } return $icao; } // }}} // {{{ searchLocationByCountry() /** * Returns IDs with location-name for a given country or all available * countries, if no value was given * * @param string $country * @return PEAR_Error|array * @throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION * @throws PEAR_Error::SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @access public */ function searchLocationByCountry($country = "") { if (!isset($this->_db) || !DB::isConnection($this->_db)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED, __FILE__, __LINE__); } // Return the available countries as no country was given if (!strlen($country)) { $select = "SELECT DISTINCT(country) ". "FROM metarAirports ". "ORDER BY country ASC"; $countries = $this->_db->getCol($select); // As $countries is either an error or the true result, // we can just return it return $countries; } // Now for the real search $select = "SELECT icao, name, state, country ". "FROM metarAirports ". "WHERE LOWER(country) LIKE '%".strtolower(trim($country))."%' ". "ORDER BY name ASC"; $result = $this->_db->query($select); // Check result for validity if (DB::isError($result)) { return $result; } elseif (strtolower(get_class($result)) != "db_result" || $result->numRows() == 0) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION, __FILE__, __LINE__); } // Construct the result $locations = array(); while (($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) != null) { $locicao = $row["icao"]; if ($locicao != "----") { // First the name of the location if (!strlen($row["state"])) { $locname = $row["name"].", ".$row["country"]; } else { $locname = $row["name"].", ".$row["state"].", ".$row["country"]; } $locations[$locicao] = $locname; } } return $locations; } // }}} // {{{ searchAirport() /** * Searches the nearest airport(s) for given coordinates, returns array * of IDs or single ID * * @param float $latitude * @param float $longitude * @param int $numResults * @return PEAR_Error|array|string * @throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION * @throws PEAR_Error::SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED * @throws PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION * @access public */ function searchAirport($latitude, $longitude, $numResults = 1) { if (!isset($this->_db) || !DB::isConnection($this->_db)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED, __FILE__, __LINE__); } if (!is_numeric($latitude) || !is_numeric($longitude)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_INVALID_LOCATION, __FILE__, __LINE__); } // Get all airports $select = "SELECT icao, x, y, z FROM metarAirports"; $result = $this->_db->query($select); if (DB::isError($result)) { return $result; } elseif (strtolower(get_class($result)) != "db_result" || $result->numRows() == 0) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION, __FILE__, __LINE__); } // Result is valid, start search // Initialize values $min_dist = null; $query = $this->polar2cartesian($latitude, $longitude); $search = array("dist" => array(), "icao" => array()); while (($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) != null) { $icao = $row["icao"]; $air = array($row["x"], $row["y"], $row["z"]); $dist = 0; $d = 0; // Calculate distance of query and current airport // break off, if distance is larger than current $min_dist for($d; $d < sizeof($air); $d++) { $t = $air[$d] - $query[$d]; $dist += pow($t, 2); if ($min_dist != null && $dist > $min_dist) { break; } } if ($d >= sizeof($air)) { // Ok, current airport is one of the nearer locations // add to result-array $search["dist"][] = $dist; $search["icao"][] = $icao; // Sort array for distance array_multisort($search["dist"], SORT_NUMERIC, SORT_ASC, $search["icao"], SORT_STRING, SORT_ASC); // If array is larger then desired results, chop off last one if (sizeof($search["dist"]) > $numResults) { array_pop($search["dist"]); array_pop($search["icao"]); } $min_dist = max($search["dist"]); } } if ($numResults == 1) { // Only one result wanted, return as string return $search["icao"][0]; } elseif ($numResults > 1) { // Return found locations return $search["icao"]; } else { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION, __FILE__, __LINE__); } } // }}} // {{{ getLocation() /** * Returns the data for the location belonging to the ID * * @param string $id * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getLocation($id = "") { $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } $locationReturn = array(); if ($this->_cacheEnabled && ($location = $this->_getCache("METAR-".$id, "location"))) { // Grab stuff from cache $this->_location = $location; $locationReturn["cache"] = "HIT"; } elseif (isset($this->_db) && DB::isConnection($this->_db)) { // Get data from DB $select = "SELECT icao, name, state, country, latitude, longitude, elevation ". "FROM metarAirports WHERE icao='".$id."'"; $result = $this->_db->query($select); if (DB::isError($result)) { return $result; } elseif (strtolower(get_class($result)) != "db_result" || $result->numRows() == 0) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION, __FILE__, __LINE__); } // Result is ok, put things into object $this->_location = $result->fetchRow(DB_FETCHMODE_ASSOC); if ($this->_cacheEnabled) { // ...and cache it $this->_saveCache("METAR-".$id, $this->_location, "", "location"); } $locationReturn["cache"] = "MISS"; } else { $this->_location = array( "name" => $id, "state" => "", "country" => "", "latitude" => "", "longitude" => "", "elevation" => "" ); } // Stuff name-string together if (strlen($this->_location["state"]) && strlen($this->_location["country"])) { $locname = $this->_location["name"].", ".$this->_location["state"].", ".$this->_location["country"]; } elseif (strlen($this->_location["country"])) { $locname = $this->_location["name"].", ".$this->_location["country"]; } else { $locname = $this->_location["name"]; } $locationReturn["name"] = $locname; $locationReturn["latitude"] = $this->_location["latitude"]; $locationReturn["longitude"] = $this->_location["longitude"]; $locationReturn["sunrise"] = gmdate($this->_timeFormat, $this->calculateSunRiseSet(gmmktime(), SUNFUNCS_RET_TIMESTAMP, $this->_location["latitude"], $this->_location["longitude"], SERVICES_WEATHER_SUNFUNCS_SUNRISE_ZENITH, 0, true)); $locationReturn["sunset"] = gmdate($this->_timeFormat, $this->calculateSunRiseSet(gmmktime(), SUNFUNCS_RET_TIMESTAMP, $this->_location["latitude"], $this->_location["longitude"], SERVICES_WEATHER_SUNFUNCS_SUNSET_ZENITH, 0, false)); $locationReturn["elevation"] = $this->_location["elevation"]; return $locationReturn; } // }}} // {{{ getWeather() /** * Returns the weather-data for the supplied location * * @param string $id * @param string $unitsFormat * @return PHP_Error|array * @throws PHP_Error * @access public */ function getWeather($id = "", $unitsFormat = "") { $id = strtoupper($id); $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } // Get other data $units = $this->getUnitsFormat($unitsFormat); $location = $this->getLocation($id); if (Services_Weather::isError($location)) { return $location; } if ($this->_cacheEnabled && ($weather = $this->_getCache("METAR-".$id, "weather"))) { // Wee... it was cached, let's have it... $weatherReturn = $weather; $this->_weather = $weatherReturn; $weatherReturn["cache"] = "HIT"; } else { // Download weather $weatherData = $this->_retrieveServerData($id, "metar"); if (Services_Weather::isError($weatherData)) { return $weatherData; } elseif (!is_array($weatherData) || sizeof($weatherData) < 2) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } // Parse weather $weatherReturn = $this->_parseWeatherData($weatherData); if (Services_Weather::isError($weatherReturn)) { return $weatherReturn; } // Add an icon for the current conditions // Determine if certain values are set, if not use defaults $condition = isset($weatherReturn["condition"]) ? $weatherReturn["condition"] : "No Significant Weather"; $clouds = isset($weatherReturn["clouds"]) ? $weatherReturn["clouds"] : array(); $wind = isset($weatherReturn["wind"]) ? $weatherReturn["wind"] : 5; $temperature = isset($weatherReturn["temperature"]) ? $weatherReturn["temperature"] : 70; $latitude = isset($location["latitude"]) ? $location["latitude"] : -360; $longitude = isset($location["longitude"]) ? $location["longitude"] : -360; // Get the icon $weatherReturn["conditionIcon"] = $this->getWeatherIcon($condition, $clouds, $wind, $temperature, $latitude, $longitude, strtotime($weatherReturn["updateRaw"]." GMT")); // Calculate the moon phase and age $moon = $this->calculateMoonPhase(strtotime($weatherReturn["updateRaw"]." GMT")); if (Services_Weather::isError($moon)) { return $moon; } $weatherReturn["moon"] = $moon["phase"]; $weatherReturn["moonIcon"] = $moon["icon"]; if ($this->_cacheEnabled) { // Cache weather $this->_saveCache("METAR-".$id, $weatherReturn, $unitsFormat, "weather"); } $this->_weather = $weatherReturn; $weatherReturn["cache"] = "MISS"; } $this->_convertReturn($weatherReturn, $units, $location); return $weatherReturn; } // }}} // {{{ getForecast() /** * METAR provides no forecast per se, we use the TAF reports to generate * a forecast for the announced timeperiod * * @param string $id * @param int $days Ignored, not applicable * @param string $unitsFormat * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getForecast($id = "", $days = null, $unitsFormat = "") { $id = strtoupper($id); $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } // Get other data $units = $this->getUnitsFormat($unitsFormat); $location = $this->getLocation($id); if (Services_Weather::isError($location)) { return $location; } if ($this->_cacheEnabled && ($forecast = $this->_getCache("METAR-".$id, "forecast"))) { // Wee... it was cached, let's have it... $forecastReturn = $forecast; $this->_forecast = $forecastReturn; $forecastReturn["cache"] = "HIT"; } else { // Download forecast $forecastData = $this->_retrieveServerData($id, "taf"); if (Services_Weather::isError($forecastData)) { return $forecastData; } elseif (!is_array($forecastData) || sizeof($forecastData) < 2) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } // Parse forecast $forecastReturn = $this->_parseForecastData($forecastData); if (Services_Weather::isError($forecastReturn)) { return $forecastReturn; } if ($this->_cacheEnabled) { // Cache weather $this->_saveCache("METAR-".$id, $forecastReturn, $unitsFormat, "forecast"); } $this->_forecast = $forecastReturn; $forecastReturn["cache"] = "MISS"; } $this->_convertReturn($forecastReturn, $units, $location); return $forecastReturn; } // }}} } // }}} ?> php-services-weather-1.4.7/Services_Weather-1.4.7/Weather/Weatherdotcom.php000066400000000000000000000657141250332203600264250ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/Services_Weather * @link http://www.weather.com/services/xmloap.html * @example examples/weather.com-basic.php weather.com-basic.php * @example examples/weather.com-extensive.php weather.com-extensive.php * @filesource */ require_once "Services/Weather/Common.php"; // {{{ class Services_Weather_Weatherdotcom /** * This class acts as an interface to the xml service of weather.com. It * searches for given locations and retrieves current weather data as well * as forecast for up to 5 days. * * For using the weather.com xml-service please visit * http://www.weather.com/services/xmloap.html * and follow the link to sign up, it's free! You will receive an email * where to download the SDK with the needed images and guidelines how to * publish live data from weather.com. Unfortunately the guidelines are a * bit harsh, that's why there's no actual data-representation in this * class, just the raw data. Also weather.com demands active caching, so I'd * strongly recommend enabling the caching implemented in this class. It * obeys to the times as written down in the guidelines. * * For working examples, please take a look at * docs/Services_Weather/examples/weather.com-basic.php * docs/Services_Weather/examples/weather.com-extensive.php * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version Release: 1.4.7 * @link http://pear.php.net/package/Services_Weather * @link http://www.weather.com/services/xmloap.html * @example examples/weather.com-basic.php weather.com-basic.php * @example examples/weather.com-extensive.php weather.com-extensive.php */ class Services_Weather_Weatherdotcom extends Services_Weather_Common { // {{{ properties /** * Partner-ID at weather.com * * @var string $_partnerID * @access private */ var $_partnerID = ""; /** * License key at weather.com * * @var string $_licenseKey * @access private */ var $_licenseKey = ""; /** * Switch to toggle pre-fetching of data in one single request * * @var bool $_preFetch * @access private */ var $_preFetch = false; /** * Object containing the promotional links-data * * @var object stdClass $_links * @access private */ var $_links; /** * Object containing the location * * @var object stdClass $_location * @access private */ var $_location; /** * Object containing the weather * * @var object stdClass $_weather * @access private */ var $_weather; /** * Object containing the forecast * * @var object stdClass $_forecast * @access private */ var $_forecast; /** * XML_Unserializer, used for processing the xml * * @var object XML_Unserializer $_unserializer * @access private */ var $_unserializer; // }}} // {{{ constructor /** * Constructor * * Requires XML_Serializer to be installed * * @param array $options * @param mixed $error * @throws PEAR_Error * @access private */ function Services_Weather_Weatherdotcom($options, &$error) { $perror = null; $this->Services_Weather_Common($options, $perror); if (Services_Weather::isError($perror)) { $error = $perror; return; } // Set options accordingly if (isset($options["partnerID"])) { $this->setAccountData($options["partnerID"]); } if (isset($options["licenseKey"])) { $this->setAccountData("", $options["licenseKey"]); } if (isset($options["preFetch"])) { $this->enablePreFetch($options["preFetch"]); } include_once "XML/Unserializer.php"; $unserializer = &new XML_Unserializer(array("complexType" => "object", "keyAttribute" => "type")); if (Services_Weather::isError($unserializer)) { $error = $unserializer; return; } else { $this->_unserializer = $unserializer; } // Initialize the properties containing the data from the server $this->_links = null; $this->_location = null; $this->_weather = null; $this->_forecast = null; // Can't acquire an object here, has to be clean on every request include_once "HTTP/Request.php"; } // }}} // {{{ setAccountData() /** * Sets the neccessary account-information for weather.com, you'll * receive them after registering for the XML-stream * * @param string $partnerID * @param string $licenseKey * @access public */ function setAccountData($partnerID, $licenseKey) { if (strlen($partnerID) && ctype_digit($partnerID)) { $this->_partnerID = $partnerID; } if (strlen($licenseKey) && ctype_alnum($licenseKey)) { $this->_licenseKey = $licenseKey; } } // }}} // {{{ enablePreFetch() /** * Enables pre-fetching of data in one single request * * @param bool $preFetch * @access public */ function enablePreFetch($preFetch) { if ($preFetch == true) { $this->_preFetch = true; } } // }}} // {{{ _checkLocationID() /** * Checks the id for valid values and thus prevents silly requests to * weather.com server * * @param string $id * @return PEAR_Error|bool * @throws PEAR_Error::SERVICES_WEATHER_ERROR_NO_LOCATION * @throws PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION * @access private */ function _checkLocationID($id) { if (is_array($id) || is_object($id) || !strlen($id)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_NO_LOCATION, __FILE__, __LINE__); } elseif (!ctype_alnum($id) || (strlen($id) > 8)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_INVALID_LOCATION, __FILE__, __LINE__); } return true; } // }}} // {{{ _parseWeatherData() /** * Fetches the data based on the requested type and caches it * * @param string $id * @param string $reqType * @return PEAR_Error|bool * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @throws PEAR_Error * @access private */ function _parseWeatherData($id, $reqType) { if ($this->_preFetch) { $reqType = "all"; } $url = "http://xoap.weather.com/weather/local/".$id."?link=xoap&prod=xoap&par=".$this->_partnerID."&key=".$this->_licenseKey; switch ($reqType) { case "links": $url .= ""; break; case "weather": $url .= "&cc=*&unit=s"; break; case "forecast": $url .= "&dayf=5&unit=s"; break; case "all": $url .= "&cc=*&dayf=5&unit=s"; break; } // Get data from URL... $request = &new HTTP_Request($url, $this->_httpOptions); $status = $request->sendRequest(); if (Services_Weather::isError($status) || (int) $request->getResponseCode() <> 200) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } $data = $request->getResponseBody(); // ...and unserialize $status = $this->_unserializer->unserialize($data); if (Services_Weather::isError($status)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } else { $root = $this->_unserializer->getRootName(); $data = $this->_unserializer->getUnserializedData(); if (Services_Weather::isError($root) || $root == "HTML") { // Something wrong here, maybe not XML retrieved... return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } elseif ($root == "error") { // We got an error back from weather.com $errno = key(get_object_vars($data)); return Services_Weather::raiseError($errno, __FILE__, __LINE__); } else { // Valid data, lets get started // Loop through the different sub-parts of the data for processing foreach (get_object_vars($data) as $key => $val) { switch ($key) { case "head": continue 2; case "prmo": $varname = "links"; break; case "loc": $varname = "location"; break; case "cc": $varname = "weather"; break; case "dayf": $varname = "forecast"; break; } // Save data in object $this->{"_".$varname} = $val; if ($this->_cacheEnabled) { // ...and cache if possible $this->_saveCache($id, $val, "", $varname); } } } } return true; } // }}} // {{{ searchLocation() /** * Searches IDs for given location, returns array of possible locations * or single ID * * @param string $location * @param bool $useFirst If set, first ID of result-array is returned * @return PEAR_Error|array|string * @throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA * @throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION * @access public */ function searchLocation($location, $useFirst = false) { $location = trim($location); $locLow = strtolower($location); // Check on cached data: MD5-hash of location has to be correct and the userdata has to be the same as the given location if ($this->_cacheEnabled && $locLow == $this->_getUserCache(md5($locLow), "search")) { $search = $this->_getCache(md5($locLow), "search"); } else { // Get search data from server and unserialize $request = &new HTTP_Request("http://xoap.weather.com/search/search?where=".urlencode($location), $this->_httpOptions); $status = $request->sendRequest(); if (Services_Weather::isError($status) || (int) $request->getResponseCode() <> 200) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } $data = $request->getResponseBody(); // ...and unserialize $status = $this->_unserializer->unserialize($data, false, array("overrideOptions" => true, "complexType" => "array", "keyAttribute" => "id")); if (Services_Weather::isError($status)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } $root = $this->_unserializer->getRootName(); $search = $this->_unserializer->getUnserializedData(); if (Services_Weather::isError($search) || $root == "HTML") { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } elseif (!is_array($search) || !sizeof($search)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION, __FILE__, __LINE__); } if ($this->_cacheEnabled) { // ...and cache if possible $this->_saveCache(md5($locLow), $search, $locLow, "search"); } } if (!$useFirst && (sizeof($search) > 1)) { $searchReturn = $search; } elseif ($useFirst || (sizeof($search) == 1)) { $searchReturn = key($search); } else { $searchReturn = array(); } return $searchReturn; } // }}} // {{{ searchLocationByCountry() /** * Returns only false, as weather.com offers no country listing via * its XML services * * @param string $country * @return bool * @access public * @deprecated */ function searchLocationByCountry($country = "") { return false; } // }}} // {{{ getLinks() /** * Returns the data for the promotional links belonging to the ID * * @param string $id * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getLinks($id = "") { $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } $linksReturn = array(); if (is_object($this->_links)) { $linksReturn["cache"] = "MEM"; } elseif ($this->_cacheEnabled && ($links = $this->_getCache($id, "links"))) { // Get data from cache $this->_links = $links; $linksReturn["cache"] = "HIT"; } else { // Same as in the function above... $status = $this->_parseWeatherData($id, "links"); if (Services_Weather::isError($status)) { return $status; } $linksReturn["cache"] = "MISS"; } $linksReturn["promo"] = array(); for ($i = 0; $i < sizeof($this->_links->link); $i++) { $linksReturn["promo"][$i] = array(); $linksReturn["promo"][$i]["title"] = $this->_links->link[$i]->t; // B0rked response (returned is &par=xoap, should be &prod=xoap), fix it $linksReturn["promo"][$i]["link"] = str_replace("par=", "prod=", $this->_links->link[$i]->l); $linksReturn["promo"][$i]["link"] .= "&par=".$this->_partnerID; } return $linksReturn; } // }}} // {{{ getLocation() /** * Returns the data for the location belonging to the ID * * @param string $id * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getLocation($id = "") { $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } $locationReturn = array(); if (is_object($this->_location)) { $locationReturn["cache"] = "MEM"; } elseif ($this->_cacheEnabled && ($location = $this->_getCache($id, "location"))) { // Get data from cache $this->_location = $location; $locationReturn["cache"] = "HIT"; } else { // Same as in the function above... $status = $this->_parseWeatherData($id, "location"); if (Services_Weather::isError($status)) { return $status; } $locationReturn["cache"] = "MISS"; } $locationReturn["name"] = $this->_location->dnam; $locationReturn["time"] = date($this->_timeFormat, strtotime($this->_location->tm)); $locationReturn["latitude"] = $this->_location->lat; $locationReturn["longitude"] = $this->_location->lon; $locationReturn["sunrise"] = date($this->_timeFormat, strtotime($this->_location->sunr)); $locationReturn["sunset"] = date($this->_timeFormat, strtotime($this->_location->suns)); $locationReturn["timezone"] = $this->_location->zone; return $locationReturn; } // }}} // {{{ getWeather() /** * Returns the weather-data for the supplied location * * @param string $id * @param string $unitsFormat * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getWeather($id = "", $unitsFormat = "") { $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } // Get other data $units = $this->getUnitsFormat($unitsFormat); $weatherReturn = array(); if (is_object($this->_weather)) { $weatherReturn["cache"] = "MEM"; } elseif ($this->_cacheEnabled && ($weather = $this->_getCache($id, "weather"))) { // Same procedure... $this->_weather = $weather; $weatherReturn["cache"] = "HIT"; } else { // ...as last function $status = $this->_parseWeatherData($id, "weather"); if (Services_Weather::isError($status)) { return $status; } $weatherReturn["cache"] = "MISS"; } // Make sure the location object has been loaded if (!is_object($this->_location)) { $this->getLocation($id); } // Some explanation for the next two lines: // weather.com isn't always supplying the timezone in the update string, but // uses "Local Time" as reference, which is imho utterly stupid, because it's // inconsistent. Well, what I do here is check for this string and if I can // find it, I calculate the difference between the timezone at the location // and this computers timezone. This amount of seconds is then subtracted from // the time the update-string has delivered. $update = str_replace("Local Time", "", $this->_weather->lsup); $adjustTZ = ($update == $this->_weather->lsup) ? 0 : $this->_location->zone * 3600 - date("Z"); $weatherReturn["update"] = gmdate(trim($this->_dateFormat." ".$this->_timeFormat), strtotime($update) - $adjustTZ); $weatherReturn["updateRaw"] = $this->_weather->lsup; $weatherReturn["station"] = $this->_weather->obst; $weatherReturn["temperature"] = round($this->convertTemperature($this->_weather->tmp, "f", $units["temp"]), 2); $weatherReturn["feltTemperature"] = round($this->convertTemperature($this->_weather->flik, "f", $units["temp"], 2)); $weatherReturn["condition"] = $this->_weather->t; $weatherReturn["conditionIcon"] = $this->_weather->icon; $weatherReturn["pressure"] = round($this->convertPressure($this->_weather->bar->r, "in", $units["pres"]), 2); $weatherReturn["pressureTrend"] = $this->_weather->bar->d; $weatherReturn["wind"] = round($this->convertSpeed($this->_weather->wind->s, "mph", $units["wind"]), 2); $weatherReturn["windGust"] = round($this->convertSpeed($this->_weather->wind->gust, "mph", $units["wind"]), 2); $weatherReturn["windDegrees"] = $this->_weather->wind->d; $weatherReturn["windDirection"] = $this->_weather->wind->t; $weatherReturn["humidity"] = round($this->_weather->hmid, 1); if (is_numeric($this->_weather->vis)) { $weatherReturn["visibility"] = round($this->convertDistance($this->_weather->vis, "sm", $units["vis"]), 2); } else { $weatherReturn["visibility"] = $this->_weather->vis; } $weatherReturn["uvIndex"] = $this->_weather->uv->i; $weatherReturn["uvText"] = $this->_weather->uv->t; $weatherReturn["dewPoint"] = round($this->convertTemperature($this->_weather->dewp, "f", $units["temp"]), 2); $weatherReturn["moon"] = $this->_weather->moon->t; $weatherReturn["moonIcon"] = $this->_weather->moon->icon; return $weatherReturn; } // }}} // {{{ getForecast() /** * Get the forecast for the next days * * @param string $id * @param int $days Values between 1 and 5 * @param string $unitsFormat * @return PEAR_Error|array * @throws PEAR_Error * @access public */ function getForecast($id = "", $days = 5, $unitsFormat = "") { $status = $this->_checkLocationID($id); if (Services_Weather::isError($status)) { return $status; } if (!is_int($days) || ($days < 1) || ($days > 5)) { $days = 5; } // Get other data $units = $this->getUnitsFormat($unitsFormat); $forecastReturn = array(); if (is_object($this->_forecast)) { $forecastReturn["cache"] = "MEM"; } elseif ($this->_cacheEnabled && ($forecast = $this->_getCache($id, "forecast"))) { // Encore... $this->_forecast = $forecast; $forecastReturn["cache"] = "HIT"; } else { // ... $status = $this->_parseWeatherData($id, "forecast"); if (Services_Weather::isError($status)) { return $status; } $forecastReturn["cache"] = "MISS"; } // Make sure the location object has been loaded if (!is_object($this->_location)) { $this->getLocation($id); } // Some explanation for the next two lines: (same as above) // weather.com isn't always supplying the timezone in the update string, but // uses "Local Time" as reference, which is imho utterly stupid, because it's // inconsistent. Well, what I do here is check for this string and if I can // find it, I calculate the difference between the timezone at the location // and this computers timezone. This amount of seconds is then subtracted from // the time the update-string has delivered. $update = str_replace("Local Time", "", $this->_forecast->lsup); $adjustTZ = ($update == $this->_forecast->lsup) ? 0 : $this->_location->zone * 3600 - date("Z"); $forecastReturn["update"] = gmdate($this->_dateFormat." ".$this->_timeFormat, strtotime($update) - $adjustTZ); $forecastReturn["updateRaw"] = $this->_forecast->lsup; $forecastReturn["days"] = array(); for ($i = 0; $i < $days; $i++) { $day = array( "temperatureHigh" => round($this->convertTemperature($this->_forecast->day[$i]->hi, "f", $units["temp"]), 2), "temperatureLow" => round($this->convertTemperature($this->_forecast->day[$i]->low, "f", $units["temp"]), 2), "sunrise" => date($this->_timeFormat, strtotime($this->_forecast->day[$i]->sunr)), "sunset" => date($this->_timeFormat, strtotime($this->_forecast->day[$i]->suns)), "day" => array( "condition" => $this->_forecast->day[$i]->part[0]->t, "conditionIcon" => $this->_forecast->day[$i]->part[0]->icon, "wind" => round($this->convertSpeed($this->_forecast->day[$i]->part[0]->wind->s, "mph", $units["wind"]), 2), "windGust" => round($this->convertSpeed($this->_forecast->day[$i]->part[0]->wind->gust, "mph", $units["wind"]), 2), "windDegrees" => $this->_forecast->day[$i]->part[0]->wind->d, "windDirection" => $this->_forecast->day[$i]->part[0]->wind->t, "precipitation" => $this->_forecast->day[$i]->part[0]->ppcp, "humidity" => round($this->_forecast->day[$i]->part[0]->hmid, 1) ), "night" => array ( "condition" => $this->_forecast->day[$i]->part[1]->t, "conditionIcon" => $this->_forecast->day[$i]->part[1]->icon, "wind" => round($this->convertSpeed($this->_forecast->day[$i]->part[1]->wind->s, "mph", $units["wind"]), 2), "windGust" => round($this->convertSpeed($this->_forecast->day[$i]->part[1]->wind->gust, "mph", $units["wind"]), 2), "windDegrees" => $this->_forecast->day[$i]->part[1]->wind->d, "windDirection" => $this->_forecast->day[$i]->part[1]->wind->t, "precipitation" => $this->_forecast->day[$i]->part[1]->ppcp, "humidity" => round($this->_forecast->day[$i]->part[1]->hmid, 1) ) ); $forecastReturn["days"][] = $day; } return $forecastReturn; } // }}} } // }}} ?> php-services-weather-1.4.7/Services_Weather-1.4.7/buildMetarDB.php000077500000000000000000000374141250332203600245160ustar00rootroot00000000000000#!/usr/local/bin/php * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @subpackage buildMetarDB * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version SVN: $Id$ * @link http://pear.php.net/package/Services_Weather * @link http://weather.noaa.gov/tg/site.shtml * @filesource */ require_once "DB.php"; // {{{ constants // {{{ natural constants and measures define("SERVICES_WEATHER_RADIUS_EARTH", 6378.15); // }}} // }}} // {{{ Services_Weather_checkData() /** * Services_Weather_checkData * * Checks the data for a certain string-length and if it either consists of * a certain char-type or a string of "-" as replacement. * * @param array $data The data to be checked * @param array $dataOrder Because the data is in different locations, we provide this * @return bool */ function Services_Weather_checkData($data, $dataOrder) { $return = true; foreach ($dataOrder as $type => $idx) { switch (strtolower($type)) { case "b": $len = 2; $func = "ctype_digit"; break; case "s": $len = 3; $func = "ctype_digit"; break; case "i": $len = 4; $func = "ctype_alnum"; break; default: break; } if ((strlen($data[$idx]) != $len) || (!$func($data[$idx]) && ($data[$idx] != str_repeat("-", $len)))) { $return = false; break; } } return $return; } // }}} // {{{ Services_Weather_getNextArg() /** * Services_Weather_getNextArg * * Checks, if the next argument is a parameter to a predecessing option. * Returns either that parameter or false, if the next argument is an option * * @param int $c Internal argument counter * @return string|bool */ function Services_Weather_getNextArg(&$c) { if ((($c + 1) < $_SERVER["argc"]) && ($_SERVER["argv"][$c + 1]{0} != "-")) { $c++; return $_SERVER["argv"][$c]; } else { return false; } } // }}} // First set a few variables for processing the options $modeSet = false; $saveFile = false; $printHelp = false; $invOpt = false; $verbose = 0; $dbType = "mysql"; $dbProt = "unix"; $dbName = "servicesWeatherDB"; $dbUser = "root"; $dbPass = ""; $dbHost = "localhost"; $dbOptions = array(); $userFile = ""; // Iterate through the arguments and check their validity for ($c = 1; $c < $_SERVER["argc"]; $c++) { switch ($_SERVER["argv"][$c]{1}) { case "l": // location-mode, if another mode is set, bail out if ($modeSet) { $printHelp = true; } else { $modeSet = true; $filePart = "bbsss"; $tableName = "metarLocations"; $dataOrder = array("b" => 0, "s" => 1, "i" => 2); } break; case "a": // dito for airport-mode if ($modeSet) { $printHelp = true; } else { $modeSet = true; $filePart = "cccc"; $tableName = "metarAirports"; $dataOrder = array("b" => 1, "s" => 2, "i" => 0); } break; case "f": // file-flag was provided, check if next argument is a string if (($userFile = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "s": // If you download the file, it will be saved to disk $saveFile = true; break; case "t": // The type of the DB to be used if (($dbType = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "r": // The protocol of the DB to be used if (($dbProt = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "d": // The name of the DB to be used if (($dbName = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "u": // The user of the DB to be used if (($dbUser = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "p": // The password of the DB to be used if (($dbPass = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "h": // The host of the DB to be used if (($dbHost = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "o": // Options for the DB if (($options = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } else { $options = explode(",", $options); foreach ($options as $option) { $optPair = explode("=", $option); $dbOptions[$optPair[0]] = $optPair[1]; } } break; case "v": // increase verbosity for ($i = 1; $i < strlen($_SERVER["argv"][$c]); $i++) { if ($_SERVER["argv"][$c]{$i} == "v") { $verbose++; } else { $invOpt = true; break; } } break; default: // argument not valid, bail out $invOpt = true; break; } if ($invOpt) { // see above $printHelp = true; echo "Invalid option: '".$_SERVER["argv"][$c]."'\n"; break; } } // help-message if (!$modeSet || $printHelp) { echo "Usage: ".basename($_SERVER["argv"][0], ".php")." -l|-a [options]\n"; echo "Options:\n"; echo " -l build locationsDB\n"; echo " -a build airportsDB\n"; echo " -f use as input\n"; echo " -s save downloaded file to disk\n"; echo " -t type of the DB to be used\n"; echo " -r protocol -----\"----------\n"; echo " -d name ---------\"----------\n"; echo " -u user ---------\"----------\n"; echo " -p pass ---------\"----------\n"; echo " -h host ---------\"----------\n"; echo " -o options ------\"----------\n"; echo " in the notation option=value,...\n"; echo " -v display verbose debugging messages\n"; echo " multiple -v increases verbosity\n"; exit(255); } // check, if zlib is available if (extension_loaded("zlib")) { $open = "gzopen"; $close = "gzclose"; $files = array( $userFile, "nsd_".$filePart, "nsd_".$filePart.".txt", "nsd_".$filePart.".gz", "http://weather.noaa.gov/data/nsd_".$filePart.".gz" ); } else { $open = "fopen"; $close = "fclose"; $files = array( $userFile, "nsd_".$filePart, "nsd_".$filePart.".txt", "http://weather.noaa.gov/data/nsd_".$filePart.".txt" ); } // then try to open a source in the given order foreach ($files as $file) { $fp = @$open($file, "rb"); if ($fp) { // found a valid source if ($verbose > 0) { echo "Services_Weather: Using '".$file."' as source.\n"; } if ($saveFile && !file_exists($file)) { // apparently we want to save the file, and it's a remote file $file = basename($file); $fps = @$open($file, "wb"); if (!$fps) { echo "Services_Weather: Couldn't save to '".$file."'!\n"; } else { if ($verbose > 0) { echo "Services_Weather: Saving source to '".$file."'.\n"; } // read from filepointer and save to disk while ($line = fread($fp, 1024)) { fwrite($fps, $line, strlen($line)); } // unfortunately zlib does not support r/w on a resource, // so no rewind -> move $fp to new file on disk $close($fp); $close($fps); $fp = @$open($file, "rb"); } } break; } } if (!$fp) { // no files found, or connection not available... bail out die("Services_Weather: Sourcefile nsd_".$filePart." not found!\n"); } $dsn = $dbType."://".$dbUser.":".$dbPass."@".$dbProt."+".$dbHost."/".$dbName; $dsninfo = array( "phptype" => $dbType, "protocol" => $dbProt, "username" => $dbUser, "password" => $dbPass, "hostspec" => $dbHost, "database" => $dbName, "mode" => "0644" ); $db = DB::connect($dsninfo, $dbOptions); if (DB::isError($db)) { echo "Services_Weather: Connection to DB with '".$dbType."://".$dbUser.":PASS@".$dbHost."/".$dbName."' failed!\n"; die($db->getMessage()."\n"); } else { // Test, if we have to swipe or create the table first $select = "SELECT * FROM ".$tableName; $result = $db->query($select); if (DB::isError($result)) { // Create new table $create = "CREATE TABLE ".$tableName."(id int,block int,station int,icao varchar(4),name varchar(80),state varchar(2),country varchar(50),wmo int,latitude float,longitude float,elevation float,x float,y float,z float)"; if ($verbose > 0) { echo "Services_Weather: Creating table '".$tableName."'.\n"; } $result = $db->query($create); if (DB::isError($result)) { die($result->getMessage()."\n"); } } else { // Delete the old stuff $delete = "DELETE FROM ".$tableName; if ($verbose > 0) { echo "Services_Weather: Deleting from table '".$tableName."'.\n"; } $result = $db->query($delete); if (DB::isError($result)) { die($result->getMessage()."\n"); } } // Ok, DB should be up and running now, let's shove in the data $line = 0; $error = 0; // read data from file while ($data = fgetcsv($fp, 1000, ";")) { // Check for valid data if ((sizeof($data) < 9) || !Services_Weather_checkData($data, $dataOrder)) { echo "Services_Weather: Invalid data in file!\n"; echo "\tLine ".($line + 1).": ".implode(";", $data)."\n"; $error++; } else { // calculate latitude and longitude // it comes in a ddd-mm[-ss]N|S|E|W format $coord = array( "latitude" => 7, "longitude" => 8); foreach ($coord as $latlon => $aId) { preg_match("/^(\d{1,3})-(\d{1,2})(-(\d{1,2}))?([NSEW])$/", $data[$aId], $result); ${$latlon} = 0; $factor = 1; foreach ($result as $var) { if ((strlen($var) > 0) && ctype_digit($var)) { ${$latlon} += $var / $factor; $factor *= 60; } elseif (ctype_alpha($var) && in_array($var, array("S", "W"))) { ${$latlon} *= (-1); } } } // Calculate the cartesian coordinates for latitude and longitude $theta = deg2rad($latitude); $phi = deg2rad($longitude); $x = SERVICES_WEATHER_RADIUS_EARTH * cos($phi) * cos($theta); $y = SERVICES_WEATHER_RADIUS_EARTH * sin($phi) * cos($theta); $z = SERVICES_WEATHER_RADIUS_EARTH * sin($theta); // Check for elevation in data $elevation = is_numeric($data[11]) ? $data[11] : 0; // integers: convert "--" fields to null, empty fields to 0 foreach (array($dataOrder["b"], $dataOrder["s"], 6) as $i) { if (strpos($data[$i], "--") !== false) { $data[$i] = "null"; } elseif ($data[$i] == "") { $data[$i] = 0; } } // strings: quote foreach (array($dataOrder["i"], 3, 4, 5) as $i) { $data[$i] = $db->quote($data[$i]); } // insert data $insert = "INSERT INTO ".$tableName." VALUES(".($line - $error).","; $insert .= $data[$dataOrder["b"]].",".$data[$dataOrder["s"]].","; $insert .= $data[$dataOrder["i"]].",".$data[3].",".$data[4].","; $insert .= $data[5].",".$data[6].",".round($latitude, 4).","; $insert .= round($longitude, 4).",".$elevation.",".round($x, 4).","; $insert .= round($y, 4).",".round($z, 4).")"; $result = $db->query($insert); if (DB::isError($result)) { echo "\tLine ".($line + 1).": ".$insert."\n"; echo $result->getMessage()."\n"; $error++; } elseif($verbose > 2) { echo $insert."\n"; } } $line++; } // commit and close $db->disconnect(); if ($verbose > 0 || $error > 0) { echo "Services_Weather: ".($line - $error)." ".$tableName." added "; echo "to database '".$dbName."' (".$error." error(s)).\n"; } } $close($fp); ?> php-services-weather-1.4.7/Services_Weather-1.4.7/examples/000077500000000000000000000000001250332203600233115ustar00rootroot00000000000000php-services-weather-1.4.7/Services_Weather-1.4.7/examples/ejse-basic.php000066400000000000000000000063111250332203600260300ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version SVN: $Id$ * @link http://pear.php.net/package/Services_Weather * @filesource */ require_once "Services/Weather.php"; // Object initialization - error checking is important, because of // handling exceptions such as missing PEAR modules or not being online $ejse = &Services_Weather::service("Ejse", array("debug" => 2)); if (Services_Weather::isError($ejse)) { die("Error: ".$ejse->getMessage()."\n"); } // Set ejse.com account data $ejse->setAccountData("", ""); /* Erase comments to enable caching $status = $ejse->setCache("file", array("cache_dir" => "/tmp/cache/")); if (Services_Weather::isError($status)) { echo "Error: ".$status->getMessage()."\n"; } */ $ejse->setUnitsFormat("metric"); $ejse->setDateTimeFormat("d.m.Y", "H:i"); $location = "81611"; // Aspen, CO //$location = "02115"; // Boston, MA //$location = "96799"; // Pago Pago, AS //$location = "09009"; // Armed Forces Europe -> Error // Now iterate through available functions for retrieving data foreach (array("getLocation", "getWeather", "getForecast") as $function) { $data = $ejse->$function($location); if (Services_Weather::isError($data)) { echo "Error: ".$data->getMessage()."\n"; continue; } var_dump($data); } ?> php-services-weather-1.4.7/Services_Weather-1.4.7/examples/globalweather-basic.php000066400000000000000000000063241250332203600277260ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version SVN: $Id$ * @link http://pear.php.net/package/Services_Weather * @filesource */ require_once "Services/Weather.php"; // Object initialization - error checking is important, because of // handling exceptions such as missing PEAR modules or not being online $globalweather = &Services_Weather::service("GlobalWeather", array("debug" => 2)); if (Services_Weather::isError($globalweather)) { die("Error: ".$globalweather->getMessage()."\n"); } /* Erase comments to enable caching $status = $globalweather->setCache("file", array("cache_dir" => "/tmp/cache/")); if (Services_Weather::isError($status)) { echo "Error: ".$status->getMessage()."\n"; } */ $globalweather->setUnitsFormat("metric"); $globalweather->setDateTimeFormat("d.m.Y", "H:i"); // First get code for location $search = $globalweather->searchLocation("Koeln / Bonn"); if (Services_Weather::isError($search)) { die("Error: ".$search->getMessage()."\n"); } // Now iterate through available functions for retrieving data foreach (array("getLocation", "getWeather", "getForecast") as $function) { $data = $globalweather->$function($search); if (Services_Weather::isError($data)) { echo "Error: ".$data->getMessage()."\n"; continue; } var_dump($data); } ?> php-services-weather-1.4.7/Services_Weather-1.4.7/examples/metar-basic.php000066400000000000000000000067761250332203600262310ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version SVN: $Id$ * @link http://pear.php.net/package/Services_Weather * @filesource */ require_once "Services/Weather.php"; // Object initialization - error checking is important, because of // handling exceptions such as missing PEAR modules $metar = &Services_Weather::service("METAR", array("debug" => 0)); if (Services_Weather::isError($metar)) { die("Error: ".$metar->getMessage()."\n"); } // Set parameters for DB access, needed for location searches $metar->setMetarDB("sqlite://localhost//usr/local/lib/php/data/Services_Weather/servicesWeatherDB"); if (Services_Weather::isError($metar)) { echo "Error: ".$metar->getMessage()."\n"; } /* Erase comments to enable caching $status = $metar->setCache("file", array("cache_dir" => "/tmp/cache/")); if (Services_Weather::isError($status)) { echo "Error: ".$status->getMessage()."\n"; } */ $metar->setUnitsFormat("custom", array( "wind" => "kt", "vis" => "km", "height" => "ft", "temp" => "c", "pres" => "hpa", "rain" => "in")); $metar->setDateTimeFormat("d.m.Y", "H:i"); // First get code for location $search = $metar->searchLocation("Bonn, Germany"); if (Services_Weather::isError($search)) { die("Error: ".$search->getMessage()."\n"); } // Now iterate through available functions for retrieving data foreach (array("getLocation", "getWeather", "getForecast") as $function) { $data = $metar->$function($search); if (Services_Weather::isError($data)) { echo "Error: ".$data->getMessage()."\n"; continue; } var_dump($data); } ?> php-services-weather-1.4.7/Services_Weather-1.4.7/examples/metar-extensive.php000066400000000000000000000671031250332203600271510ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version SVN: $Id$ * @link http://pear.php.net/package/Services_Weather * @filesource */ //------------------------------------------------------------------------- // This is the area, where you can customize the script //------------------------------------------------------------------------- $location = "Bonn, Germany"; // The city we want to fetch the data for. // Where the search function will look for // the ICAO database (generated with the // buildMetarDB.php script) $dsn = "sqlite://localhost//usr/local/lib/php/data/Services_Weather/servicesWeatherDB"; $sourceMetar = "http"; // This script will pull the METAR data via http $sourceTaf = "http"; // TAF $sourcePathMetar = ""; // Only needed when non-standard access is used $sourcePathTaf = ""; // $cacheType = ""; // Set a type (file, db, mdb, ...) to // enable caching. $cacheOpt = array(); // Cache needs various options, depending // on the container-type - please consult // the Cache manual / sourcecode! $unitsFormat = "metric"; // The format the units are displayed in - // metric, standard or some customization. $dateFormat = "j. M Y"; // Set the format the date is displayed in $timeFormat = "H:i"; // time //------------------------------------------------------------------------- // Load the Weather class require_once "Services/Weather.php"; // Object initialization - error checking is important, because of // handling exceptions such as missing PEAR modules $metar = &Services_Weather::service("Metar"); if (Services_Weather::isError($metar)) { die("Error: ".$metar->getMessage()."\n"); } // Set parameters for DB access, needed for location searches $metar->setMetarDB($dsn); if (Services_Weather::isError($metar)) { echo "Error: ".$metar->getMessage()."\n"; } // Initialize caching if (strlen($cacheType)) { $status = $metar->setCache($cacheType, $cacheOpt); if (Services_Weather::isError($status)) { echo "Error: ".$status->getMessage()."\n"; } } // Define the units format, bring the retrieved format into // something more common... $metar->setUnitsFormat($unitsFormat); $units = $metar->getUnitsFormat(); $units["temp"] = "°".strtoupper($units["temp"]); $units["wind"] = " ".str_replace("kmh", "km/h", $units["wind"]); $units["vis"] = " ".$units["vis"]; $units["height"] = " ".$units["height"]; $units["pres"] = " ".$units["pres"]; $units["rain"] = " ".$units["rain"]; $metar->setMetarSource($sourceMetar, $sourcePathMetar, $sourceTaf, $sourcePathTaf); // Set date-/time-format $metar->setDateTimeFormat($dateFormat, $timeFormat); // Search for defined location and fetch the first item found. // Bail out if something bad happens... $search = $metar->searchLocation($location, true); if (Services_Weather::isError($search)) { die("Error: ".$search->getMessage()."\n"); } // Retrieve data, store in variables, bail out on error $fetch = array( "location" => "getLocation", "weather" => "getWeather", "forecast" => "getForecast" ); foreach ($fetch as $variable => $function) { $$variable = $metar->$function($search); if (Services_Weather::isError($$variable)) { echo "Error: ".$$variable->getMessage()."\n"; continue; } } // Now we output all the data, please don't expect extensive comments here, this is basic // HTML/CSS stuff. Also this isn't a very fancy design, it's just to show you, what // the script is able to do (and more ;-))... ?> Services_Weather::METAR/TAF \n"; var_dump($location, $weather, $forecast); echo "\n"; } ?> Weather Forecast created with PEARs Services_Weather

Precipitation:

()
Sunrise: Sunrise Sunset: Sunset    
Temperature: Dew point: Felt temperature: Trend:
$val) { switch ($key) { case "type": switch ($val) { case "NOSIG": $string = "No Significant Weather"; break; case "TEMPO": $string = "Temporary Weather"; break; case "BECMG": $string = "Weather Becoming"; break; } $value = ""; foreach (array("from", "to", "at") as $time) { if (isset($trend[$time])) { $value .= " ".$time." ".$trend[$time]; } } ($value != "") ? $value = trim($value).":":""; $string = ''.$string.''; $value = ''.$value.''; break; case "wind": $string = "Wind:"; $value = (strtolower($trend["windDirection"]) == "calm") ? "Calm" : "From the ".$trend["windDirection"]." (".$trend["windDegrees"]."°) at ".round($trend["wind"], 1).$units["wind"]; if (isset($trend["windVariability"])) { $value .= ", variable from ".$trend["windVariability"]["from"]."° to ".$trend["windVariability"]["to"]."°"; } if (isset($trend["windGust"])) { $value .= ", with gusts up to ".round($trend["windGust"], 1).$units["wind"]; } break; case "visibility": $string = "Visibility:"; $value = strtolower($trend["visQualifier"])." ".round($trend["visibility"], 1).$units["vis"]; break; case "clouds": $string = "Clouds:"; $value = ""; for ($i = 0; $i < sizeof($val); $i++) { $cloud = ucwords($val[$i]["amount"]); if (isset($val[$i]["type"])) { $cloud .= " ".$val[$i]["type"]; } if (isset($val[$i]["height"])) { $cloud .= " at ".$val[$i]["height"].$units["height"]; } $value .= $cloud." "; } break; case "condition": $string = "Condition:"; $value = ucwords($val); break; case "pressure": $string = "Pressure:"; $value = round($val, 1).$units["pres"]; break; case "from": case "to": case "at": case "windDirection": case "windDegrees": case "windVariability": case "windGust": case "visQualifier": continue 2; default: $string = ""; $value = ""; var_dump($key, $val); break; } ?>
none
Remarks:
$val) { switch ($key) { case "autostation": case "presschg": case "nospeci": case "sunduration": case "maintain": $string = ""; $value = $val; break; case "seapressure": $string = "Pressure at sealevel:"; $value = round($val, 1).$units["pres"]; break; case "1htemp": $string = "Temperature for last hour:"; $value = round($val, 1).$units["temp"]; break; case "1hdew": $string = "Dew Point for last hour:"; $value = round($val, 1).$units["temp"]; break; case "6hmaxtemp": case "6hmintemp": if (!isset($weather["remark"]["6hmaxtemp"]) && !isset($weather["remark"]["6hmintemp"])) { continue(2); } $string = "Max/Min Temp for last 6 hours:"; $value = (isset($weather["remark"]["6hmaxtemp"])) ? round($weather["remark"]["6hmaxtemp"], 1).$units["temp"] : "-"; $value .= "/"; $value .= (isset($weather["remark"]["6hmintemp"])) ? round($weather["remark"]["6hmintemp"], 1).$units["temp"] : "-"; unset($weather["remark"]["6hmaxtemp"]); unset($weather["remark"]["6hmintemp"]); break; case "24hmaxtemp": case "24hmintemp": if (!isset($weather["remark"]["24hmaxtemp"]) && !isset($weather["remark"]["24hmintemp"])) { continue(2); } $string = "Max/Min Temp for last 24 hours:"; $value = (isset($weather["remark"]["24hmaxtemp"])) ? round($weather["remark"]["24hmaxtemp"], 1).$units["temp"] : "-"; $value .= "/"; $value .= (isset($weather["remark"]["24hmintemp"])) ? round($weather["remark"]["24hmintemp"], 1).$units["temp"] : "-"; unset($weather["remark"]["24hmaxtemp"]); unset($weather["remark"]["24hmintemp"]); break; case "snowdepth": $string = "Snow depth:"; $value = $val.$units["rain"]; break; case "snowequiv": $string = "Water equivalent of snow:"; $value = $val.$units["rain"]; break; case "sensors": $string = ""; $value = implode("
", $val); break; default: $string = ""; $value = ""; var_dump($key, $val); break; } ?>
none
Pressure: Humidity: %
Wind: variable from ".$weather["windVariability"]["from"]."° to ".$weather["windVariability"]["to"]."°" : ""?> with gusts up to ".round($weather["windGust"], 1).$units["wind"] : ""?> Visibility:
Current condition:
<?=isset($weather[" src="images/32x32/.png"> Clouds:

Clear Below convertDistance(12000, "ft", $units["height"]).$units["height"]?>
Forecast (TAF)
valid from to
0, "vis" => 0, "clouds" => 0, "cond" => 0); // Ok, the forecast is a bit more interesting, as I'm taking a few // precautions here so that the table isn't filled up to the max. // o If a value is repeated in the next major timeslot (not when // significant weather changes are processed), it's not printed // o Significant weather gets its own rows, with times printed normal // as in opposition to bold print for major timeslots // o The while($row)-construct below is for handling the significant // weather, as I point $row to $row["fmc"] afterwards, where the // smaller changes are mentioned for ($i = 0; $i < sizeof($forecast["time"]); $i++) { $row = $forecast["time"][$times[$i]]; // Create timestamp $start = $times[$i]; if ($i + 1 < sizeof($forecast["time"])) { $end = $times[$i + 1]; } else { $end = substr($forecast["validRaw"], -2).":00"; $end = ($end == "24:00") ? "00:00" : $end; } $time = $start." - ".$end; $class = ' class="bold"'; // This is for outputting "Becoming", "Temporary" and such $fmc = isset($row["fmc"]) ? $row["fmc"] : false; $fmctype = ""; $fmccnt = 0; while ($row) { ?> at ".round($row["wind"], 1).$units["wind"]; if (isset($row["windProb"])) { $string .= " (".$row["windProb"]."% Prob.)"; } if ($string === $pre["wind"]) { $string = " "; } else { $pre["wind"] = $string; } } $class = ' class="bggrey"'; break; case "vis": if (!isset($row["visibility"])) { $string = " "; } else { $string = strtolower($row["visQualifier"])." ".round($row["visibility"], 1).$units["vis"]; if (isset($row["visProb"])) { $string .= " (".$row["visProb"]."% Prob.)"; } if ($string === $pre["vis"]) { $string = " "; } else { $pre["vis"] = $string; } } $class = ''; break; case "clouds": if (!isset($row["clouds"])) { $string = " "; } else { $clouds = ""; for ($j = 0; $j < sizeof($row["clouds"]); $j++) { $cloud = ucwords($row["clouds"][$j]["amount"]); if (isset($row["clouds"][$j]["type"])) { $cloud .= " ".$row["clouds"][$j]["type"]; } if (isset($row["clouds"][$j]["height"])) { $cloud .= " at ".$row["clouds"][$j]["height"].$units["height"]; } if (isset($row["clouds"][$j]["prob"])) { $cloud .= " (".$row["clouds"][$j]["prob"]."% Prob.)"; } $clouds .= $cloud."
"; } if ($clouds === $pre["clouds"]) { $string = " "; } else { $string = $clouds; $pre["clouds"] = $clouds; } } $class = ' class="bggrey"'; break; case "cond": if (!isset($row["condition"]) || (isset($prerow) && $prerow["condition"] == $row["condition"])) { $string = " "; } else { $string = ucwords($row["condition"]); } $class = ''; } ?>
  Meteorological Conditions Wind Visibility Clouds Condition
> >
  Updated: ( / ) All times UTC
back php-services-weather-1.4.7/Services_Weather-1.4.7/examples/weather.com-basic.php000066400000000000000000000064641250332203600273270ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version SVN: $Id$ * @link http://pear.php.net/package/Services_Weather * @filesource */ require_once "Services/Weather.php"; // Object initialization - error checking is important, because of // handling exceptions such as missing PEAR modules $weatherDotCom = &Services_Weather::service("WeatherDotCom", array("debug" => 2, "httpTimeout" => 30)); if (Services_Weather::isError($weatherDotCom)) { die("Error: ".$weatherDotCom->getMessage()."\n"); } // Set weather.com partner data $weatherDotCom->setAccountData("", ""); /* Erase comments to enable caching $status = $weatherDotCom->setCache("file", array("cache_dir" => "/tmp/cache/")); if (Services_Weather::isError($status)) { echo "Error: ".$status->getMessage()."\n"; } */ $weatherDotCom->setUnitsFormat("metric"); $weatherDotCom->setDateTimeFormat("d.m.Y", "H:i"); // First get code for location $search = $weatherDotCom->searchLocation("Bonn, Germany"); if (Services_Weather::isError($search)) { die("Error: ".$search->getMessage()."\n"); } // Now iterate through available functions for retrieving data foreach (array("getLocation", "getWeather", "getForecast") as $function) { $data = $weatherDotCom->$function($search); if (Services_Weather::isError($data)) { echo "Error: ".$data->getMessage()."\n"; continue; } var_dump($data); } ?> php-services-weather-1.4.7/Services_Weather-1.4.7/examples/weather.com-extensive.php000066400000000000000000000452131250332203600302530ustar00rootroot00000000000000 * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version SVN: $Id$ * @link http://pear.php.net/package/Services_Weather * @filesource */ //------------------------------------------------------------------------- // This is the area, where you can customize the script //------------------------------------------------------------------------- $location = "Bonn, Germany"; // The city we want to fetch the data for. $forecastDays = 4; // This regulates the amount of displayed // dates in the forecast. (1 <= x <= 10) $partnerID = ""; // As provided by weather.com in the $licenseKey = ""; // registration eMail. $cacheType = ""; // Set a type (file, db, mdb, ...) to // enable caching. $cacheOpt = array(); // Cache needs various options, depending // on the container-type - please consult // the Cache manual / sourcecode! $unitsFormat = "metric"; // The format the units are displayed in - // metric, standard or some customization. $dateFormat = "Y-m-d"; // Set the format the date is displayed in // Changing it will break a few things in // this script, but usually you can define // this to your likings. $timeFormat = "H:i"; // Set the format the time is displayed in //------------------------------------------------------------------------- // Load the Weather class require_once "Services/Weather.php"; // Object initialization - error checking is important, because of // handling exceptions such as missing PEAR modules $weatherDotCom = &Services_Weather::service("WeatherDotCom", array("httpTimeout" => 30)); if (Services_Weather::isError($weatherDotCom)) { die("Error: ".$weatherDotCom->getMessage()."\n"); } // Set weather.com partner data $weatherDotCom->setAccountData($partnerID, $licenseKey); // Initialize caching if (strlen($cacheType)) { $status = $weatherDotCom->setCache($cacheType, $cacheOpt); if (Services_Weather::isError($status)) { echo "Error: ".$status->getMessage()."\n"; } } // Define the units format, bring the retrieved format into // something more common... $weatherDotCom->setUnitsFormat($unitsFormat); $units = $weatherDotCom->getUnitsFormat(); $units["temp"] = "°".strtoupper($units["temp"]); $units["wind"] = " ".str_replace("kmh", "km/h", $units["wind"]); $units["vis"] = " ".$units["vis"]; $units["height"] = " ".$units["height"]; $units["pres"] = " ".$units["pres"]; $units["rain"] = " ".$units["rain"]; // Set date-/time-format $weatherDotCom->setDateTimeFormat($dateFormat, $timeFormat); // Search for defined location and fetch the first item found. // Bail out if something bad happens... $search = $weatherDotCom->searchLocation($location, true); if (Services_Weather::isError($search)) { die("Error: ".$search->getMessage()."\n"); } // Retrieve data, store in variables, bail out on error $fetch = array( "links" => "getLinks", "location" => "getLocation", "weather" => "getWeather", "forecast" => "getForecast" ); foreach ($fetch as $variable => $function) { $$variable = $weatherDotCom->$function($search, $forecastDays); if (Services_Weather::isError($$variable)) { echo "Error: ".$$variable->getMessage()."\n"; continue; } } // We need this for some time-checks and displays later $wupd = strtotime($weather["update"]) + date("Z"); $fupd = strtotime($forecast["update"]) + date("Z"); $fup = strtotime($forecast["update"]) + $location["timezone"] * 3600; // Check, if we're in the afternoon and if the forecast was updated yet... // This triggers if the day-forecast for the current day will get shown. $afternoon = ($location["time"] > "13:59" || date("Ymd", $fup) < date("Ymd")) ? true : false; // The usual notation for condition icons is numeric. Check for numeric icon or "na" and set to "na" if that's not the case if (!(ctype_digit($weather["conditionIcon"]) || $weather["conditionIcon"] == "na")) { $weather["conditionIcon"] = "na"; } // Now we output all the data, please don't expect extensive comments here, this is basic // HTML/CSS stuff. Also this isn't a very fancy design, it's just to show you, what // the script is able to do (and more ;-))... ?> Services_Weather::Weatherdotcom \n"; var_dump($links, $location, $weather, $forecast); echo "\n"; } ?> Weather Forecast created with PEARs Services_Weather
Local time: (GMT)  
Sunrise: Sunrise Sunset: Sunset
Featured on weather.com®
">
Temperature: Dew point: Felt temperature:
Pressure: and Humidity: %
Wind: Visibility:
Current condition:
<?=$weather[" src="images/32x32/.png"> UV-Index: ()
-day forecast
 
Temperature High / Low
 
Condition
Precipitation probability
Wind
Humidity
>
'.round($forecast["days"][$day]["temperatureHigh"], 0).$units["temp"].' / '?>
Day'?> Night
'?>
<?=$forecast[" src="images/32x32/.png">
%
from 
%
  Updated: ( / ) Weather data provided by weather.com®weather.com(R) logo
back php-services-weather-1.4.7/Services_Weather-1.4.7/images/000077500000000000000000000000001250332203600227405ustar00rootroot00000000000000php-services-weather-1.4.7/Services_Weather-1.4.7/images/sunrise.gif000066400000000000000000000004411250332203600251160ustar00rootroot00000000000000GIF89a 찝u\T$ Y4sO(Ʒ:~A뾐y3!, `$di&P iD"@tF"(^:`vj*#xmQ]axF`.h۷HBmZ r 0l %K  = \" Yy"W2  "mN+@1(!;php-services-weather-1.4.7/Services_Weather-1.4.7/images/sunset.gif000066400000000000000000000004341250332203600247510ustar00rootroot00000000000000GIF89aVaGz$n<n8 մcK< ~Ғ3!,`$dih#˞G!"( A9@!'!  HFvO3ΖH,x,t4#V49 â V2%I }}pGV&>y 0N  #N#  .3"!;php-services-weather-1.4.7/package.xml000066400000000000000000000147241250332203600177110ustar00rootroot00000000000000 Services_Weather pear.php.net This class acts as an interface to various online weather-services. Services_Weather searches for given locations and retrieves current weather data and, dependent on the used service, also forecasts. Up to now, GlobalWeather from CapeScience, Weather XML from EJSE (US only), a XOAP service from Weather.com and METAR/TAF from NOAA are supported. Further services will get included, if they become available, have a usable API and are properly documented. Alexander Wirtz eru alex@pc4p.net yes 2012-10-22 1.4.7 1.4.5 stable stable BSD License QA Release Bug #19228 Fatal error: Cannot use object of type PEAR_Error Bug #19213 Undefined array index in Metar.php 4.0.0 6.0.0 1.4.0 HTTP_Request pear.php.net 1.2.0 ctype pcre Net_FTP pear.php.net 1.3.1 Cache pear.php.net 1.5.3 DB pear.php.net 1.4.0 SOAP pear.php.net 0.7.5 XML_Serializer pear.php.net 0.8 1.4.6 1.4.5 stable stable 2011-12-11 BSD License QA Release Bug #16801 Wrong temperature calculation on current day eru Bug #16802 Location used without being processed eru Request # 16803 Caching without cache eru Bug #16899 Pressure conversion from inHg to hPa calculated wrong eru Bug #16928 getWeatherIcon() vs. grains eru Request #17856 getWeatherIcon() / time issue eru Request #18502 Moon data not avaolable eru Request #18503 Use of Cache_Lite eru 1.4.7 1.4.5 stable stable 2012-10-22 BSD License QA Release Bug #19228 Fatal error: Cannot use object of type PEAR_Error Bug #19213 Undefined array index in Metar.php