Skip to content

Commit

Permalink
Add METAR/TAF data source support.
Browse files Browse the repository at this point in the history
Parsing code ported/refactored from PEAR/Services_Weather_Metar.
Still lots to do, but this gets things started.
  • Loading branch information
mrubinsk committed Aug 8, 2016
1 parent 04f4b54 commit c945c11
Show file tree
Hide file tree
Showing 6 changed files with 1,786 additions and 2 deletions.
295 changes: 295 additions & 0 deletions framework/Service_Weather/lib/Horde/Service/Weather.php
Expand Up @@ -47,9 +47,11 @@ class Horde_Service_Weather

/** 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;
Expand All @@ -73,4 +75,297 @@ public static function degToDirection($degree)
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, kmh, kt, mps, fps and bft
*
* Function will return 'false' when trying to convert from
* Beaufort, as it is a scale and not a true measurement
*
* @param 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,
'kmh' => 1.609344,
'kt' => 0.8689762,
'mps' => 0.44704,
'fps' => 1.4666667
),
'kmh' => array(
'mph' => 0.6213712,
'kmh' => 1,
'kt' => 0.5399568,
'mps' => 0.2777778,
'fps' => 0.9113444
),
'kt' => array(
'mph' => 1.1507794,
'kmh' => 1.852,
'kt' => 1,
'mps' => 0.5144444,
'fps' => 1.6878099
),
'mps' => array(
'mph' => 2.2369363,
'kmh' => 3.6,
'kt' => 1.9438445,
'mps' => 1,
'fps' => 3.2808399
),
'fps' => array(
'mph' => 0.6818182,
'kmh' => 1.09728,
'kt' => 0.5924838,
'mps' => 0.3048,
'fps' => 1
)
);

$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 ($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];
}

}
@@ -0,0 +1,71 @@
<?php
/**
* Copyright 2011-2016 Horde LLC (http://www.horde.org/)
*
* @author Michael J Rubinsky <mrubinsk@horde.org>
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Service_Weather
*/

/**
* Horde_Service_Weather_Current_Metar
*
* Responsible for parsing encoded METAR text and presenting human readable
* weather data.
*
* Parsing code adapted from PEAR's Services_Weather_Metar class. Original
* phpdoc attributes as follows:
* @author Alexander Wirtz <alex@pc4p.net>
* @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 <mrubinsk@horde.org>
* @category Horde
* @package Service_Weather
*/
class Horde_Service_Weather_Current_Metar extends Horde_Service_Weather_Current_Base
{
protected $_map = array(
'dewpoint' => 'dewPoint',
'wind_direction' => 'windDirection',
'wind_degrees' => 'windDegrees',
'wind_speed' => 'wind',
'wind_guest' => 'windGust',
'wind_chill' => 'feltTemperature',
'temp' => 'temperature',
);

public function __get($property)
{
switch ($property) {
// These are unsupported
case 'logo_url':
case 'heat_index':
case 'icon':
case 'icon_url':
return null;
case 'pressure_trend':
return !empty($this->_properties['remark']['presschg'])
? $this->_properties['remark']['presschg']
: null;
case 'visibility':
return (!empty($this->_properties['visQualifier'])
? $this->_properties['visQualifier']
: '') . ' ' . $this->_properties['visibility'];
case 'condition':
// @todo - need to build this from other properties.
break;
default:
if (!empty($this->_properties[$property])) {
return $this->_properties[$property];
} elseif (!empty($this->_map[$property])) {
return $this->_properties[$this->_map[$property]];
}

throw new Horde_Service_Weather_Exception_InvalidProperty();
}
}

}

0 comments on commit c945c11

Please sign in to comment.