* @category Horde
* @package Service_Weather
*/
abstract class Horde_Service_Weather_Forecast_Base implements IteratorAggregate
{
/**
* The forecast properties as returned from the forecast request.
*
* @var array
*/
protected $_properties = array();
/**
* Local cache of forecast periods
*
* @var array
*/
protected $_periods = array();
/**
* Forecast type
*
* @var integer A Horde_Service_Weather::FORECAST_TYPE_* constant.
*/
protected $_type;
/**
* Maximum forecast length to return. Defaults to sufficiently high number
* to ensure all available days returned by default.
*/
protected $_maxDays = 20;
/**
* Parent Weather driver.
*
* @var Horde_Service_Weather_Base
*/
public $weather;
/**
* Array of supported "detailed" forecast fields. To be populated by
* concrete classes.
*
* @var array
*/
public $fields = array();
/**
* Advertise how detailed the forecast period is.
*
* FORECAST_TYPE_STANDARD - Each Period represents a full day
* FORECAST_TYPE_DETAILED - Each period represents either day or night.
* FORECAST_TYPE_HOURLY - Each period represents a single hour.
*
*
* @var integer
*/
public $detail = Horde_Service_Weather::FORECAST_TYPE_STANDARD;
/**
* Const'r
*
* @param array $properties Forecast properties.
* @param Horde_Service_Weather_base $weather The base driver.
* @param integer $type The forecast type.
*/
public function __construct(
$properties,
Horde_Service_Weather_Base $weather,
$type = Horde_Service_Weather::FORECAST_TYPE_STANDARD)
{
$this->_properties = $properties;
$this->weather = $weather;
$this->_type = $type;
}
/**
* Return the forecast for the specified ordinal day.
*
* @param integer $day The forecast day to return.
*
* @return Horde_Service_Weather_Period_Base
*/
public function getForecastDay($day)
{
return $this->_periods[$day];
}
/**
* Limit the returned number of forecast days. Used for emulating a smaller
* forecast length than the provider supports or for using one, longer
* request to supply two different forecast length requests.
*
* @param integer $days The number of days to return.
*/
public function limitLength($days)
{
$this->_maxDays = $days;
}
/**
* Return the time of the forecast, in local (to station) time.
*
* @return Horde_Date The time of the forecast.
*/
abstract public function getForecastTime();
/**
* Return an ArrayIterator
*
* @return ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator(array_slice($this->_periods, 0, $this->_maxDays));
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Forecast/Owm.php 0000664 0001750 0001750 00000003621 13055003005 023646 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Forecast_Wwo
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Forecast_Owm extends Horde_Service_Weather_Forecast_Base
{
/**
* @see Horde_Service_Weather_Forecast_Base::$fields
*/
public $fields = array(Horde_Service_Weather::FORECAST_FIELD_WIND);
/**
* Const'r
*
* @see Horde_Service_Weather_Forecast_Base::__construct()
*/
public function __construct(
$properties,
Horde_Service_Weather_Base $weather,
$type = Horde_Service_Weather::FORECAST_TYPE_STANDARD)
{
parent::__construct($properties, $weather, $type);
$this->_parseStd();
}
/**
* Return the forecast time. Note that Wwo doesn't provide the validity time
* for the forecast, so we use the last known station time. File this under
* the "good enough" file.
*
* @see Horde_Service_Weather_Forecast_Base::getForecastTime()
*/
public function getForecastTime()
{
return new Horde_Date($this->weather->getStation()->time);
}
/**
* Parse a stdRequest
*
* @throws Horde_Service_Weather_Exception
*/
protected function _parseStd()
{
if (empty($this->_properties)) {
throw new Horde_Service_Weather_Exception('No forecast data to parse.');
}
foreach ($this->_properties as $values) {
$this->_periods[] = new Horde_Service_Weather_Period_Owm($values, $this);
}
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Forecast/Taf.php 0000664 0001750 0001750 00000010367 13055003005 023623 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Forecast_Taf
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Forecast_Taf extends Horde_Service_Weather_Forecast_Base
{
/**
* Const'r
*
* @param array $properties Forecast properties.
* @param Horde_Service_Weather_base $weather The base driver.
* @param integer $type The forecast type.
*/
public function __construct(
$properties,
Horde_Service_Weather_Base $weather,
$type = Horde_Service_Weather::FORECAST_TYPE_STANDARD)
{
parent::__construct($properties, $weather, $type);
$this->_parsePeriods();
}
/**
* Compatibility layer for old PEAR/Services_Weather data.
*
* @return array The raw parsed data array - keyed by descriptors that are
* compatible with PEAR/Services_Weather. Structure of data:
* Data is converted into the appropriate units based on
* the Horde_Service_Weather_Base::units setting at the time
* or parsing.
*
* - station: The station identifier.
* - dataRaw: The raw TAF data.
* - update: The update timestamp.
* - updateRaw: The raw TAF encoded update time.
* - validRaw: The raw TAF encoded valid forecast times.
* - validFrom: The valid forecast FROM time.
* - validTo: The valid forecast TO time.
* - time: Array containing an entry for each weather section.
* Basically each entry contains forcasted changes
* beginning at the time of the key to the entry.
* - wind: The wind speed.
* - windDegrees: The wind direction in degrees.
* - windDirection: The wind direction in a cardinal compass direction.
* - windGust: The wind gust speed.
* - windProb: Probability of forecast wind.
* - visibility: Visibility distance.
* - visQualifier: Qualifier of visibility. I.e., "AT", "BEYOND", "BELOW"
* - visProb: Probability of forecast visibility.
* - clouds: Array containing cloud layer information:
* - amount: Amount of sky cover. I.e., "BROKEN", "OVERCAST"
* - height: The height of the base of the cloud layer.
* - type: The type of clouds if available.
* - condition: The weather condition. I.e., "RAIN", "MIST"
* - windshear: Windshear delta.
* - windshearHeight: The height of windshear.
* - windshearDegrees: The degrees of windshear.
* - windshearDirection:The compass direction of windshear.
* - temperatureLow: The forecast low temperature.
* - temperatureHigh: The forecast high temperature.
* - fmc: Array containing any FMC changes. I.e, "TEMPO", or
* "BECMG" lines.
* - from: Horde_Date representing the starting time of the
* FMC change.
* - to: Horde_Date representing the ending time of the FMC
* period.
*/
public function getRawData()
{
return $this->_properties;
}
/**
* Return the time of the forecast, in local (to station) time.
*
* @return Horde_Date The time of the forecast.
*/
public function getForecastTime()
{
return new Horde_Date($this->_properties['update'], 'GMT');
}
protected function _parsePeriods()
{
foreach ($this->_properties['time'] as $time => $data) {
$data['period'] = $time;
$this->_periods[] = new Horde_Service_Weather_Period_Taf($data, $this);
}
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Forecast/WeatherUnderground.php 0000664 0001750 0001750 00000004544 13055003005 026725 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Forecast_WeatherUnderground
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Forecast_WeatherUnderground extends Horde_Service_Weather_Forecast_Base
{
/**
* @see Horde_Service_Weather_Forecast_Base::$fields
*/
public $fields = array(
Horde_Service_Weather::FORECAST_FIELD_WIND,
Horde_Service_Weather::FORECAST_FIELD_HUMIDITY,
Horde_Service_Weather::FORECAST_FIELD_PRECIPITATION);
/**
* Const'r
*
* @see Horde_Service_Weather_Forecast_Base::__construct()
*/
public function __construct(
$properties,
Horde_Service_Weather_Base $weather,
$type = Horde_Service_Weather::FORECAST_TYPE_STANDARD)
{
parent::__construct($properties, $weather, $type);
switch ($type) {
case Horde_Service_Weather::FORECAST_TYPE_STANDARD:
$this->_parseStd();
break;
case Horde_Service_Weather::FORECAST_TYPE_HOURLY:
$this->_parseHourly();
}
}
/**
* Return the forecast time.
*
* @see Horde_Service_Weather_Forecast_Base::getForecastTime()
*/
public function getForecastTime()
{
return new Horde_Date($this->_properties['txt_forecast']->date);
}
/**
* Parse the Std forecast data
*
* @throws Horde_Service_Weather_Exception
*/
protected function _parseStd()
{
if (empty($this->_properties)) {
throw new Horde_Service_Weather_Exception('No forecast data to parse.');
}
foreach ($this->_properties['simpleforecast']->forecastday as $period => $values) {
$this->_periods[] = new Horde_Service_Weather_Period_WeatherUnderground((array)$values, $this);
}
}
// @TODO: Use the hourly forecast to maybe get a "day"/"night"
protected function _parseHourly()
{
throw Horde_Exception('Not currently implemented.');
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Forecast/Wwo.php 0000664 0001750 0001750 00000003672 13055003005 023666 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Forecast_Wwo
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Forecast_Wwo extends Horde_Service_Weather_Forecast_Base
{
/**
* @see Horde_Service_Weather_Forecast_Base::$fields
*/
public $fields = array(Horde_Service_Weather::FORECAST_FIELD_WIND);
/**
* Const'r
*
* @see Horde_Service_Weather_Forecast_Base::__construct()
*/
public function __construct(
$properties,
Horde_Service_Weather_Base $weather,
$type = Horde_Service_Weather::FORECAST_TYPE_STANDARD)
{
parent::__construct($properties, $weather, $type);
$this->_parseStd();
}
/**
* Return the forecast time. Note that Wwo doesn't provide the validity time
* for the forecast, so we use the last known station time. File this under
* the "good enough" file.
*
* @see Horde_Service_Weather_Forecast_Base::getForecastTime()
*/
public function getForecastTime()
{
return new Horde_Date($this->weather->getStation()->time);
}
/**
* Parse a stdRequest
*
* @throws Horde_Service_Weather_Exception
*/
protected function _parseStd()
{
if (empty($this->_properties)) {
throw new Horde_Service_Weather_Exception('No forecast data to parse.');
}
foreach ($this->_properties as $period => $values) {
$period = new Horde_Service_Weather_Period_Wwo($values, $this);
$this->_periods[] = $period;
}
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Forecast/Wwov2.php 0000664 0001750 0001750 00000003266 13055003005 024135 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Forecast_Wwo
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Forecast_Wwov2 extends Horde_Service_Weather_Forecast_Wwo
{
/**
* @see Horde_Service_Weather_Forecast_Base::$fields
*/
public $fields = array(
Horde_Service_Weather::FORECAST_FIELD_WIND,
Horde_Service_Weather::FORECAST_FIELD_HUMIDITY,
Horde_Service_Weather::FORECAST_FIELD_PRECIPITATION);
/**
* Parse a stdRequest
*
* @throws Horde_Service_Weather_Exception
*/
protected function _parseStd()
{
if (empty($this->_properties)) {
throw new Horde_Service_Weather_Exception('No forecast data to parse.');
}
// @todo Need to refactor this when we support hourly data.
foreach ($this->_properties as $period => $values) {
$data = new stdClass();
foreach ($values as $k => $v) {
if ($k != 'hourly') {
$data->{$k} = $v;
}
}
foreach ($values->hourly[0] as $k => $v) {
$data->{$k} = $v;
}
$period = new Horde_Service_Weather_Period_Wwov2($data, $this);
$this->_periods[] = $period;
}
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Parser/Base.php 0000664 0001750 0001750 00000023536 13055003005 023453 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Parser_Base
*
* Base class for parsing TAF/METAR data.
*
* Parsing code adapted from PEAR's Services_Weather_Metar class. Original
* phpdoc attributes as follows:
* @author Alexander Wirtz
* @copyright 2005-2011 Alexander Wirtz
* @link http://pear.php.net/package/Services_Weather
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
abstract class Horde_Service_Weather_Parser_Base
{
const UNIT_KEY_TEMP = 'temp';
const UNIT_KEY_SPEED = 'speed';
const UNIT_KEY_PRESSURE = 'pressure';
const UNIT_KEY_DISTANCE = 'distance';
/**
* The type of units to convert to.
*
* @var integer A Horde_Service_Weather::UNITS_* constant.
*/
protected $_units;
/**
* Mapping of what units to use for each type of value.
* Built using self::_units
*
* @var array
*/
protected $_unitMap;
/**
* Cloud cover code map.
*
* @var array
*/
protected $_clouds = array();
/**
* CloudType map
*
* @var array
*/
protected $_cloudTypes = array();
/**
* Conditions map
*
* @var array
*/
protected $_conditions = array();
/**
* Sensors map
*
* @var array
*/
protected $_sensors = array();
/**
* constructor
*
* @param array $params Parameter array:
* - units: (integer) The Horde_Service_Weather::UNITS_* constant.
*/
public function __construct(array $params = array())
{
$this->_units = $params['units'];
$this->_unitMap = array(
self::UNIT_KEY_TEMP => $this->_units == Horde_Service_Weather::UNITS_METRIC ? 'c' : 'f',
self::UNIT_KEY_SPEED => $this->_units == Horde_Service_Weather::UNITS_METRIC ? 'kph' : 'mph',
self::UNIT_KEY_PRESSURE => $this->_units == Horde_Service_Weather::UNITS_METRIC ? 'mb' : 'in',
self::UNIT_KEY_DISTANCE => $this->_units == Horde_Service_Weather::UNITS_METRIC ? 'km' : 'sm'
);
$this->_conditions = array(
'+' => Horde_Service_Weather_Translation::t('heavy'),
'-' => Horde_Service_Weather_Translation::t('light'),
'vc' => Horde_Service_Weather_Translation::t('vicinity'),
're' => Horde_Service_Weather_Translation::t('recent'),
'nsw' => Horde_Service_Weather_Translation::t('no significant weather'),
'mi' => Horde_Service_Weather_Translation::t('shallow'),
'bc' => Horde_Service_Weather_Translation::t('patches'),
'pr' => Horde_Service_Weather_Translation::t('partial'),
'ts' => Horde_Service_Weather_Translation::t('thunderstorm'),
'bl' => Horde_Service_Weather_Translation::t('blowing'),
'sh' => Horde_Service_Weather_Translation::t('showers'),
'dr' => Horde_Service_Weather_Translation::t('low drifting'),
'fz' => Horde_Service_Weather_Translation::t('freezing'),
'dz' => Horde_Service_Weather_Translation::t('drizzle'),
'ra' => Horde_Service_Weather_Translation::t('rain'),
'sn' => Horde_Service_Weather_Translation::t('snow'),
'sg' => Horde_Service_Weather_Translation::t('snow grains'),
'ic' => Horde_Service_Weather_Translation::t('ice crystals'),
'pe' => Horde_Service_Weather_Translation::t('ice pellets'),
'pl' => Horde_Service_Weather_Translation::t('ice pellets'),
'gr' => Horde_Service_Weather_Translation::t('hail'),
'gs' => Horde_Service_Weather_Translation::t('small hail/snow pellets'),
'up' => Horde_Service_Weather_Translation::t('unknown precipitation'),
'br' => Horde_Service_Weather_Translation::t('mist'),
'fg' => Horde_Service_Weather_Translation::t('fog'),
'fu' => Horde_Service_Weather_Translation::t('smoke'),
'va' => Horde_Service_Weather_Translation::t('volcanic ash'),
'sa' => Horde_Service_Weather_Translation::t('sand'),
'hz' => Horde_Service_Weather_Translation::t('haze'),
'py' => Horde_Service_Weather_Translation::t('spray'),
'du' => Horde_Service_Weather_Translation::t('widespread dust'),
'sq' => Horde_Service_Weather_Translation::t('squall'),
'ss' => Horde_Service_Weather_Translation::t('sandstorm'),
'ds' => Horde_Service_Weather_Translation::t('duststorm'),
'po' => Horde_Service_Weather_Translation::t('well developed dust/sand whirls'),
'fc' => Horde_Service_Weather_Translation::t('funnel cloud'),
'+fc' => Horde_Service_Weather_Translation::t('tornado/waterspout')
);
$this->_clouds = array(
'skc' => Horde_Service_Weather_Translation::t('sky clear'),
'nsc' => Horde_Service_Weather_Translation::t('no significant cloud'),
'few' => Horde_Service_Weather_Translation::t('few'),
'sct' => Horde_Service_Weather_Translation::t('scattered'),
'bkn' => Horde_Service_Weather_Translation::t('broken'),
'ovc' => Horde_Service_Weather_Translation::t('overcast'),
'vv' => Horde_Service_Weather_Translation::t('vertical visibility'),
'tcu' => Horde_Service_Weather_Translation::t('Towering Cumulus'),
'cb' => Horde_Service_Weather_Translation::t('Cumulonimbus'),
'clr' => Horde_Service_Weather_Translation::t('clear below 12,000 ft')
);
$this->_cloudTypes = array(
'low' => array(
'/' => Horde_Service_Weather_Translation::t('Overcast'),
'0' => Horde_Service_Weather_Translation::t('None'),
'1' => Horde_Service_Weather_Translation::t('Cumulus (fair weather)'),
'2' => Horde_Service_Weather_Translation::t('Cumulus (towering)'),
'3' => Horde_Service_Weather_Translation::t('Cumulonimbus (no anvil)'),
'4' => Horde_Service_Weather_Translation::t('Stratocumulus (from Cumulus)'),
'5' => Horde_Service_Weather_Translation::t('Stratocumulus (not Cumulus)'),
'6' => Horde_Service_Weather_Translation::t('Stratus or Fractostratus (fair)'),
'7' => Horde_Service_Weather_Translation::t('Fractocumulus/Fractostratus (bad weather)'),
'8' => Horde_Service_Weather_Translation::t('Cumulus and Stratocumulus'),
'9' => Horde_Service_Weather_Translation::t('Cumulonimbus (thunderstorm)')
),
'middle' => array(
'/' => Horde_Service_Weather_Translation::t('Overcast'),
'0' => Horde_Service_Weather_Translation::t('None'),
'1' => Horde_Service_Weather_Translation::t('Altostratus (thin)'),
'2' => Horde_Service_Weather_Translation::t('Altostratus (thick)'),
'3' => Horde_Service_Weather_Translation::t('Altocumulus (thin)'),
'4' => Horde_Service_Weather_Translation::t('Altocumulus (patchy)'),
'5' => Horde_Service_Weather_Translation::t('Altocumulus (thickening)'),
'6' => Horde_Service_Weather_Translation::t('Altocumulus (from Cumulus)'),
'7' => Horde_Service_Weather_Translation::t('Altocumulus (w/ Altocumulus, Altostratus, Nimbostratus)'),
'8' => Horde_Service_Weather_Translation::t('Altocumulus (w/ turrets)'),
'9' => Horde_Service_Weather_Translation::t('Altocumulus (chaotic)')
),
'high' => array(
'/' => Horde_Service_Weather_Translation::t('Overcast'),
'0' => Horde_Service_Weather_Translation::t('None'),
'1' => Horde_Service_Weather_Translation::t('Cirrus (filaments)'),
'2' => Horde_Service_Weather_Translation::t('Cirrus (dense)'),
'3' => Horde_Service_Weather_Translation::t('Cirrus (often w/ Cumulonimbus)'),
'4' => Horde_Service_Weather_Translation::t('Cirrus (thickening)'),
'5' => Horde_Service_Weather_Translation::t('Cirrus/Cirrostratus (low in sky)'),
'6' => Horde_Service_Weather_Translation::t('Cirrus/Cirrostratus (high in sky)'),
'7' => Horde_Service_Weather_Translation::t('Cirrostratus (entire sky)'),
'8' => Horde_Service_Weather_Translation::t('Cirrostratus (partial)'),
'9' => Horde_Service_Weather_Translation::t('Cirrocumulus or Cirrocumulus/Cirrus/Cirrostratus')
)
);
$this->_sensors = array(
'rvrno' => Horde_Service_Weather_Translation::t('Runway Visual Range Detector offline'),
'pwino' => Horde_Service_Weather_Translation::t('Present Weather Identifier offline'),
'pno' => Horde_Service_Weather_Translation::t('Tipping Bucket Rain Gauge offline'),
'fzrano' => Horde_Service_Weather_Translation::t('Freezing Rain Sensor offline'),
'tsno' => Horde_Service_Weather_Translation::t('Lightning Detection System offline'),
'visno' => Horde_Service_Weather_Translation::t('2nd Visibility Sensor offline'),
'chino' => Horde_Service_Weather_Translation::t('2nd Ceiling Height Indicator offline')
);
}
/**
* Parse the raw data.
*
* @param string $data The raw TAF or METAR data.
*
* @return array The parsed data array.
*/
public function parse($data)
{
return $this->_parse(preg_split('/\n|\r\n|\n\r/', $data));
}
abstract protected function _parse(array $data);
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Parser/Metar.php 0000664 0001750 0001750 00000073224 13055003005 023650 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Parser_Metar
*
* Responsible for parsing encoded METAR data.
*
* Parsing code adapted from PEAR's Services_Weather_Metar class. Original
* phpdoc attributes as follows:
* @author Alexander Wirtz
* @copyright 2005-2011 Alexander Wirtz
* @link http://pear.php.net/package/Services_Weather
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Parser_Metar extends Horde_Service_Weather_Parser_Base
{
/**
* Map of hours descriptors.
*
* @var array
*/
protected $_hours = array(
'P' => '1',
'6' => '3/6',
'7' => '24'
);
/**
* Parses METAR
*
* @param array $data An array of METAR data lines.
*
* @return array An array of weather data. Possible keys include:
* - station:
* - dataRaw:
* - update:
* - updateRaw:
* - wind:
* - windDegrees:
* - windDirection:
* - windGust:
* - windVariability:
* - visibility:
* - visQualifier:
* - clouds:
* - amount
* - height
* - type
* - temperature
* - dewpoint
* - humidity
* - felttemperature
* - pressure
* - trend
* - type
* - from
* - to
* - at
* - remark
* - autostation
* - seapressure
* - presschg
* - snowdepth
* - snowequiv
* - cloudtypes
* - sunduration
* - 1hrtemp
* - 1hrdew
* - 6hmaxtemp
* - 6hmintemp
* - 24hmaxtemp
* - 24hmintemp
* - 3hpresstrend
* - nospeci
* - sensors
* - maintain
* - precipitation
* - amount
* - hours
*/
protected function _parse(array $data)
{
// 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]);
if (empty($weatherData['update'])) {
throw new Horde_Service_Weather_Exception('Unable to parse data.');
}
// and prepare the rest for stepping through
array_shift($data);
$metar = explode(' ', preg_replace('/\s{2,}/', ' ', implode(' ', $data)));
// Trend handling
$trendCount = 0;
// Pointer to the array we add the data to. Needed for handling trends.
$pointer = &$weatherData;
// Load the metar codes for this go around.
$metarCode = $this->_getMetarCodes();
for ($i = 0; $i < sizeof($metar); $i++) {
$metar[$i] = trim($metar[$i]);
if (!strlen($metar[$i])) {
continue;
}
$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':
$pointer['wind'] = round(Horde_Service_Weather::convertSpeed(
$result[2],
$result[5],
$this->_unitMap[self::UNIT_KEY_SPEED]
));
$wind_mph = Horde_Service_Weather::convertSpeed(
$result[2],
$result[5],
'mph',
$this->_unitMap[self::UNIT_KEY_SPEED]
);
if ($result[1] == 'VAR' || $result[1] == 'VRB') {
// Variable winds
$pointer['windDegrees'] = round(Horde_Service_Weather_Translation::t('Variable'));
$pointer['windDirection'] = Horde_Service_Weather_Translation::t('Variable');
} else {
// Save wind degree and calc direction
$pointer['windDegrees'] = intval($result[1]);
$pointer['windDirection'] = Horde_Service_Weather::degToDirection($result[1]);
}
if (is_numeric($result[4])) {
// Wind with gusts...
$pointer['windGust'] = round(Horde_Service_Weather::convertSpeed(
$result[4],
$result[5],
$this->_unitMap[self::UNIT_KEY_SPEED]
));
}
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.
$found = false;
break;
} else {
// Match. Hand over result and advance METAR
$key = 'visibility';
$result = $resultVF;
$i++;
}
case 'visibility':
$pointer['visQualifier'] = Horde_Service_Weather_Translation::t('AT');
if (is_numeric($result[1]) && ($result[1] == 9999)) {
// Upper limit of visibility range is 10KM.
$visibility = Horde_Service_Weather::convertDistance(
10,
'km',
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
$pointer['visQualifier'] = Horde_Service_Weather_Translation::t('BEYOND');
} elseif (is_numeric($result[1])) {
// 4-digit visibility in m
$visibility = Horde_Service_Weather::convertDistance(
$result[1],
'm',
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
} elseif (!isset($result[11]) || $result[11] != 'CAVOK') {
if ($result[3] == 'M') {
$pointer['visQualifier'] = Horde_Service_Weather_Translation::t('BELOW');
} elseif ($result[3] == 'P') {
$pointer['visQualifier'] = Horde_Service_Weather_Translation::t('BEYOND');
}
if (is_numeric($result[5])) {
// visibility as one/two-digit number
$visibility = Horde_Service_Weather::convertDistance(
$result[5],
$result[10],
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
} else {
// the y/z part, add if we had a x part (see visibility1)
if (is_numeric($result[7])) {
$visibility = Horde_Service_Weather::convertDistance(
$result[7] + $result[8] / $result[9],
$result[10],
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
} else {
$visibility = Horde_Service_Weather::convertDistance(
$result[8] / $result[9],
$result[10],
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
}
}
} else {
$pointer['visQualifier'] = Horde_Service_Weather_Translation::t('BEYOND');
$visibility = Horde_Service_Weather::convertDistance(
10,
'km',
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
$pointer['clouds'] = array(array(
'amount' => Horde_Service_Weather_Translation::t('Clear below'),
'height' => 5000)
);
$pointer['condition'] = Horde_Service_Weather_Translation::t('no significant weather');
}
$pointer['visibility'] = $visibility;
break;
case 'condition':
if (!isset($pointer['condition'])) {
$pointer['condition'] = '';
} elseif (strlen($pointer['condition']) > 0) {
$pointer['condition'] .= ',';
}
if (in_array(strtolower($result[0]), $this->_conditions)) {
// First try matching the complete string
$pointer['condition'] .= ' ' . $this->_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'] .= ' ' . $this->_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' => $this->_clouds[strtolower($result[3])]);
if ($result[4] == '///') {
$cloud['height'] = Horde_Service_Weather_Translation::t('station level or below');
} else {
$cloud['height'] = $result[4] * 100;
}
} elseif (sizeof($result) == 6) {
// Amount, height and type
$cloud = array(
'amount' => $this->_clouds[strtolower($result[3])],
'type' => $this->_clouds[strtolower($result[5])]
);
if ($result[4] == '///') {
$cloud['height'] = Horde_Service_Weather_Translation::t('station level or below');
} else {
$cloud['height'] = $result[4] * 100;
}
} else {
// SKC or CLR or NSC
$cloud = array('amount' => $this->_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'] = round(Horde_Service_Weather::convertTemperature(
$result[2],
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
));
$temp_f = Horde_Service_Weather::convertTemperature($result[2], 'c', 'f');
if (sizeof($result) > 4) {
// same for dewpoint
if ($result[4] == 'M') {
$result[5] *= -1;
}
$pointer['dewPoint'] = round(Horde_Service_Weather::convertTemperature(
$result[5],
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
));
$pointer['humidity'] = round(Horde_Service_Weather::calculateHumidity($result[2], $result[5])) . '%';
}
if (isset($pointer['wind'])) {
// Now calculate windchill from temperature and windspeed
// Note these must be in F and MPH.
$pointer['feltTemperature'] = round(Horde_Service_Weather::convertTemperature(
Horde_Service_Weather::calculateWindChill($temp_f, $wind_mph),
'f',
$this->_unitMap[self::UNIT_KEY_TEMP]
));
}
break;
case 'pressure':
if ($result[1] == 'A') {
// Pressure provided in inches
$pointer['pressure'] = round(Horde_Service_Weather::convertPressure(
$result[2] / 100,
'in',
$this->_unitMap[self::UNIT_KEY_PRESSURE]
), 2);
} elseif ($result[3] == 'Q') {
// ... in hectopascal
$pointer['pressure'] = round(Horde_Service_Weather::convertPressure(
$result[4],
'hpa',
$this->_unitMap[self::UNIT_KEY_PRESSURE]
), 2);
}
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 = $this->_getRemarks();
$weatherData['remark'] = array();
break;
case 'autostation':
// Which autostation do we have here?
if ($result[1] == 0) {
$weatherData['remark']['autostation'] = Horde_Service_Weather_Translation::t('Automatic weatherstation w/o precipitation discriminator');
} else {
$weatherData['remark']['autostation'] = Horde_Service_Weather_Translation::t('Automatic weatherstation w/ precipitation discriminator');
}
unset($metarCode['autostation']);
break;
case 'presschg':
// Decoding for rapid pressure changes
if (strtolower($result[1]) == 'r') {
$weatherData['remark']['presschg'] = Horde_Service_Weather_Translation::t('Pressure rising rapidly');
} else {
$weatherData['remark']['presschg'] = Horde_Service_Weather_Translation::t('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'] = Horde_Service_Weather::convertPressure(
$press,
'hpa',
$this->_unitMap[self::UNIT_KEY_PRESSURE]
);
}
unset($metarCode['seapressure']);
break;
case 'precip':
// Precipitation in inches
if (!isset($weatherData['precipitation'])) {
$weatherData['precipitation'] = array();
}
if (!is_numeric($result[2])) {
$precip = 'indeterminable';
} elseif ($result[2] == '0000') {
$precip = 'traceable';
} else {
$precip = $result[2] / 100;
}
$weatherData['precipitation'][] = array(
'amount' => $precip,
'hours' => $this->_hours[$result[1]]
);
break;
case 'snowdepth':
// Snow depth in inches
// @todo convert to metric
$weatherData['remark']['snowdepth'] = $result[1];
unset($metarCode['snowdepth']);
break;
case 'snowequiv':
// Same for equivalent in Water... (inches)
// @todo convert
$weatherData['remark']['snowequiv'] = $result[1] / 10;
unset($metarCode['snowequiv']);
break;
case 'cloudtypes':
// Cloud types
$weatherData['remark']['cloudtypes'] = array(
'low' => $this->_cloudTypes['low'][$result[1]],
'middle' => $this->_cloudTypes['middle'][$result[2]],
'high' => $this->_cloudTypes['high'][$result[3]]
);
unset($metarCode['cloudtypes']);
break;
case 'sunduration':
// Duration of sunshine (in minutes)
$weatherData['remark']['sunduration'] = sprintf(
Horde_Service_Weather_Translation::t('Total minutes of sunshine: %s'),
$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'] = Horde_Service_Weather::convertTemperature(
$result[2] / 10,
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
);
if (sizeof($result) > 3) {
// same for dewpoint
if ($result[4] == '1') {
$result[5] *= -1;
}
$weatherData['remark']['1hdew'] = Horde_Service_Weather::convertTemperature(
$result[5] / 10,
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
);
}
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'] = Horde_Service_Weather::convertTemperature(
$result[2] / 10,
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
);
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'] = Horde_Service_Weather::convertTemperature(
$result[2] / 10,
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
);
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'] = Horde_Service_Weather::convertTemperature(
$result[2] / 10,
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
);
if ($result[3] == '1') {
$result[4] *= -1;
}
$weatherData['remark']['24hmintemp'] = Horde_Service_Weather::convertTemperature(
$result[4] / 10,
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
);
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' => Horde_Service_Weather::convertPressure($result[2] / 10, 'hpa', $this->_unitMap[self::UNIT_KEY_PRESSURE]),
'description' => ($result[1] >= 0 && $result[1] <=3)
? Horde_Service_Weather_Translation::t('Rising')
: ($result[1] == 4)
? Horde_Service_Weather_Translation::t('Steady')
: ($result[1] > 4)
? Horde_Service_Weather_Translation::t('Falling') : ''
);
unset($metarCode['3hpresstend']);
break;
case 'nospeci':
// No change during the last hour
$weatherData['remark']['nospeci'] = Horde_Service_Weather_Translation::t('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])] = $this->_sensors[strtolower($result[0])];
break;
case 'maintain':
$weatherData['remark']['maintain'] = Horde_Service_Weather_Translation::t('Maintainance needed');
unset($metarCode['maintain']);
break;
default:
// Do nothing, just prevent further matching
unset($metarCode[$key]);
break;
}
if ($found) {
break;
}
}
}
}
return $weatherData;
}
/**
* Return the array of regexps used to parse METAR text. We don't define
* this in the declaration since we unset the entries as they are parsed.
*
* @return array
*/
protected function _getMetarCodes()
{
return 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'
);
}
/**
* Return the array of regexps used to parse METAR remarks section.
*
* @return array
*/
protected function _getRemarks()
{
return 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' => '[\$]'
);
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Parser/Taf.php 0000664 0001750 0001750 00000056633 13055003005 023317 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Parser_Taf
*
* Responsible for parsing encoded TAF data.
*
* Parsing code adapted from PEAR's Services_Weather_Metar class. Original
* phpdoc attributes as follows:
* @author Alexander Wirtz
* @copyright 2005-2011 Alexander Wirtz
* @link http://pear.php.net/package/Services_Weather
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Parser_Taf extends Horde_Service_Weather_Parser_Base
{
/**
* Parses TAF data.
*
* 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 The TAF encoded weather data, spilt on line endings.
*
* @return array An array of forecast data. Keys include:
* - station: (string) The station identifier.
* - dataRaw: (string) The raw TAF data.
* - update: (timestamp) Timestamp of last update.
* - validFrom: (Horde_Date) The valid FROM time.
* - validTo: (Horde_Date) The valid TO time.
* - time: (array) An array of Horde_Service_Weather_Period objects for
* each available valid time provided by the TAF report.
*/
protected function _parse(array $data)
{
$tafCode = $this->_getTafCodes();
// 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)));
// The timeperiod the data gets added to
$fromTime = '';
// If we have FMCs (Forecast Meteorological Conditions), we need this
$fmcCount = 0;
// Pointer to the array we add the data to
$pointer = &$forecastData;
for ($i = 0; $i < sizeof($taf); $i++) {
$taf[$i] = trim($taf[$i]);
if (!strlen($taf[$i])) {
continue;
}
// Init
$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'] = new Horde_Date(array(
'hour' => $result[2],
'month' => $month,
'mday' => $result[1],
'year' => $year), 'GMT');
$pointer['validTo'] = new Horde_Date(array(
'hour' => $result[4],
'month' => $month,
'mday' => $result[3],
'year' => $year), 'GMT');
unset($tafCode['valid']);
// Now the groups will start, so initialize the time groups
$pointer['time'] = array();
$start_time = new Horde_Date(array(
'year' => $year,
'month' => $month,
'mday' => $result[1],
'hour' => $result[2]), 'UTC');
$fromTime = (string)$start_time;
$pointer['time'][$fromTime] = array();
// Set pointer to the first timeperiod
$pointer = &$pointer['time'][$fromTime];
break;
case 'wind':
if ($result[5] == 'KTS') {
$result[5] = 'KT';
}
$pointer['wind'] = round(Horde_Service_Weather::convertSpeed(
$result[2],
$result[5],
$this->_unitMap[self::UNIT_KEY_SPEED]
));
if ($result[1] == 'VAR' || $result[1] == 'VRB') {
$pointer['windDegrees'] = Horde_Service_Weather_Translation::t('Variable');
$pointer['windDirection'] = Horde_Service_Weather_Translation::t('Variable');
} else {
$pointer['windDegrees'] = $result[1];
$pointer['windDirection'] = Horde_Service_Weather::degToDirection($result[1]);
}
if (is_numeric($result[4])) {
$pointer['windGust'] = round(Horde_Service_Weather::convertSpeed(
$result[4],
$result[5],
$this->_unitMap[self::UNIT_KEY_SPEED]
));
}
if (isset($probability)) {
$pointer['windProb'] = $probability;
unset($probability);
}
unset($tafCode['wind']);
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.
$found = false;
break;
}
// Match. Hand over result and advance TAF
$key = 'visibility';
$result = $resultVF;
$i++;
// Fall through
case 'visibility':
$pointer['visQualifier'] = Horde_Service_Weather_Translation::t('AT');
if (is_numeric($result[1]) && ($result[1] == 9999)) {
// Upper limit of visibility range
$visibility = Horde_Service_Weather::convertDistance(
10,
'km',
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
$pointer['visQualifier'] = Horde_Service_Weather_Translation::t('BEYOND');
} elseif (is_numeric($result[1])) {
// 4-digit visibility in m
$visibility = Horde_Service_Weather::convertDistance(
$result[1],
'm',
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
} elseif (!isset($result[11]) || $result[11] != 'CAVOK') {
if ($result[3] == 'M') {
$pointer['visQualifier'] = Horde_Service_Weather_Translation::t('BELOW');
} elseif ($result[3] == 'P') {
$pointer['visQualifier'] = Horde_Service_Weather_Translation::t('BEYOND');
}
if (is_numeric($result[5])) {
// visibility as one/two-digit number
$visibility = Horde_Service_Weather::convertDistance(
$result[5],
$result[10],
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
} else {
// the y/z part, add if we had a x part (see visibility1)
if (is_numeric($result[7])) {
$visibility = Horde_Service_Weather::convertDistance(
$result[7] + $result[8] / $result[9],
$result[10],
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
} else {
$visibility = Horde_Service_Weather::convertDistance(
$result[8] / $result[9],
$result[10],
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
}
}
} else {
$pointer['visQualifier'] = Horde_Service_Weather_Translation::t('BEYOND');
$visibility = Horde_Service_Weather::convertDistance(
10,
'km',
$this->_unitMap[self::UNIT_KEY_DISTANCE]
);
$pointer['clouds'] = array(array(
'amount' => Horde_Service_Weather_Translation::t('Clear below'),
'height' => 5000)
);
$pointer['condition'] = Horde_Service_Weather_Translation::t('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]), $this->_conditions)) {
// First try matching the complete string
$pointer['condition'] .= ' ' . $this->_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'] .= ' ' . $this->_conditions[strtolower($condition)];
}
}
}
$pointer['condition'] = trim($pointer['condition']);
if (isset($probability)) {
$pointer['condition'] .= ' (' . $probability
. '% '
. Horde_Service_Weather_Translation::t('probability')
. ').';
unset($probability);
}
break;
case 'clouds':
if (!isset($pointer['clouds'])) {
$pointer['clouds'] = array();
}
if (sizeof($result) == 5) {
// Only amount and height
$cloud = array('amount' => $this->_clouds[strtolower($result[3])]);
if ($result[4] == '///') {
$cloud['height'] = Horde_Service_Weather_Translation::t('station level or below');
} else {
$cloud['height'] = $result[4] * 100;
}
} elseif (sizeof($result) == 6) {
// Amount, height and type
$cloud = array(
'amount' => $this->_clouds[strtolower($result[3])],
'type' => $this->_clouds[strtolower($result[5])]
);
if ($result[4] == '///') {
$cloud['height'] = Horde_Service_Weather_Translation::t('station level or below');
} else {
$cloud['height'] = $result[4] * 100;
}
} else {
// SKC or CLR or NSC
$cloud = array('amount' => $this->_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'] = round(Horde_Service_Weather::convertSpeed(
$result[3],
$result[4],
$this->_unitMap[self::UNIT_KEY_SPEED]
));
$pointer['windshearHeight'] = $result[1] * 100;
$pointer['windshearDegrees'] = $result[2];
$pointer['windshearDirection'] = Horde_Service_Weather::degToDirection($result[2]);
break;
case 'tempmax':
$forecastData['temperatureHigh'] = Horde_Service_Weather::convertTemperature(
$result[1],
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
);
break;
case 'tempmin':
// Parse max/min temperature
$forecastData['temperatureLow'] = Horde_Service_Weather::convertTemperature(
$result[1],
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
);
break;
case 'tempmaxmin':
$forecastData['temperatureHigh'] = Horde_Service_Weather::convertTemperature(
$result[1],
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
);
$forecastData['temperatureLow'] = Horde_Service_Weather::convertTemperature(
$result[4],
'c',
$this->_unitMap[self::UNIT_KEY_TEMP]
);
break;
case 'from':
// Next timeperiod is coming up, prepare array and
// set pointer accordingly
$fromTime = clone $start_time;
if (sizeof($result) > 2) {
// The ICAO way
$fromTime->hour = $result[2];
$fromTime->min = $result[3];
} else {
// The Australian way (Hey mates!)
$fromTime->hour = $result[1];
}
if ($start_time->compareDateTime($fromTime) >= 1) {
$fromTime->mday++;
}
$fromTime = (string)$fromTime;
$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 = clone($start_time);
$from->hour = $lresult[1];
if ($start_time->compareDateTime($from) >= 1) {
$from->mday++;
}
$to = clone($from);
$to->hour = $lresult[2];
if ($start_time->compareDateTime($to) >= 1) {
$to->mday++;
}
// 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 = clone($start_time);
$from->hour = $lresult[2];
if ($start_time->compareDateTime($from) >= 1) {
$from->mday++;
}
$to = clone($from);
$to->hour = $lresult[4];
if ($start_time->compareDateTime($to) >= 1) {
$to->mday++;
}
// 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) {
break;
}
}
}
}
return $forecastData;
}
/**
* Return a fresh set of the regexps needed for parsing the TAF data.
*
* @return array
*/
protected function _getTafCodes()
{
return 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})?'
);
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Period/Base.php 0000664 0001750 0001750 00000005674 13055003005 023444 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Period_Base class
*
* @property is_pm Indicates if this is for an evening period.
* @property hour Indicates if this is for an hourly period.
* @property precipitation_percent Change of precipitation percent.
* @property period The cardinal number of this period.
* @property humidity Humidity percent.
* @property wind_speed Wind speed, in currently configured units.
* @property wind_direction Cardinal wind direction e.g., NNW.
* @property wind_degrees Wind direction, in degrees.
* @property wind_gust Wind gust speed, in currently configured units.
* @property snow_total Daily snow totals, in configured units.
* @property rain_total Daily rain totals, in configured units.
* @property conditions Forecast conditions e.g., "Sunny".
* @property icon_url Url to a condition icon.
* @property icon Name of condition icon.
* @property date Date for this period.
* @property high Forecast high temp, in configured units.
* @property low Forecast low temp, in configured units.
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Period_Base
{
/**
* Properties for this single peridd, as returned from the forecast request.
*
* @var mixed
*/
protected $_properties;
/**
* Reference to parent forecast object.
*
* @var Horde_Service_Weather_Forecast_Base
*/
protected $_forecast;
/**
* Const'r
*
* @param mixed $properties Current properties.
* @param Horde_Service_Forecast_Base $forecast The parent forecast.
*
* @return Horde_Service_Weather_Current
*/
public function __construct($properties, Horde_Service_Weather_Forecast_Base $forecast)
{
$this->_forecast = $forecast;
$this->_properties = $properties;
}
/**
* Default implementation - just return the value set.
*
* @param string $property The requested property.
*
* @return mixed The property value.
* @throws Horde_Service_Weather_Exception_InvalidProperty
*/
public function __get($property)
{
if (isset($this->_properties[$property])) {
return $this->_properties[$property];
}
throw new Horde_Service_Weather_Exception_InvalidProperty('This provider does not support that property');
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Period/Owm.php 0000664 0001750 0001750 00000004671 13055003005 023330 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Period_Wwo
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Period_Owm extends Horde_Service_Weather_Period_Base
{
/**
* Property Map
*
* @TODO Figure out what to do with the 'skyicon' value - which is sometimes
* different than the icon and icon_url. Also, should add a icon set
* property to allow using other icon sets e.g., {icon_set_url}/{icon}.gif
*
* @var array
*/
protected $_map = array();
/**
* Accessor so we can lazy-parse the results.
*
* @param string $property The property name.
*
* @return mixed The value of requested property
* @throws Horde_Service_Weather_Exception_InvalidProperty
*/
public function __get($property)
{
switch ($property) {
case 'humidity':
case 'precipitation_percent':
case 'wind_gust':
case 'snow_total':
case 'rain_total':
return false;
case 'conditions':
return Horde_Service_Weather_Translation::t($this->_properties->weather[0]->main);
case 'is_pm':
return false;
case 'hour':
return false;
case 'date':
return new Horde_Date($this->_properties->date);
case 'high':
return round($this->_properties->temp->day);
case 'low':
return round($this->_properties->temp->night);
case 'icon':
return $this->_forecast->weather->iconMap[
str_replace('.png', '', $this->_properties->weather[0]->icon)
];
case 'wind_direction':
return Horde_Service_Weather::degToDirection($this->_properties->deg);
case 'wind_degrees':
return $this->_properties->deg;
case 'wind_speed':
return $this->_properties->speed;
default:
throw new Horde_Service_Weather_Exception_InvalidProperty('This provider does not support the "' . $property . '" property');
}
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Period/Taf.php 0000664 0001750 0001750 00000007053 13055003005 023275 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Period_Taf
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Period_Taf extends Horde_Service_Weather_Period_Base
{
/**
* Property Map
*
* @var array
*/
protected $_map = array(
'wind_speed' => 'wind',
'wind_direction' => 'windDirection',
'wind_degrees' => 'windDegrees',
'wind_gust' => 'windGust',
'high' => 'temperatureHigh',
'low' => 'temperatureLow'
);
/**
* Accessor so we can lazy-parse the results.
*
* @param string $property The property name.
*
* @return mixed The value of requested property
* @throws Horde_Service_Weather_Exception_InvalidProperty
*/
public function __get($property)
{
switch ($property) {
case 'is_pm':
case 'hour':
case 'humidity':
case 'precipitation_percent':
case 'wind_gust':
case 'snow_total':
case 'rain_total':
case 'icon_url':
case 'icon':
return false;
case 'conditions':
$units = $this->_forecast->weather->getUnits();
$conds = '';
// Note that most of these properties will only
// be included if different from the main MFC section.
// Wind
if (!empty($this->_properties['wind'])) {
$conds .= sprintf(
Horde_Service_Weather_Translation::t('Wind from %s at %s%s '),
$this->_properties['windDirection'],
$this->_properties['wind'],
$units['wind']
);
}
// Visibility - this *should* always be here.
$conds .= sprintf(
Horde_Service_Weather_Translation::t('Visibility %s %s %s '),
$this->_properties['visQualifier'],
$this->_properties['visibility'],
$units['vis']
);
if (!empty($this->_properties['condition'])) {
$conds .= $this->_properties['condition'] . ' ';
}
// @todo This isn't totally acurate since you could have e.g., BKN
// clouds below OVC cloud cover. Probably should iterate over all
// layers and just include the highest coverage.
if (!empty($this->_properties['clouds'])) {
$conds .= sprintf('Sky %s ',
$this->_properties['clouds'][0]['amount']
);
}
return trim($conds);
case 'date':
return new Horde_Date($this->_forecast->validFrom);
default:
if (!empty($this->_properties[$property])) {
return $this->_properties[$property];
}
if (!empty($this->_map[$property])) {
return !empty($this->_properties[$this->_map[$property]])
? $this->_properties[$this->_map[$property]]
: false;
}
throw new Horde_Service_Weather_Exception_InvalidProperty('This provider does not support the "' . $property . '" property');
}
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Period/WeatherUnderground.php 0000664 0001750 0001750 00000011601 13055003005 026371 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Period_WeatherUnderground
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Period_WeatherUnderground extends Horde_Service_Weather_Period_Base
{
/**
* Property Map
*
* @TODO Figure out what to do with the 'skyicon' value - which is sometimes
* different than the icon and icon_url. Also, should add a icon set
* property to allow using other icon sets e.g., {icon_set_url}/{icon}.gif
*
* @var array
*/
protected $_map = array(
'conditions' => 'conditions',
'icon_url' => 'icon_url',
'precipitation_percent' => 'pop',
'period' => 'period',
'humidity' => 'avehumidity',
);
/**
* Accessor so we can lazy-parse the results.
*
* @param string $property The property name.
*
* @return mixed The value of requested property
* @throws Horde_Service_Weather_Exception_InvalidProperty
*/
public function __get($property)
{
switch ($property) {
case 'is_pm':
// Wunderground only supports standard
return false;
case 'hour':
// Wunderground supports this, but we don't.
return false;
case 'date':
$date = new Horde_Date(array(
'year' => $this->_properties['date']->year,
'month' => $this->_properties['date']->month,
'mday' => $this->_properties['date']->day));
$date->hour = $this->_properties['date']->hour;
$date->min = $this->_properties['date']->min;
$date->setTimezone($this->_properties['date']->tz_long);
return $date;
case 'high':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties['high']->fahrenheit !== '' ?
$this->_properties['high']->fahrenheit :
Horde_Service_Weather_Translation::t("N/A");
}
return $this->_properties['high']->celsius;
case 'low':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties['low']->fahrenheit !== '' ?
$this->_properties['low']->fahrenheit :
Horde_Service_Weather_Translation::t("N/A");
}
return $this->_properties['low']->celsius;
case 'icon':
return $this->_forecast->weather->iconMap[$this->_properties['icon']];
case 'wind_direction':
return strlen($this->_properties['avewind']->dir)
? Horde_Service_Weather_Translation::t($this->_properties['avewind']->dir)
: Horde_Service_Weather_Translation::t("N/A");
case 'wind_degrees':
return strlen($this->_properties['avewind']->dir)
? $this->_properties['avewind']->degrees
: Horde_Service_Weather_Translation::t("N/A");
case 'wind_speed':
if (strlen($this->_properties['avewind']->dir)) {
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties['avewind']->mph;
}
return $this->_properties['avewind']->kph;
} else {
return Horde_Service_Weather_Translation::t("N/A");
}
case 'wind_gust':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties['maxwind']->mph;
}
return $this->_properties['maxwind']->kph;
case 'rain_total':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties['qpf_allday']->in;
}
return $this->_properties['qpf_allday']->mm;
case 'snow_total':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties['snow_allday']->in;
}
return $this->_properties['snow_allday']->cm;
default:
if (!empty($this->_map[$property])) {
return Horde_Service_Weather_Translation::t($this->_properties[$this->_map[$property]]);
}
throw new Horde_Service_Weather_Exception_InvalidProperty('This provider does not support the "' . $property . '" property');
}
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Period/Wwo.php 0000664 0001750 0001750 00000005762 13055003005 023344 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Period_Wwo
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Period_Wwo extends Horde_Service_Weather_Period_Base
{
/**
* Property Map
*
* @TODO Figure out what to do with the 'skyicon' value - which is sometimes
* different than the icon and icon_url. Also, should add a icon set
* property to allow using other icon sets e.g., {icon_set_url}/{icon}.gif
*
* @var array
*/
protected $_map = array();
/**
* Accessor so we can lazy-parse the results.
*
* @param string $property The property name.
*
* @return mixed The value of requested property
* @throws Horde_Service_Weather_Exception_InvalidProperty
*/
public function __get($property)
{
switch ($property) {
case 'humidity':
case 'precipitation_percent':
case 'wind_gust':
case 'snow_total':
case 'rain_total':
return false;
case 'conditions':
return Horde_Service_Weather_Translation::t($this->_properties->weatherDesc[0]->value);
case 'icon_url':
return $this->_properties->weatherIconUrl[0]->value;
case 'is_pm':
return false;
case 'hour':
return false;
case 'date':
return new Horde_Date($this->_properties->date);
case 'high':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties->tempMaxF ;
}
return $this->_properties->tempMaxC;
case 'low':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties->tempMinF;
}
return $this->_properties->tempMinC;
case 'icon':
return $this->_forecast->weather->iconMap[
str_replace('.png', '', basename($this->_properties->weatherIconUrl[0]->value))
];
case 'wind_direction':
return $this->_properties->winddirection;
case 'wind_degrees':
return $this->_properties->winddirDegree;
case 'wind_speed':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties->windspeedMiles;
}
return $this->_properties->windspeedKmph;
default:
throw new Horde_Service_Weather_Exception_InvalidProperty('This provider does not support the "' . $property . '" property');
}
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Period/Wwov2.php 0000664 0001750 0001750 00000007506 13055003005 023612 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Period_Wwo
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Period_Wwov2 extends Horde_Service_Weather_Period_Base
{
/**
* Property Map
*
* @TODO Figure out what to do with the 'skyicon' value - which is sometimes
* different than the icon and icon_url. Also, should add a icon set
* property to allow using other icon sets e.g., {icon_set_url}/{icon}.gif
*
* @var array
*/
protected $_map = array();
/**
* Accessor so we can lazy-parse the results.
*
* @param string $property The property name.
*
* @return mixed The value of requested property
* @throws Horde_Service_Weather_Exception_InvalidProperty
*/
public function __get($property)
{
switch ($property) {
case 'humidity':
return $this->_properties->humidity;
case 'precipitation_percent':
// There is no "precipitation" field, so if we don't have
// rain, check for snow, and return 0 if we have neither.
return !empty($this->_properties->chanceofrain)
? $this->_properties->chanceofrain
: (!empty($this->_properties->chanceofsnow)
? $this->_properties->chanceofsnow
: 0);
case 'wind_gust':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties->WindGustMiles;
}
return $this->_properties->WindGustKmph;
case 'snow_total':
case 'rain_total':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties->precipInches;
}
return $this->_properties->precipMM;
case 'conditions':
return Horde_Service_Weather_Translation::t($this->_properties->weatherDesc[0]->value);
case 'icon_url':
return $this->_properties->weatherIconUrl[0]->value;
case 'is_pm':
return false;
case 'hour':
return false;
case 'date':
return new Horde_Date($this->_properties->date);
case 'high':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties->maxtempF ;
}
return $this->_properties->maxtempC;
case 'low':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties->mintempF;
}
return $this->_properties->mintempC;
case 'icon':
return $this->_forecast->weather->iconMap[
str_replace('.png', '', basename($this->_properties->weatherIconUrl[0]->value))
];
case 'wind_direction':
return $this->_properties->winddir16Point;
case 'wind_degrees':
return $this->_properties->winddirDegree;
case 'wind_speed':
if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) {
return $this->_properties->windspeedMiles;
}
return $this->_properties->windspeedKmph;
default:
throw new Horde_Service_Weather_Exception_InvalidProperty('This provider does not support the "' . $property . '" property');
}
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Base.php 0000664 0001750 0001750 00000021431 13055003005 022207 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Base class
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
abstract class Horde_Service_Weather_Base
{
/**
* Parameters
*
* @var array
*/
protected $_params;
/**
* Cache object
*
* @var Horde_Cache
*/
protected $_cache;
/**
* Lifetime for cached data.
*
* @var integer
*/
protected $_cache_lifetime = 21600;
/**
* Units to display results in.
*
* @var integer
*/
public $units = Horde_Service_Weather::UNITS_STANDARD;
/**
* URL to a logo for this provider
*
* @var string
*/
public $logo;
/**
* URL to the provider's site
*
* @var string
*/
public $link;
/**
* Title for the provider
*
* @var string
*/
public $title;
/**
* Driver spcific icon map for condition icons. Made public so icons can
* be overridden in client code if desired.
*
* @var array
*/
public $iconMap = array();
/**
* The http client
*
* @var Horde_Http_Client
*/
protected $_http;
/**
* Local cache of current conditions
*
*/
protected $_current;
/**
* Local cache of forecast
*
* @var Horde_Service_Weather_Forecast_Base
*/
protected $_forecast;
/**
* Local cache of station data
*
* @var Horde_Service_Weather_Station
*/
protected $_station;
/**
* Last location requested.
*
* @var string
*/
protected $_lastLocation;
/**
* Last requested forecast length.
*
* @var integer
*/
protected $_lastLength;
protected $_alerts = array();
protected $_radar;
/**
* Constructor.
*
* @param array $params Parameters:
* - cache: (Horde_Cache) Optional Horde_Cache object.
* - cache_lifetime: (integer) Lifetime of cached data, if caching.
* - http_client: (Horde_Http_Client) Required http client object.
*/
public function __construct(array $params = array())
{
if (empty($params['http_client'])) {
throw new InvalidArgumentException('Missing http_client parameter.');
}
$this->_http = $params['http_client'];
unset($params['http_client']);
if (!empty($params['cache'])) {
$this->_cache = $params['cache'];
unset($params['cache']);
if (!empty($params['cache_lifetime'])) {
$this->_cache_lifetime = $params['cache_lifetime'];
unset($params['cache_lifetime']);
}
}
$this->_params = $params;
}
/**
* Returns the current observations.
*
* @param string $location The location string.
*
* @return Horde_Service_Weather_Current_Base
*/
abstract public function getCurrentConditions($location);
/**
* Returns the forecast for the current location.
*
* @param string $location The location code.
* @param integer $length The forecast length, a
* Horde_Service_Weather::FORECAST_* constant.
* @param integer $type The type of forecast to return, a
* Horde_Service_Weather::FORECAST_TYPE_* constant
*
* @return Horde_Service_Weather_Forecast_Base
*/
abstract public function getForecast(
$location,
$length = Horde_Service_Weather::FORECAST_3DAY,
$type = Horde_Service_Weather::FORECAST_TYPE_STANDARD);
/**
* Searches locations.
*
* @param string $location The location string to search.
* @param integer $type The type of search to perform, a
* Horde_Service_Weather::SEARCHTYPE_* constant.
*
* @return Horde_Service_Weather_Station The search location suitable to use
* directly in a weather request.
* @throws Horde_Service_Weather_Exception
*/
abstract public function searchLocations(
$location,
$type = Horde_Service_Weather::SEARCHTYPE_STANDARD);
/**
* Get array of supported forecast lengths.
*
* @return array The array of supported lengths.
*/
abstract public function getSupportedForecastLengths();
/**
* Return array of weather alerts, if available.
*
* @return array
*/
public function getAlerts($location)
{
return $this->_alerts;
}
/**
* Return the URL to a (possibly animated) radar image.
*
* @param string $location The location
*
* @return string|boolean The Url, or false if not available.
*/
public function getRadarImageUrl($location)
{
return false;
}
/**
* Return the URL a OpenLayers suitable tile server.
*
* @param string $location The location.
* @param string $type The optional layer type.
*
* @return string|boolean The Url, or false if not available.
*/
public function getTileServerUrl($location, $type = null)
{
return false;
}
/**
* Searches for locations that begin with the text in $search.
*
* @param string $search The text to search.
*
* @return array An array of stdClass objects with 'name' and 'code'
* properties.
* @throws Horde_Service_Weather_Exception
*/
public function autocompleteLocation($search)
{
throw new Horde_Service_Weather_Exception('Not implemented');
}
/**
* Returns a mapping of units for each UNIT type.
*
* @param integer $type The units for measurement. A
* Horde_Service_Weather::UNITS_* constant.
*
* @return array The mapping of measurements (as keys) and units (as values).
*/
public function getUnits($type = null)
{
if (empty($type)) {
$type = $this->units;
}
if ($type == Horde_Service_Weather::UNITS_STANDARD) {
return array(
'temp' => Horde_Service_Weather_Translation::t('F'),
'wind' => Horde_Service_Weather_Translation::t('mph'),
'pres' => Horde_Service_Weather_Translation::t('inches'),
'vis' => Horde_Service_Weather_Translation::t('miles'),
'rain' => Horde_Service_Weather_Translation::t('inches'),
'snow' => Horde_Service_Weather_Translation::t('inches'),
);
}
return array(
'temp' => Horde_Service_Weather_Translation::t('C'),
'wind' => Horde_Service_Weather_Translation::t('kph'),
'pres' => Horde_Service_Weather_Translation::t('millibars'),
'vis' => Horde_Service_Weather_Translation::t('km'),
'rain' => Horde_Service_Weather_Translation::t('millimeters'),
'snow' => Horde_Service_Weather_Translation::t('centimeters'),
);
}
/**
* Returns the station information associated with the last request.
*
* @return Horde_Service_Weather_Station
* @throws Horde_Service_Weather_Exception if not request has yet been made.
*/
public function getStation()
{
if (empty($this->_station)) {
throw new Horde_Service_Weather_Exception('No request made.');
}
return $this->_station;
}
/**
* Check if an IP address is a globally unique address and not in RFC1918 or
* RFC3330 address space.
*
* @param string $ip The IPv4 IP address to check.
*
* @return boolean True if the IP address is globally unique, otherwise
* false.
* @link http://tools.ietf.org/html/rfc3330
* @link http://www.faqs.org/rfcs/rfc1918.html
*/
protected function _ipIsUnique($ip)
{
// Make sure it's sane
$parts = explode('.', $ip);
if (count($parts) != 4) {
return false;
}
// zero config IPs RFC3330
if ($parts[0] == 169 && $parts[1] == 254) {
return false;
}
// reserved RFC 1918
if ($parts[0] == 10 ||
($parts[0] == 192 && $parts[1] == 168) ||
($parts[0] == 172 && ($parts[1] >= 16 && $parts[1] <= 31))) {
return false;
}
// Loopback
if ($parts[0] == 127) {
return false;
}
return true;
}
}
Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Exception.php 0000664 0001750 0001750 00000000553 13055003005 023275 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/bsd BSD
* @package Service_Weather
*/
class Horde_Service_Weather_Exception extends Horde_Exception_Wrapped
{
}
Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Metar.php 0000664 0001750 0001750 00000027014 13055003005 022410 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Metar
*
* Responsible for parsing encoded METAR and TAF data.
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Metar extends Horde_Service_Weather_Base
{
/**
* Database handle. Expects to have the following table available:
*
* @var Horde_Db_Adapter_Base
*/
protected $_db;
/**
* Name of table containing the NOAA METAR database.
*
* @var string
*/
protected $_tableName = 'horde_metar_airports';
/**
* Default paths to download weather data.
*
* @var string
*/
protected $_metar_path = 'http://tgftp.nws.noaa.gov/data/observations/metar/stations';
protected $_taf_path = 'http://tgftp.nws.noaa.gov/data/forecasts/taf/stations';
/**
* Local cache of locations.
*
* @var array
*/
protected $_locations;
/**
* Constructor.
*
* In addtion to the params for the parent class, you can also set a
* database adapter for NOAA station lookups, and if you don't want
* to use the default METAR/TAF http locations, you can set them here too.
* Note only HTTP is currently supported, but file and ftp are @todo.
*
* @param array $params Parameters:
* - cache: (Horde_Cache) Optional Horde_Cache object.
* - cache_lifetime: (integer) Lifetime of cached data, if caching.
* - http_client: (Horde_Http_Client) Required http client object.
* - db: (Horde_Db_Adapter_Base) DB Adapter for METAR DB.
* - metar_path: (string) Path or URL to METAR data.
* - taf_path: (string) Path or URL to TAF data.
*/
public function __construct(array $params = array())
{
// Mini-hack to avoid passing a http_client object if
// we really don't need it.
if (empty($params['http_client']) &&
!empty($params['metar_path']) &&
!empty($params['taf_path'])) {
$params['http_client'] = true;
}
parent::__construct($params);
if (!empty($params['db'])) {
$this->_db = $params['db'];
}
if (!empty($params['metar_path'])) {
$this->_metar_path = $params['metar_path'];
}
if (!empty($params['taf_path'])) {
$this->_taf_path = $params['taf_path'];
}
if (!empty($params['table_name'])) {
$this->_tableName = $params['table_name'];
}
}
/**
* Returns the current observations (METAR).
*
* @param string $location The location string.
*
* @return Horde_Service_Weather_Current_Base
* @throws Horde_Service_Weather_Exception
*/
public function getCurrentConditions($location)
{
$this->_station = $this->_getStation($location);
// Sniff out type of request.
$endpoint = sprintf('%s/%s.TXT', $this->_metar_path, $location);
$pathinfo = parse_url($endpoint);
if (empty($pathinfo['scheme']) && file_exists($endpoint)) {
$pathinfo['scheme'] = 'file';
} elseif (empty($pathinfo['scheme'])) {
throw new Horde_Service_Weather_Exception('Invalid path to METAR data.');
}
switch ($pathinfo['scheme']) {
case 'http':
$data = $this->_makeRequest($endpoint);
break;
case 'file':
$data = file_get_contents(realpath($endpoint));
break;
}
if (empty($data)) {
throw new Horde_Service_Weather_Exception('METAR file not found.');
}
$parser = new Horde_Service_Weather_Parser_Metar(array('units' => $this->units));
return new Horde_Service_Weather_Current_Metar(
$parser->parse($data),
$this
);
}
/**
* Performs a HTTP request.
*
* @param string $url The URL endpoint.
* @param integer $lifetime The cache lifetime.
*
* @return string The response body of the request.
* @throws Horde_Service_Weather_Exception
*/
protected function _makeRequest($url, $lifetime = 86400)
{
$cachekey = md5('hordeweather' . $url);
if ((!empty($this->_cache) && !$results = $this->_cache->get($cachekey, $lifetime)) ||
empty($this->_cache)) {
$url = new Horde_Url($url);
$response = $this->_http->get((string)$url);
if (!$response->code == '200') {
throw new Horde_Service_Weather_Exception($response->code);
}
$results = $response->getBody();
if (!empty($this->_cache)) {
$this->_cache->set($cachekey, $results);
}
}
return $results;
}
/**
* Returns the forecast for the current location.
*
* @param string $location The location code.
* @param integer $length The forecast length, a
* Horde_Service_Weather::FORECAST_* constant.
* (Ignored)
* @param integer $type The type of forecast to return, a
* Horde_Service_Weather::FORECAST_TYPE_* constant
* (Ignored)
*
* @return Horde_Service_Weather_Forecast_Base
* @throws Horde_Service_Weather_Exception
*/
public function getForecast(
$location,
$length = Horde_Service_Weather::FORECAST_3DAY,
$type = Horde_Service_Weather::FORECAST_TYPE_STANDARD)
{
$this->_station = $this->_getStation($location);
// Sniff out type of request.
$endpoint = sprintf('%s/%s.TXT', $this->_taf_path, $location);
$pathinfo = parse_url($endpoint);
if (empty($pathinfo['scheme']) && file_exists($endpoint)) {
$pathinfo['scheme'] = 'file';
} elseif (empty($pathinfo['scheme'])) {
throw new Horde_Service_Weather_Exception('Invalid path to TAF data.');
}
switch ($pathinfo['scheme']) {
case 'http':
$url = sprintf('%s/%s.TXT', $this->_taf_path, $location);
$data = $this->_makeRequest($url);
break;
case 'file':
$data = file_get_contents(realpath($pathinfo['path']));
break;
}
if (empty($data)) {
throw new Horde_Service_Weather_Exception('TAF file not found.');
}
// Parse the data.
$parser = new Horde_Service_Weather_Parser_Taf(array('units' => $this->units));
return new Horde_Service_Weather_Forecast_Taf(
$parser->parse($data),
$this
);
}
/**
* Searches locations.
*
* @param string $location The location string to search.
* @param integer $type The type of search to perform, a
* Horde_Service_Weather::SEARCHTYPE_* constant.
*
* @return Horde_Service_Weather_Station The search location suitable to use
* directly in a weather request.
* @throws Horde_Service_Weather_Exception
*/
public function searchLocations(
$location,
$type = Horde_Service_Weather::SEARCHTYPE_STANDARD)
{
try {
return $this->_getStation($location);
} catch (Horde_Exception_NotFound $e) {
return new Horde_Service_Weather_Station(array(
'code' => $location
));
}
}
/**
* Get array of supported forecast lengths.
*
* @return array The array of supported lengths.
*/
public function getSupportedForecastLengths()
{
// There are no "normal" forecast lengths in TAF data.
return array();
}
/**
* Return an array containing all available METAR locations/airports.
*
* @return array An array of station information. Each entry contains:
* - icao: The ICAO identifier of the location.
* - name: The human readable name of the station.
* - country: The country the station is located in (if available).
*/
public function getLocations()
{
if (empty($this->_locations)) {
$this->_locations = $this->_getLocations();
}
return $this->_locations;
}
/**
* Searches for locations that begin with the text in $search.
*
* @param string $search The text to search.
*
* @return array An array of stdClass objects with 'name' and 'code'
* properties.
* @throws Horde_Service_Weather_Exception
*/
public function autocompleteLocation($search)
{
if (empty($this->_db)) {
return array();
}
$sql = 'SELECT icao, name, state, municipality, country FROM ' . $this->_tableName . ' WHERE '
. 'name LIKE ? OR icao LIKE ? OR state LIKE ? OR municipality LIKE ?';
try {
$rows = $this->_db->select($sql, array_fill(0, 4, $search . '%'));
} catch (Horde_Db_Exception $e) {
throw new Horde_Service_Weather_Exception($e);
}
$results = array();
foreach ($rows as $row) {
$obj = new stdClass();
$obj->name = sprintf('%s (%s, %s, %s)', $row['name'], $row['municipality'], $row['state'], $row['country']);
$obj->code = $row['icao'];
$results[] = $obj;
}
return $results;
}
/**
* Perform DB query to obtain list of airport codes.
*
* @return array An array of station information. Each entry contains:
* - icao: The ICAO identifier of the location.
* - name: The human readable name of the station.
* - country: The country the station is located in (if available).
*
* @throws Horde_Service_Weather_Exception
*/
protected function _getLocations()
{
if (empty($this->_db)) {
return array();
}
$sql = 'SELECT icao, name, state, municipality, country FROM ' . $this->_tableName . ' ORDER BY country';
try {
return $this->_db->selectAll($sql);
} catch (Horde_Exception $e) {
throw new Horde_Service_Weather_Exception($e);
}
}
/**
* Return a station object matching $code.
*
* @param string $code The ICAO station identifier.
*
* @return Horde_Service_Weather_Station The station object.
* @throws Horde_Service_Weather_Exception
* @throws Horde_Exception_NotFound
*/
protected function _getStation($code)
{
if (empty($this->_db)) {
return new Horde_Service_Weather_Station(array(
'code' => $code,
'name' => $code
));
}
$sql = 'SELECT icao, name, country, latitude, longitude from ' . $this->_tableName . ' WHERE icao = ?';
try {
$result = $this->_db->selectOne($sql, array($code));
} catch (Horde_Db_Exception $e) {
throw new Horde_Service_Weather_Exception($e);
}
if (empty($result)) {
throw new Horde_Exception_NotFound();
}
return new Horde_Service_Weather_Station(array(
'name' => $result['name'],
'code' => $code,
'country_name' => $result['country'],
'lat' => $result['latitude'],
'lon' => $result['lon']
));
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Owm.php 0000664 0001750 0001750 00000026365 13055003005 022112 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Owm
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Owm extends Horde_Service_Weather_Base
{
const API_URL = 'http://api.openweathermap.org/data/2.5';
/**
* @see Horde_Service_Weather_Base::$title
* @var string
*/
public $title = 'OpenWeatherMap';
/**
* @see Horde_Service_Weather_Base::$link
* @var string
*/
public $link = 'http://openweathermap.org';
/**
* @see Horde_Service_Weather::$iconMap
*/
public $iconMap = array(
'01d' => '32.png',
'01n' => '33.png',
'02d' => '30.png', //Few Clouds,day
'02n' => '29.png', //Few Clouds,pm
'03d' => '28.png', //Broken clouds
'03n' => '27.png',
'04d' => '26.png', //Overcast
'04d' => '26.png',
'09d' => '11.png',
'09n' => '40.png',
'10d' => '12.png',
'10n' => '30.png',
'11d' => '0.png',
'11n' => '47.png',
'13d' => '16.png',
'13n' => '42.png',
);
/**
* Owm API key.
*
* @var string
*/
protected $_key;
protected $_locationCode;
/**
* Constructor.
*
* @param array $params Parameters:
* - cache: (Horde_Cache) Optional Horde_Cache object.
* - cache_lifetime: (integer) Lifetime of cached data, if caching.
* - http_client: (Horde_Http_Client) Required http client object.
* - apikey: (string) Require api key for Wwo.
* - apiVersion: (integer) Version of the API to use.
* Defaults to v1 for BC reasons.
*
* @return Horde_Service_Weather_Wwo
*/
public function __construct(array $params = array())
{
// Check required api key parameters here...
if (empty($params['apikey'])) {
throw new InvalidArgumentException('Missing required API Key parameter.');
}
$this->_key = $params['apikey'];
unset($params['apikey']);
parent::__construct($params);
}
/**
* Obtain the current observations.
*
* @see Horde_Service_Weather_Base::getCurrentConditions
*
* @return Horde_Service_Weather_Current_Wwo
*/
public function getCurrentConditions($location)
{
$this->_getCommonElements($location);
return $this->_current;
}
/**
* Obtain the forecast for the current location.
*
* @see Horde_Service_Weather_Base::getForecast
*/
public function getForecast(
$location,
$length = Horde_Service_Weather::FORECAST_3DAY,
$type = Horde_Service_Weather::FORECAST_TYPE_STANDARD)
{
$this->_getCommonElements($location);
return $this->_forecast;
}
/**
* Search for a valid location code.
*
* @see Horde_Service_Weather_Base::searchLocations
*/
public function searchLocations($location, $type = Horde_Service_Weather::SEARCHTYPE_STANDARD)
{
return current($this->_parseSearchLocations($this->_searchLocations($location)));
}
/**
* Return an autocomplete request result.
* @todo Provide switch to use another autocomplete API since
* Owm does not provide one. E.g., Wunderground provides free,
* key-less access to their autocomplete API.
*
* @see Horde_Service_Weather_Base::autocompleteLocation
*/
public function autocompleteLocation($search)
{
$results = $this->searchLocations($search);
return $this->_parseAutocomplete($results);
}
/**
* Return the supported forecast lengths.
*
* @see Horde_Service_Weather_Base::getSupportedForecastLengths
*/
public function getSupportedForecastLengths()
{
return array(
3 => Horde_Service_Weather::FORECAST_3DAY,
5 => Horde_Service_Weather::FORECAST_5DAY,
7 => Horde_Service_Weather::FORECAST_7DAY,
10 => Horde_Service_Weather::FORECAST_10DAY
);
}
/**
* Populates some common data used by forecasts and current conditions.
*
* @param string $location The location identifier.
* @param integer $length The forecast length.
*/
protected function _getCommonElements($location, $length = Horde_Service_Weather::FORECAST_5DAY)
{
if (!empty($this->_current) && $location == $this->_lastLocation
&& $this->_lastLength == $length) {
return;
}
$this->_lastLength = $length;
$this->_lastLocation = $location;
$weather_url = new Horde_Url(self::API_URL . '/weather');
$forecast_url = new Horde_Url(self::API_URL . '/forecast/daily');
if (is_int($location)) {
$weather_url->add(array(
'id' => $location
));
$forecast_url->add(array(
'id' => $location
));
} else {
$weather_url->add(array(
'q' => $location
));
$forecast_url->add(array(
'q' => $location
));
}
$current_results = $this->_makeRequest($weather_url);
$forecast_results = $this->_makeRequest($forecast_url);
$this->_current = $this->_parseCurrent($current_results);
// Use the minimum station data provided by forecast request to
// fetch the full station data.
// @todo - use the weather station api?
$station = $this->_parseStation($current_results);
// // Sunrise/Sunset
$station->sunrise = new Horde_Date($current_results->sys->sunrise, 'UTC');
$station->sunset = new Horde_Date($current_results->sys->sunset, 'UTC');
$station->time = $this->_current->time;
$this->_station = $station;
$this->_forecast = $this->_parseForecast($forecast_results->list);
}
/**
* Parses the JSON response for a location request into a station object.
*
* @param StdClass $station The response from a Location request.
*
* @return Horde_Service_Weather_Station
*/
protected function _parseStation($station)
{
$properties = array(
'name' => $station->name,
'city' => $station->name,
'state' => '',
'country' => $station->sys->country,
'country_name' => '',
'lat' => $station->coord->lat,
'lon' => $station->coord->lon,
'zip' => '',
'code' => $station->id
);
return new Horde_Service_Weather_Station($properties);
}
/**
* Parses the forecast data.
*
* @param stdClass $forecast The result of the forecast request.
*
* @return Horde_Service_Weather_Forecast_Wwo The forecast.
*/
protected function _parseForecast($forecast)
{
$forecast = new Horde_Service_Weather_Forecast_Owm($forecast, $this);
return $forecast;
}
/**
* Parse the current_conditions response.
*
* @param stdClass $current The current_condition request response object
*
* @return Horde_Service_Weather_Current
*/
protected function _parseCurrent($current)
{
// The Current object takes care of the parsing/mapping.
$current = new Horde_Service_Weather_Current_Owm($current, $this);
return $current;
}
/**
*
*
* @param array $results An array of Horde_Service_Weather_Station objects.
*
* @return [type] [description]
*/
protected function _parseAutocomplete($results)
{
$return = array();
foreach($results as $result) {
$new = new stdClass();
$new->name = sprintf('%s (%s/%s)', $result->name, $result->lat, $result->lon);
$new->code = $result->code;
$return[] = $new;
}
return $return;
}
/**
* Execute a location search.
*
* @param string $location The location text to search.
*
* @return string The location code result(s).
*/
protected function _searchLocations($location)
{
$url = new Horde_Url(self::API_URL . '/find');
$url = $url->add(array(
'q' => $location,
'type' => 'like')
);
return $this->_makeRequest($url);
}
/**
* Return an array of location search results.
*
* @param stdClass $response The results of a find query.
*
* @return array An array of Horde_Service_Weather_Station objects.
* @throws Horde_Service_Weather_Exception
*/
protected function _parseSearchLocations($response)
{
if (!empty($response->results->error)) {
throw new Horde_Service_Weather_Exception($response->results->error->message);
}
if (!$response->count) {
return array();
}
$results = array();
foreach ($response->list as $result) {
$properties = array(
'name' => $result->name,
'city' => $result->name,
'state' => '',
'country' => $result->sys->country,
'country_name' => '',
'lat' => $result->coord->lat,
'lon' => $result->coord->lon,
'zip' => '',
'code' => $result->id,
);
$results[] = new Horde_Service_Weather_Station($properties);
}
return $results;
}
/**
* Make the remote API call.
*
* @param Horde_Url $url The endpoint.
*
* @return mixed The unserialized results form the remote API call.
* @throws Horde_Service_Weather_Exception
*/
protected function _makeRequest(Horde_Url $url)
{
// Owm returns temperature data in Kelvin by default!
if ($this->units == Horde_Service_Weather::UNITS_METRIC) {
$url->add('units', 'metric');
} else {
$url->add('units', 'imperial');
}
$url->add(array(
'key' => $this->_key
))->setRaw(true);
$cachekey = md5('hordeweather' . $url);
if ((!empty($this->_cache) &&
!($results = $this->_cache->get($cachekey, $this->_cache_lifetime))) ||
empty($this->_cache)) {
$response = $this->_http->get((string)$url);
if (!$response->code == '200') {
throw new Horde_Service_Weather_Exception($response->code);
}
$results = $response->getBody();
if (!empty($this->_cache)) {
$this->_cache->set($cachekey, $results);
}
}
$results = Horde_Serialize::unserialize($results, Horde_Serialize::JSON);
if (!($results instanceof StdClass)) {
throw new Horde_Service_Weather_Exception(sprintf(
'Error, unable to decode response: %s',
$results));
}
return $results;
}
}
Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Station.php 0000664 0001750 0001750 00000005457 13055003005 022770 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Station class
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*
* @property string name The station's common name.
* @property string city The city.
* @property string state The state.
* @property string country The country's iso3166 name (if available).
* @property string country_name The country's common name.
* @property mixed tz The timezone name, or offset from UTC
* depending on the API. @see self::getOffset()
* @property string lat The lattitude (if available).
* @property string lon The longitude (if available).
* @property string zip The postal code.
* @property string code The internal identifier for the API.
*/
class Horde_Service_Weather_Station
{
/**
* Local properties array.
*
* @var array
*/
protected $_properties = array();
/**
* Const'r
*
* @param array $properties The properties for the station.
*/
public function __construct($properties = array())
{
$this->_properties = $properties;
}
/**
* Accessor
*
* @param string $property The property to return.
*
* @return mixed The value of requested property.
*/
public function __get($property)
{
if (isset($this->_properties[$property])) {
return $this->_properties[$property];
}
return '';
}
/**
* Setter.
*
* @param string $property The property name.
* @param mixed $value The value to set $property to.
*/
public function __set($property, $value)
{
$this->_properties[$property] = $value;
}
/**
* Return the CURRENT offset from UTC for this station as provided by the
* API.
*
* @return integer The current offset from UTC.
* @since 1.2.0
*/
public function getOffset()
{
if (!empty($this->_properties['tz']) && is_numeric($this->_properties['tz'])) {
return ($this->tz < 0 ? '-' : '') . gmdate('H:i', floor(abs($this->tz) * 60 * 60));
} elseif (!empty($this->_properties['tz'])) {
try {
$d = new Horde_Date(time(), 'UTC');
$d->setTimezone($this->tz);
return $d->tzOffset();
} catch (Horde_Date_Exception $e) {
}
}
return false;
}
} Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Translation.php 0000664 0001750 0001750 00000001326 13055003005 023634 0 ustar jan jan
* @package Service_Weather
*/
class Horde_Service_Weather_Translation extends Horde_Translation_Autodetect
{
/**
* The translation domain
*
* @var string
*/
protected static $_domain = 'Horde_Service_Weather';
/**
* The absolute PEAR path to the translations for the default gettext handler.
*
* @var string
*/
protected static $_pearDirectory = '@data_dir@';
}
Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/WeatherUnderground.php 0000664 0001750 0001750 00000033637 13055003005 025164 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_WeatherUnderground.
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_WeatherUnderground extends Horde_Service_Weather_Base
{
const API_URL = 'http://api.wunderground.com';
public $logo = 'weather/wundergroundlogo.png';
/**
* Language to request strings from Google in.
*
* @var string
*/
protected $_language = 'en';
/**
* @see Horde_Service_Weather::$iconMap
*/
public $iconMap = array(
'chanceflurries' => '15.png',
'chancerain' => '11.png',
'chancesleet' => '8.png',
'chancesnow' => '14.png',
'chancetstorms' => '3.png',
'clear' => '32.png',
'cloudy' => '26.png',
'flurries' => '14.png',
'fog' => '20.png',
'hazy' => '21.png',
'mostlycloudy' => '28.png',
'mostlysunny' => '34.png',
'partlycloudy' => '30.png',
'partlysunny' => '30.png',
'sleet' => '10.png',
'rain' => '12.png',
'snow' => '16.png',
'sunny' => '32.png',
'tstorms' => '3.png',
// Nighttime
'nt_chanceflurries' => '46.png',
'nt_chancerain' => '45.png',
'nt_chancesleet' => '10.png',
'nt_chancesnow' => '46.png',
'nt_chancetstorms' => '45.png',
'nt_clear' => '31.png',
'nt_cloudy' => '26.png',
'nt_flurries' => '46.png',
'nt_fog' => '20.png',
'nt_hazy' => '21.png',
'nt_mostlycloudy' => '45.png',
'nt_partlycloudy' => '29.png',
'nt_sleet' => '10.png',
'nt_rain' => '45.png',
'nt_snow' => '46.png',
'nt_tstorms' => '47.png'
);
/**
* Constructor.
*
* @param array $params Parameters:
* - cache: (Horde_Cache) Optional Horde_Cache object.
* - cache_lifetime: (integer) Lifetime of cached data, if caching.
* - http_client: (Horde_Http_Client) Required http client object.
* - apikey: (string) Require api key for Wwo.
*
* @return Horde_Service_Weather_WeatherUnderground
*/
public function __construct(array $params = array())
{
// Check required api key parameters here...
if (empty($params['apikey'])) {
throw new InvalidArgumentException('Missing required API Key parameter.');
}
if (!empty($params['language'])) {
$this->_language = $params['language'];
}
$this->_apiKey = $params['apikey'];
unset($params['apikey']);
parent::__construct($params);
}
/**
* Obtain the current observations.
*
* @see Horde_Service_Weather_Current_Base::getCurrentConditions()
*
* @return Horde_Service_Weather_Current_WeatherUnderground.
*/
public function getCurrentConditions($location)
{
$this->_getCommonElements(rawurlencode($location), !empty($this->_lastLength) ? $this->_lastLength : null);
return $this->_current;
}
/**
* Obtain the forecast for the current location.
*
* @see Horde_Service_Weather_Base::getForecast()
*
* @return Horde_Service_Weather_Forecast_WeatherUnderground
*/
public function getForecast(
$location,
$length = Horde_Service_Weather::FORECAST_3DAY,
$type = Horde_Service_Weather::FORECAST_TYPE_STANDARD)
{
$this->_getCommonElements(rawurlencode($location), $length);
return $this->_forecast;
}
public function getAlerts($location)
{
$this->_getCommonElements(rawurlencode($location), !empty($this->_lastLength) ? $this->_lastLength : null);
return $this->_alerts;
}
/**
* Return the URL to a (possibly animated) radar image.
*
* @param string $location The location
*
* @return string|boolean The Url, or false if not available.
*/
public function getRadarImageUrl($location)
{
$this->_getCommonElements(rawurlencode($location), !empty($this->_lastLength) ? $this->_lastLength : null);
return $this->_radar;
}
/**
* Search for a valid location code.
*
* @see Horde_Service_Weather_Base::searchLocations()
*/
public function searchLocations($location, $type = Horde_Service_Weather::SEARCHTYPE_STANDARD)
{
switch ($type) {
case Horde_Service_Weather::SEARCHTYPE_STANDARD:
case Horde_Service_Weather::SEARCHTYPE_ZIP:
case Horde_Service_Weather::SEARCHTYPE_CITYSTATE:
return $this->_parseSearchLocations($this->_searchLocations(rawurlencode($location)));
case Horde_Service_Weather::SEARCHTYPE_IP:
return $this->_parseSearchLocations($this->_getLocationByIp(rawurlencode($location)));
}
}
/**
* Return an autocomplete search response.
*
* @see Horde_Service_Weather_Base::autocompleteLocation()
*/
public function autocompleteLocation($search)
{
$url = new Horde_Url('http://autocomplete.wunderground.com/aq');
$url->add(array('query' => $search, 'format' => 'JSON'));
return $this->_parseAutocomplete($this->_makeRequest($url));
}
/**
* Get array of supported forecast lengths.
*
* @return array The array of supported lengths.
*/
public function getSupportedForecastLengths()
{
return array(
3 => Horde_Service_Weather::FORECAST_3DAY,
5 => Horde_Service_Weather::FORECAST_5DAY,
7 => Horde_Service_Weather::FORECAST_7DAY,
10 => Horde_Service_Weather::FORECAST_10DAY
);
}
/**
* Perform an IP location search.
*
* @param string $ip The IP address to use.
*
* @return string The location code.
*/
protected function _getLocationByIp($ip)
{
if ($this->_ipIsUnique($ip)) {
return $this->_makeRequest(
self::API_URL . '/api/' . $this->_apiKey
. '/geolookup/q/autoip.json?geo_ip=' . $ip);
} else {
return $this->_makeRequest(
self::API_URL . '/api/' . $this->_apiKey
. '/geolookup/q/autoip.json');
}
}
/**
* Execute a location search.
*
* @param string $location The location text to search.
*
* @return string The location code result(s).
*/
protected function _searchLocations($location)
{
return $this->_makeRequest(self::API_URL . '/api/' . $this->_apiKey
. '/geolookup/q/' . $location . '.json');
}
/**
* Weather Underground allows requesting multiple features per request,
* and only counts it as a single request against your API key. So we trade
* a bit of request time/traffic for a smaller number of requests to obtain
* information for e.g., a typical weather portal display.
*/
protected function _getCommonElements($location, $length = null)
{
$length = empty($length) ? Horde_Service_Weather::FORECAST_10DAY : $length;
if (!empty($this->_forecast) && $location == $this->_lastLocation
&& $this->_lastLength >= $length) {
if ($this->_lastLength > $length) {
$this->_forecast->limitLength($length);
}
return;
}
$this->_lastLength = $length;
$this->_lastLocation = $location;
switch ($length) {
case Horde_Service_Weather::FORECAST_3DAY:
$l = 'forecast';
break;
case Horde_Service_Weather::FORECAST_5DAY:
case Horde_Service_Weather::FORECAST_7DAY:
$l = 'forecast7day';
break;
case Horde_Service_Weather::FORECAST_10DAY:
$l = 'forecast10day';
break;
}
$url = self::API_URL . '/api/' . $this->_apiKey
. '/geolookup/conditions/animatedradar/alerts/'
. $l . '/astronomy/q/' . $location . '.json';
$results = $this->_makeRequest($url, $this->_cache_lifetime);
$station = $this->_parseStation($results->location);
$this->_current = $this->_parseCurrent($results->current_observation);
if (!empty($results->alerts)) {
$this->_alerts = $this->_parseAlerts($results->alerts);
}
$astronomy = $results->moon_phase;
$date = clone $this->_current->time;
$date->hour = $astronomy->sunrise->hour;
$date->min = $astronomy->sunrise->minute;
$date->sec = 0;
$station->sunrise = $date;
$station->sunset = clone $date;
$station->sunset->hour = $astronomy->sunset->hour;
$station->sunset->min = $astronomy->sunset->minute;
// Station information doesn't include any type of name string, so
// get it from the currentConditions request.
$station->name = $results->current_observation->display_location->full;
$this->_station = $station;
$this->_forecast = $this->_parseForecast($results->forecast);
$this->_forecast->limitLength($length);
$this->link = $results->current_observation->image->link;
$this->title = $results->current_observation->image->title;
// Radar
if (isset($results->radar->image_url)) {
$this->_radar = $results->radar->image_url;
}
}
/**
* Parses the JSON response for a location request into a station object.
*
* @param StdClass $station The response from a Location request.
*
* @return Horde_Service_Weather_Station
*/
protected function _parseStation($station)
{
// @TODO: Create a subclass of Station for wunderground, parse the
// "close stations" and "pws" properties - allow for things like
// displaying other, nearby weather station conditions etc...
$properties = array(
'city' => $station->city,
'state' => $station->state,
'country' => $station->country_iso3166,
'country_name' => $station->country_name,
'tz' => isset($station->tz_long) ? $station->tz_long : '',
'lat' => isset($station->lat) ? $station->lat : '',
'lon' => isset($station->lon) ? $station->lon : '',
'zip' => isset($station->zip) ? $station->zip : '',
'code' => str_replace('/q/', '', $station->l)
);
return new Horde_Service_Weather_Station($properties);
}
/**
* Parses the forecast data.
*
* @param stdClass $forecast The result of the forecast request.
*
* @return Horde_Service_Weather_Forecast_WeatherUnderground The forecast.
*/
protected function _parseForecast($forecast)
{
return new Horde_Service_Weather_Forecast_WeatherUnderground(
(array)$forecast, $this);
}
/**
* Parses astronomy information. Returned as an array since this will be
* added to the station information.
*
* @param {[type]} $astronomy [description]
* @return {[type]}
*/
protected function _parseAstronomy($astronomy)
{
// For now, just cast to array and pass back, we need to normalize
// at least the moon data. (Given in percent illuminated and age -
// need to parse that into phases.)
return (array)$astronomy;
}
/**
* Parse the current_conditions response.
*
* @param stdClass $current The current_condition request response object
*
* @return Horde_Service_Weather_Current
*/
protected function _parseCurrent($current)
{
// The Current object takes care of the parsing/mapping.
return new Horde_Service_Weather_Current_WeatherUnderground((array)$current, $this);
}
protected function _parseSearchLocations($response)
{
if (!empty($response->response->error)) {
throw new Horde_Service_Weather_Exception($response->response->error->description);
}
if (!empty($response->response->results)) {
$results = array();
foreach ($response->response->results as $location) {
$results[] = $this->_parseStation($location);
}
return $results;
} else {
return $this->_parseStation($response->location);
}
}
protected function _parseAutocomplete($results)
{
$return = array();
foreach($results->RESULTS as $result) {
$new = new stdClass();
$new->name = $result->name;
$new->code = $result->l;
$return[] = $new;
}
return $return;
}
protected function _parseAlerts($alerts)
{
$alerts_ob = new Horde_Service_Weather_Alerts_WeatherUnderground($alerts, $this);
return $alerts_ob->getAlerts();
}
protected function _makeRequest($url, $lifetime = 86400)
{
$cachekey = md5('hordeweather' . $url);
if ((!empty($this->_cache) && !$results = $this->_cache->get($cachekey, $lifetime)) ||
empty($this->_cache)) {
$url = new Horde_Url($url);
$response = $this->_http->get((string)$url);
if (!$response->code == '200') {
throw new Horde_Service_Weather_Exception($response->code);
}
$results = $response->getBody();
if (!empty($this->_cache)) {
$this->_cache->set($cachekey, $results);
}
}
$results = Horde_Serialize::unserialize($results, Horde_Serialize::JSON);
if (!($results instanceof StdClass)) {
throw new Horde_Service_Weather_Exception('Error, unable to decode response.');
}
return $results;
}
}
Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/WeatherUnderground_Strings.php 0000664 0001750 0001750 00000007113 13055003005 026663 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather_Wwo
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Wwo extends Horde_Service_Weather_Base
{
const API_URL = 'http://api.worldweatheronline.com/free/v1/weather.ashx';
const SEARCH_URL = 'http://api.worldweatheronline.com/free/v1/search.ashx';
const API_URL_v2 = 'https://api.worldweatheronline.com/free/v2/weather.ashx';
const SEARCH_URL_v2 = 'https://api.worldweatheronline.com/free/v2/search.ashx';
/**
* @see Horde_Service_Weather_Base::$title
* @var string
*/
public $title = 'World Weather Online';
/**
* @see Horde_Service_Weather_Base::$link
* @var string
*/
public $link = 'http://worldweatheronline.com';
/**
* @see Horde_Service_Weather::$iconMap
*/
public $iconMap = array(
'wsymbol_0001_sunny' => '32.png',
'wsymbol_0002_sunny_intervals' => '30.png',
'wsymbol_0003_white_cloud' => '26.png',
'wsymbol_0004_black_low_cloud' => '26.png',
'wsymbol_0006_mist' => '34.png',
'wsymbol_0007_fog' => '20.png',
'wsymbol_0008_clear_sky_night' => '33.png',
'wsymbol_0009_light_rain_showers' => '11.png',
'wsymbol_0010_heavy_rain_showers' => '12.png',
'wsymbol_0011_light_snow_showers' => '14.png',
'wsymbol_0012_heavy_snow_showers' => '16.png',
'wsymbol_0013_sleet_showers' => '7.png',
'wsymbol_0016_thundery_showers' => '0.png',
'wsymbol_0017_cloudy_with_light_rain' => '11.png',
'wsymbol_0018_cloudy_with_heavy_rain' => '12.png',
'wsymbol_0019_cloudy_with_light_snow' => '13.png',
'wsymbol_0020_cloudy_with_heavy_snow' => '16.png',
'wsymbol_0021_cloudy_with_sleet' => '8.png',
'wsymbol_0024_thunderstorms' => '0.png',
'wsymbol_0025_light_rain_showers_night' => '40.png',
'wsymbol_0026_heavy_rain_showers_night' => '30.png',
'wsymbol_0027_light_snow_showers_night' => '41.png',
'wsymbol_0028_heavy_snow_showers_night' => '42.png',
'wsymbol_0029_sleet_showers_night' => '7.png',
'wsymbol_0032_thundery_showers_night' => '47.png',
'wsymbol_0033_cloudy_with_light_rain_night' => '45.png',
'wsymbol_0034_cloudy_with_heavy_rain_night' => '45.png',
'wsymbol_0035_cloudy_with_light_snow_night' => '46.png',
'wsymbol_0036_cloudy_with_heavy_snow_night' => '46.png',
'wsymbol_0037_cloudy_with_sleet_night' => '8.png',
'wsymbol_0040_thunderstorms_night' => '47.png'
);
/**
* Wwo API key.
*
* @var string
*/
protected $_key;
/**
* API Version
*
* @var integer
*/
protected $_version;
/**
* Constructor.
*
* @param array $params Parameters:
* - cache: (Horde_Cache) Optional Horde_Cache object.
* - cache_lifetime: (integer) Lifetime of cached data, if caching.
* - http_client: (Horde_Http_Client) Required http client object.
* - apikey: (string) Require api key for Wwo.
* - apiVersion: (integer) Version of the API to use.
* Defaults to v1 for BC reasons.
*
* @return Horde_Service_Weather_Wwo
*/
public function __construct(array $params = array())
{
// Check required api key parameters here...
if (empty($params['apikey'])) {
throw new InvalidArgumentException('Missing required API Key parameter.');
}
$this->_version = empty($params['apiVersion']) ? 1 : $params['apiVersion'];
$this->_key = $params['apikey'];
unset($params['apikey']);
parent::__construct($params);
}
/**
* Obtain the current observations.
*
* @see Horde_Service_Weather_Base::getCurrentConditions
*
* @return Horde_Service_Weather_Current_Wwo
*/
public function getCurrentConditions($location)
{
$this->_getCommonElements($location);
return $this->_current;
}
/**
* Obtain the forecast for the current location.
*
* @see Horde_Service_Weather_Base::getForecast
*/
public function getForecast(
$location,
$length = Horde_Service_Weather::FORECAST_3DAY,
$type = Horde_Service_Weather::FORECAST_TYPE_STANDARD)
{
$this->_getCommonElements($location);
return $this->_forecast;
}
/**
* Search for a valid location code.
*
* @see Horde_Service_Weather_Base::searchLocations
*/
public function searchLocations($location, $type = Horde_Service_Weather::SEARCHTYPE_STANDARD)
{
switch ($type) {
case Horde_Service_Weather::SEARCHTYPE_STANDARD:
case Horde_Service_Weather::SEARCHTYPE_IP:
return $this->_parseSearchLocations($this->_searchLocations($location));
}
}
/**
* Return an autocomplete request result.
*
* @see Horde_Service_Weather_Base::autocompleteLocation
*/
public function autocompleteLocation($search)
{
$url = new Horde_Url($this->_version == 1 ? self::SEARCH_URL : self::SEARCH_URL_v2);
$url->add(array(
'q' => $search,
'format' => 'json',
'num_of_results' => 25));
return $this->_parseAutocomplete($this->_makeRequest($url));
}
/**
* Return the supported forecast lengths.
*
* @see Horde_Service_Weather_Base::getSupportedForecastLengths
*/
public function getSupportedForecastLengths()
{
return array(
3 => Horde_Service_Weather::FORECAST_3DAY,
5 => Horde_Service_Weather::FORECAST_5DAY
);
}
/**
* Populates some common data used by forecasts and current conditions.
* Weather Underground allows requesting multiple features per request,
* and only counts it as a single request against your API key. So we trade
* a bit of request time/traffic for a smaller number of requests to obtain
* information for e.g., a typical weather portal display.
*
* @param string $location The location identifier.
* @param integer $length The forecast length.
*/
protected function _getCommonElements($location, $length = Horde_Service_Weather::FORECAST_5DAY)
{
if (!empty($this->_current) && $location == $this->_lastLocation
&& $this->_lastLength == $length) {
return;
}
$this->_lastLength = $length;
$this->_lastLocation = $location;
$url = new Horde_Url($this->_version == 1 ? self::API_URL : self::API_URL_v2);
// Not sure why, but Wwo chokes if we urlencode the location?
$url->add(array(
'q' => $location,
'num_of_days' => $length,
'includeLocation' => 'yes',
'extra' => 'localObsTime')
);
if ($this->_version == 1) {
$url->add(array(
'timezone' => 'yes')
);
}
// V2 of the API only returns hourly data, so ask for 24 hour avg.
// @todo Revisit when we support hourly forecast data.
if ($this->_version == 2) {
$url->add(array(
'tp' => 24,
'showlocaltime' => 'yes',
'showmap' => 'yes')
);
}
$results = $this->_makeRequest($url);
// Use the minimum station data provided by forecast request to
// fetch the full station data.
$station = $this->_parseStation($results->data->nearest_area[0]);
if (empty($station)) {
throw new Horde_Service_Weather_Exception('Location not found.');
}
$station = $this->searchLocations($station->lat . ',' . $station->lon);
// Hack some data to allow UTC observation time to be returned.
$results->data->current_condition[0]->date = new Horde_Date($results->data->current_condition[0]->localObsDateTime);
if (preg_match('/^(\+|-)?(\d{2}):(\d{2})/', $station->getOffset(), $match)) {
$factor = $match[1] == '-' ? -1 : 1;
$results->data->current_condition[0]->date->hour += $factor * $match[2];
$results->data->current_condition[0]->date->min += $factor * $match[3];
}
// Parse it.
$this->_current = $this->_parseCurrent($results->data->current_condition);
// Sunrise/Sunset
// @todo - this is now available in the forecast section in v2
$date = $this->_current->time;
$station->sunset = new Horde_Date(
date_sunset(
$date->timestamp(),
SUNFUNCS_RET_TIMESTAMP,
$station->lat,
$station->lon)
);
$station->sunrise = new Horde_Date(
date_sunrise(
$date->timestamp(),
SUNFUNCS_RET_TIMESTAMP,
$station->lat,
$station->lon)
);
$station->time = (string)$date;
$this->_station = $station;
$this->_forecast = $this->_parseForecast($results->data->weather);
}
/**
* Parses the JSON response for a location request into a station object.
*
* @param StdClass $station The response from a Location request.
*
* @return Horde_Service_Weather_Station
*/
protected function _parseStation($station)
{
$properties = array(
'name' => $station->areaName[0]->value . ', ' . $station->region[0]->value,
'city' => $station->areaName[0]->value,
'state' => $station->region[0]->value,
'country' => $station->country[0]->value,
'country_name' => '',
'lat' => $station->latitude,
'lon' => $station->longitude,
'zip' => '',
'code' => $station->latitude . ',' . $station->longitude
);
if (isset($station->timezone)) {
// Only the *current* UTC offset is provided, with no indication
// if we are in DST or not.
$properties['tz'] = $station->timezone->offset;
}
return new Horde_Service_Weather_Station($properties);
}
/**
* Parses the forecast data.
*
* @param stdClass $forecast The result of the forecast request.
*
* @return Horde_Service_Weather_Forecast_Wwo The forecast.
*/
protected function _parseForecast($forecast)
{
$forecast = $this->_version == 1
? new Horde_Service_Weather_Forecast_Wwo($forecast, $this)
: new Horde_Service_Weather_Forecast_Wwov2($forecast, $this);
return $forecast;
}
/**
* Parse the current_conditions response.
*
* @param stdClass $current The current_condition request response object
*
* @return Horde_Service_Weather_Current
*/
protected function _parseCurrent($current)
{
// The Current object takes care of the parsing/mapping.
$current = new Horde_Service_Weather_Current_Wwo($current[0], $this);
return $current;
}
protected function _parseAutocomplete($results)
{
$return = array();
if (!empty($results->search_api->result)) {
foreach($results->search_api->result as $result) {
if (!empty($result->region[0]->value)) {
$new = new stdClass();
$new->name = $result->areaName[0]->value . ', ' . $result->region[0]->value;
$new->code = $result->latitude . ',' . $result->longitude;
$return[] = $new;
}
}
}
return $return;
}
/**
* Execute a location search.
*
* @param string $location The location text to search.
*
* @return string The location code result(s).
*/
protected function _searchLocations($location)
{
$url = new Horde_Url($this->_version == 1 ? self::SEARCH_URL : self::SEARCH_URL_v2);
$url = $url->add(array(
'timezone' => 'yes',
'q' => $location,
'num_of_results' => 10));
return $this->_makeRequest($url);
}
protected function _parseSearchLocations($response)
{
if (!empty($response->results->error)) {
throw new Horde_Service_Weather_Exception($response->results->error->message);
}
// Wwo's location search is pretty useless. It *always* returns multiple
// matches, even if you pass an explicit identifier. We need to ignore
// these, and hope for the best.
if (!empty($response->search_api->result)) {
return $this->_parseStation($response->search_api->result[0]);
}
return array();
}
/**
* Make the remote API call.
*
* @param Horde_Url $url The endpoint.
*
* @return mixed The unserialized results form the remote API call.
* @throws Horde_Service_Weather_Exception
*/
protected function _makeRequest(Horde_Url $url)
{
$url->add(array(
'format' => 'json',
'key' => $this->_key
))->setRaw(true);
$cachekey = md5('hordeweather' . $url);
if ((!empty($this->_cache) &&
!($results = $this->_cache->get($cachekey, $this->_cache_lifetime))) ||
empty($this->_cache)) {
$response = $this->_http->get((string)$url);
if (!$response->code == '200') {
throw new Horde_Service_Weather_Exception($response->code);
}
$results = $response->getBody();
if (!empty($this->_cache)) {
$this->_cache->set($cachekey, $results);
}
}
$results = Horde_Serialize::unserialize($results, Horde_Serialize::JSON);
if (!($results instanceof StdClass)) {
throw new Horde_Service_Weather_Exception(sprintf(
'Error, unable to decode response: %s',
$results));
}
return $results;
}
}
Horde_Service_Weather-2.5.4/lib/Horde/Service/Weather/Wwo_Strings.php 0000664 0001750 0001750 00000002605 13055003005 023624 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/
/**
* Horde_Service_Weather class
*
* @author Michael J Rubinsky
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather
{
/** Forecast length constants **/
const FORECAST_3DAY = 3;
const FORECAST_5DAY = 5;
const FORECAST_7DAY = 7;
const FORECAST_10DAY = 10;
/** Standard forecast summary **/
const FORECAST_TYPE_STANDARD = 1;
/** Detailed forecast, contains a day/night component for each day **/
const FORECAST_TYPE_DETAILED = 2;
/** Hourly forecast **/
const FORECAST_TYPE_HOURLY = 3;
const FORECAST_FIELD_WIND = 'wind';
const FORECAST_FIELD_PRECIPITATION = 'pop';
const FORECAST_FIELD_HUMIDITY = 'humidity';
const FORECAST_FIELD_ACCUMULATION = 'accum';
/** Unit constants **/
const UNITS_STANDARD = 1;
const UNITS_METRIC = 2;
/** Conversion constants **/
const CONVERSION_MPH_TO_KNOTS = 0.868976242;
const CONVERSION_KPH_TO_KNOTS = 0.5399568;
const CONVERSION_MPH_TO_KPH = 1.609344;
const CONVERSION_KPH_TO_MPH = 0.621371192;
const CONVERSION_MB_TO_INCHES = 0.0295301;
const CONVERSION_KM_TO_SM = 0.6213699;
/** Location search types **/
const SEARCHTYPE_STANDARD = 1;
const SEARCHTYPE_IP = 2;
const SEARCHTYPE_ZIP = 3;
const SEARCHTYPE_CITYSTATE = 4;
/**
* Utility function to return textual cardinal compass directions from degress.
*
* @param integer $degree The degree direction (0 - 360).
*
* @return string The cardinal direction.
* @since 2.3.0
*/
public static function degToDirection($degree)
{
$cardinal = array('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW');
$deg_delta = (int)($degree/22.5 + .5);
return $cardinal[$deg_delta % 16];
}
/**
* Calculate windchill from temperature and windspeed.
*
* Temperature has to be entered in deg F, speed in mph!
*
* @param double $temperature The temperature in degrees F.
* @param double $speed The wind speed in MPH.
*
* @return double The windchill factor.
* @link http://www.nws.noaa.gov/om/windchill/
*/
public static function calculateWindChill($temperature, $speed)
{
return (35.74 + 0.6215 * $temperature - 35.75 * pow($speed, 0.16) + 0.4275 * $temperature * pow($speed, 0.16));
}
/**
* 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 double $temperature Temperature in degrees C.
* @param double $dewPoint Dewpoint in degrees C.
*
* @return double
* @link http://www.faqs.org/faqs/meteorology/temp-dewpoint/
*/
public static 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);
}
/**
* 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 double $temperature Temperature in degrees C.
* @param double $humidity Humidity.
*
* @return double
* @link http://www.faqs.org/faqs/meteorology/temp-dewpoint/
*/
public static 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));
}
/**
* Convert pressure between in, hpa, mb, mm and atm
*
* @param double $pressure The pressure in $from units.
* @param string $from Units converting from.
* @param string $to Units converting to.
*
* @return float The converted pressure
*/
public static function convertPressure($pressure, $from, $to)
{
$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
)
);
$from = strtolower($from);
$to = strtolower($to);
return ($pressure * $factor[$from][$to]);
}
/**
* Convert speed between mph, kph, 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 double $speed The speed in $from units.
* @param string $from The units to convert from.
* @param string $to The units to convert to.
*
* @return double|integer|boolean
* @link http://www.spc.noaa.gov/faq/tornado/beaufort.html
*/
public static function convertSpeed($speed, $from, $to)
{
$factor = array(
'mph' => array(
'mph' => 1,
'kph' => 1.609344,
'kt' => 0.8689762,
'mps' => 0.44704,
'fps' => 1.4666667
),
'kph' => array(
'mph' => 0.6213712,
'kph' => 1,
'kt' => 0.5399568,
'mps' => 0.2777778,
'fps' => 0.9113444
),
'kt' => array(
'mph' => 1.1507794,
'kph' => 1.852,
'kt' => 1,
'mps' => 0.5144444,
'fps' => 1.6878099
),
'mps' => array(
'mph' => 2.2369363,
'kph' => 3.6,
'kt' => 1.9438445,
'mps' => 1,
'fps' => 3.2808399
),
'fps' => array(
'mph' => 0.6818182,
'kph' => 1.09728,
'kt' => 0.5924838,
'mps' => 0.3048,
'fps' => 1
)
);
$from = strtolower($from);
$to = strtolower($to);
if ($from == 'bft') {
return false;
} elseif ($to == 'bft') {
$beaufort = array(
1, 3, 6, 10,
16, 21, 27, 33,
40, 47, 55, 63
);
$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]);
}
}
/**
* Convert distance between m, km, ft and sm
*
* @param double $distance The distance in $from units.
* @param string $from The units to convert from.
* @param string $to The units to convert to.
*
* @return double
*/
public static function convertDistance($distance, $from, $to)
{
$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
)
);
$to = strtolower($to);
$from = strtolower($from);
return round($distance * $factor[$from][$to]);
}
/**
* Convert temperature between f and c
*
* @param double $temperature The temperature in $from units.
* @param string $from Units to convert from.
* @param string $to Units to convert to.
*
* @return double
*/
public static 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];
}
} Horde_Service_Weather-2.5.4/locale/de/LC_MESSAGES/Horde_Service_Weather.mo 0000664 0001750 0001750 00000032370 13055003005 024173 0 ustar jan jan t 9 / > N ] u
% 6
C Q b o t
,
@ K
^ i | "
3 > N g
s ~ # # > b u
0 < H [ n
0 I [ s "
& 8 K W # j #
3 A
U ` r
+ 3 _ ( } +
) 7
N
\ j w y }
&