/** * 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; }
/** * 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; } }
/** * 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->_cache->get("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 $expire = constant("SERVICES_WEATHER_EXPIRES_FORECAST"); $this->_cache->extSave("METAR-" . $id, $forecastReturn, $unitsFormat, $expire, "forecast"); } $this->_forecast = $forecastReturn; $forecastReturn["cache"] = "MISS"; } $this->_convertReturn($forecastReturn, $units, $location); return $forecastReturn; }
/** * 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; }
/** * 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 <kekabloom[at]ucdavis[dot]edu> * for the zmanim project, converted to C by Moshe Doron <mosdoron[at]netvision[dot]net[dot]il> * 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 = (double) $latitude; } if ($longitude === null) { $longitude = SUNFUNCS_DEFAULT_LONGITUDE; } else { $longitude = (double) $longitude; } if ($zenith === null) { if ($sunrise) { $zenith = SUNFUNCS_SUNRISE_ZENITH; } else { $zenith = SUNFUNCS_SUNSET_ZENITH; } } else { $zenith = (double) $zenith; } // Default value for GMT offset if ($gmt_offset === null) { $gmt_offset = date("Z", $date) / 3600; } else { $gmt_offset = (double) $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.02 * 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__); } }
/** * 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; }
/** * 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()) { // The error handling in Cache is a bit crummy (read: not existent) // so we have to do that on our own... @(include_once "Cache.php"); @($cache = new Cache($cacheType, $cacheOptions)); if (is_object($cache) && (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; }
/** * 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->_cache->get("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 $expire = constant("SERVICES_WEATHER_EXPIRES_LOCATION"); $this->_cache->extSave("METAR-" . $id, $this->_location, "", $expire, "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["elevation"] = $this->_location["elevation"]; return $locationReturn; }
/** * 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) { // Get search data from server and unserialize $searchURL = "http://xoap.weather.com/search/search?where=" . urlencode(trim($location)); $status = $this->_unserializer->unserialize($searchURL, true, array("overrideOptions" => true, "complexType" => "array", "keyAttribute" => "id")); if (Services_Weather::isError($status)) { return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__); } else { $search = $this->_unserializer->getUnserializedData(); if (Services_Weather::isError($search)) { 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__); } else { if (!$useFirst && sizeof($search) > 1) { $searchReturn = $search; } elseif ($useFirst || sizeof($search) == 1) { $searchReturn = key($search); } } } return $searchReturn; }