[Phphtmllib-devel] SF.net SVN: phphtmllib:[3440] trunk/open2300/lib/modules/api/ AviationWeather.in
Status: Beta
Brought to you by:
hemna
From: <he...@us...> - 2010-06-15 17:49:54
|
Revision: 3440 http://phphtmllib.svn.sourceforge.net/phphtmllib/?rev=3440&view=rev Author: hemna Date: 2010-06-15 17:49:47 +0000 (Tue, 15 Jun 2010) Log Message: ----------- first stab Added Paths: ----------- trunk/open2300/lib/modules/api/AviationWeather.inc Added: trunk/open2300/lib/modules/api/AviationWeather.inc =================================================================== --- trunk/open2300/lib/modules/api/AviationWeather.inc (rev 0) +++ trunk/open2300/lib/modules/api/AviationWeather.inc 2010-06-15 17:49:47 UTC (rev 3440) @@ -0,0 +1,423 @@ +<?php + +/** + * This class is meant to act as an API + * to fetch METAR information from a remote + * site, then process it and combine it with the + * airport data and return it as a JSON object. + * + * This can handle requests for multiple airports + * at once. + * + * @author waboring + * + */ +class AviationWeather extends JSONWidget { + + const ID = "avwx"; + + protected $url = "http://api.itimeteo.com/getMetar.ims?icao=XX_ICAO_XX&decoded=true&displayAirport=true&format=json"; + + //list of airports to fetch. + protected $airports; + + public function init() { + + } + + public function build_object() { + + } + + + public function get_metar($station, &$wxInfo) { + // This function retrieves METAR information for a given station from the + // National Weather Service. It assumes that the station exists. + // A slower URL is "ftp://weather.noaa.gov/data/observations/metar/stations/$station.TXT" + $fileName = "http://weather.noaa.gov/pub/data/observations/metar/stations/$station.TXT"; + $metar = ''; + + + $proxy = $GLOBALS["config"]->get("proxy"); + if (!empty($proxy)) { + $context = stream_context_create(array("http" => array("proxy" => "tcp://".$proxy))); + } else { + $context = null; + } + + $fileData = @file($fileName, 0, $context); // or die('Data not available'); + if ($fileData != false) { + list($i, $date) = each($fileData); + $utc = strtotime(trim($date)); + $this->set_time_data($utc,$wxInfo); + while (list($i, $line) = each($fileData)) { + $metar .= ' ' . trim($line); + } + $metar = trim(str_replace(' ', ' ', $metar)); + } + return $metar; + } + + protected function set_time_data($utc, &$wxInfo) { + // This function formats observation time in the local time zone of server, the + // current local time on server, and time difference since observation. $utc is a + // UNIX timestamp for Universal Coordinated Time (Greenwich Mean Time or Zulu Time). + $timeZoneOffset = date('Z'); + $local = $utc + $timeZoneOffset; + $wxInfo['OBSERVED'] = date('D M j, H:i T',$local); + $now = time(); + $wxInfo['NOW'] = date('D M j, H:i T',$now); + $timeDiff = floor(($now - $local) / 60); + if ($timeDiff < 91) $wxInfo['AGE'] = "$timeDiff min ago"; + else { + $min = $timeDiff % 60; + if ($min < 10) $min = '0' . $min; + $wxInfo['AGE'] = floor($timeDiff / 60) . ":$min hr ago"; + } + } + + public function process_metar($metar, &$wxInfo) { + // This function directs the examination of each group of the METAR. The problem + // with a METAR is that not all the groups have to be there. Some groups could be + // missing. Fortunately, the groups must be in a specific order. (This function + // also assumes that a METAR is well-formed, that is, no typographical mistakes.) + // This function uses a function variable to organize the sequence in which to + // decode each group. Each function checks to see if it can decode the current + // METAR part. If not, then the group pointer is advanced for the next function + // to try. If yes, the function decodes that part of the METAR and advances the + // METAR pointer and group pointer. (If the function can be called again to + // decode similar information, then the group pointer does not get advanced.) + if ($metar != '') { + $metarParts = explode(' ',$metar); + $groupName = array('get_station','get_time','get_station_type','get_wind','get_var_wind','get_visibility','get_runway','get_conditions','get_cloud_cover','get_temperature','get_altimeter'); + $metarPtr = 1; // get_station identity is ignored + $group = 1; + while ($group < count($groupName)) { + $part = $metarParts[$metarPtr]; + call_user_func_array(array($this, $groupName[$group]), array($part,&$metarPtr,&$group,&$wxInfo)); + //$groupName[$group]($part,$metarPtr,$group,$wxInfo); // $groupName is a function variable + } + } + else $wxInfo['ERROR'] = 'Data not available'; + } + + protected function get_station($part, &$metarPtr, &$group, &$wxInfo) { + // Ignore station code. Script assumes this matches requesting $station. + // This function is never called. It is here for completeness of documentation. + if (strlen($part) == 4 and $group == 0) { + $group++; + $metarPtr++; + } + } + + protected function get_time($part, &$metarPtr, &$group, &$wxInfo) { + // Ignore observation time. This information is found in the first line of the NWS file. + // Format is ddhhmmZ where dd = day, hh = hours, mm = minutes in UTC time. + if (substr($part,-1) == 'Z') $metarPtr++; + $group++; + } + + protected function get_station_type($part, &$metarPtr, &$group, &$wxInfo) { + // Ignore station type if present. + if ($part == 'AUTO' || $part == 'COR') $metarPtr++; + $group++; + } + + protected function get_wind($part, &$metarPtr, &$group, &$wxInfo) { + // Decodes wind direction and speed information. + // Format is dddssKT where ddd = degrees from North, ss = speed, KT for knots, + // or dddssGggKT where G stands for gust and gg = gust speed. (ss or gg can be a 3-digit number.) + // KT can be replaced with MPH for meters per second or KMH for kilometers per hour. + + function speed($part, $unit) { + // Convert wind speed into miles per hour. + // Some other common conversion factors (to 6 significant digits): + // 1 mi/hr = 1.15080 knots = 0.621371 km/hr = 2.23694 m/s + // 1 ft/s = 1.68781 knots = 0.911344 km/hr = 3.28084 m/s + // 1 knot = 0.539957 km/hr = 1.94384 m/s + // 1 km/hr = 1.852 knots = 3.6 m/s + // 1 m/s = 0.514444 knots = 0.277778 km/s + if ($unit == 'KT') $speed = round(1.1508 * $part); // from knots + elseif ($unit == 'MPS') $speed = round(2.23694 * $part); // from meters per second + else $speed = round(0.621371 * $part); // from km per hour + $speed = "$speed mph"; + return $speed; + } + + if (preg_match('/^([0-9G]{5,10}|VRB[0-9]{2,3})(KT|MPS|KMH)$/',$part,$pieces)) { + $part = $pieces[1]; + $unit = $pieces[2]; + if ($part == '00000') { + $wxInfo['WIND'] = 'calm'; // no wind + } + else { + preg_match('/([0-9]{3}|VRB)([0-9]{2,3})G?([0-9]{2,3})?/',$part,$pieces); + if ($pieces[1] == 'VRB') $direction = 'varies'; + else { + $angle = (integer) $pieces[1]; + $compass = array('N','NNE','NE','ENE','E','ESE','SE','SSE','S','SSW','SW','WSW','W','WNW','NW','NNW'); + $direction = $compass[round($angle / 22.5) % 16]; + } + if ($pieces[3] == 0) $gust = ''; + else $gust = ', gusting to ' . speed($pieces[3], $unit); + $wxInfo['WIND'] = $direction . ' at ' . speed($pieces[2], $unit) . $gust; + } + $metarPtr++; + } + $group++; + } + + protected function get_var_wind($part, &$metarPtr, &$group, &$wxInfo) { + // Ignore variable wind direction information if present. + // Format is fffVttt where V stands for varies from fff degrees to ttt degrees. + if (preg_match('/([0-9]{3})V([0-9]{3})/',$part,$pieces)) $metarPtr++; + $group++; + } + + protected function get_visibility($part, &$metarPtr, &$group, &$wxInfo) { + // Decodes visibility information. This function will be called a second time + // if visibility is limited to an integer mile plus a fraction part. + // Format is mmSM for mm = statute miles, or m n/dSM for m = mile and n/d = fraction of a mile, + // or just a 4-digit number nnnn (with leading zeros) for nnnn = meters. + static $integerMile = ''; + if (strlen($part) == 1) { // visibility is limited to a whole mile plus a fraction part + $integerMile = $part . ' '; + $metarPtr++; + } else if (substr($part,-2) == 'SM') { // visibility is in miles + $part = substr($part,0,strlen($part)-2); + if (substr($part,0,1) == 'M') { + $prefix = 'less than '; + $part = substr($part, 1); + } else { + $prefix = ''; + } + if (($integerMile == '' && preg_match('/[\/]/',$part,$pieces)) || $part == '1') { + $unit = ' mile'; + } else { + $unit = ' miles'; + } + $wxInfo['VISIBILITY'] = $prefix . $integerMile . $part . $unit; + $metarPtr++; + $group++; + + } else if (substr($part,-2) == 'KM') { // unknown (Reported by NFFN in Fiji) + $metarPtr++; + $group++; + + } elseif (preg_match('/^([0-9]{4})$/',$part,$pieces)) { // visibility is in meters + $distance = round($part/ 621.4, 1); // convert to miles + if ($distance > 5) $distance = round($distance); + if ($distance <= 1) $unit = ' mile'; + else $unit = ' miles'; + $wxInfo['VISIBILITY'] = $distance . $unit; + $metarPtr++; + $group++; + + } else if ($part == 'CAVOK') { // good weather + $wxInfo['VISIBILITY'] = 'greater than 7 miles'; // or 10 km + $wxInfo['CONDITIONS'] = ''; + $wxInfo['CLOUDS'] = 'clear skies'; + $metarPtr++; + $group += 4; // can skip the next 3 groups + + } else { + $group++; + } + } + + protected function get_runway($part, &$metarPtr, &$group, &$wxInfo) { + // Ignore runway information if present. Maybe called a second time. + // Format is Rrrr/vvvvFT where rrr = runway number and vvvv = visibility in feet. + if (substr($part,0,1) == 'R') { + $metarPtr++; + } else { + $group++; + } + } + + protected function get_conditions($part, &$metarPtr, &$group, &$wxInfo) { + // Decodes current weather conditions. This function maybe called several times + // to decode all conditions. To learn more about weather condition codes, visit section + // 12.6.8 - Present Weather Group of the Federal Meteorological Handbook No. 1 at + // www.nws.noaa.gov/oso/oso1/oso12/fmh1/fmh1ch12.htm + static $conditions = ''; + static $wxCode = array( + 'VC' => 'nearby', + 'MI' => 'shallow', + 'PR' => 'partial', + 'BC' => 'patches of', + 'DR' => 'low drifting', + 'BL' => 'blowing', + 'SH' => 'showers', + 'TS' => 'thunderstorm', + 'FZ' => 'freezing', + 'DZ' => 'drizzle', + 'RA' => 'rain', + 'SN' => 'snow', + 'SG' => 'snow grains', + 'IC' => 'ice crystals', + 'PE' => 'ice pellets', + 'GR' => 'hail', + 'GS' => 'small hail', // and/or snow pellets + 'UP' => 'unknown', + 'BR' => 'mist', + 'FG' => 'fog', + 'FU' => 'smoke', + 'VA' => 'volcanic ash', + 'DU' => 'widespread dust', + 'SA' => 'sand', + 'HZ' => 'haze', + 'PY' => 'spray', + 'PO' => 'well-developed dust/sand whirls', + 'SQ' => 'squalls', + 'FC' => 'funnel cloud, tornado, or waterspout', + 'SS' => 'sandstorm/duststorm'); + if (preg_match('/^(-|\+|VC)?(TS|SH|FZ|BL|DR|MI|BC|PR|RA|DZ|SN|SG|GR|GS|PE|IC|UP|BR|FG|FU|VA|DU|SA|HZ|PY|PO|SQ|FC|SS|DS)+$/',$part,$pieces)) { + if (strlen($conditions) == 0) $join = ''; + else $join = ' & '; + if (substr($part,0,1) == '-') { + $prefix = 'light '; + $part = substr($part,1); + } + elseif (substr($part,0,1) == '+') { + $prefix = 'heavy '; + $part = substr($part,1); + } + else $prefix = ''; // moderate conditions have no descriptor + $conditions .= $join . $prefix; + // The 'showers' code 'SH' is moved behind the next 2-letter code to make the English translation read better. + if (substr($part,0,2) == 'SH') $part = substr($part,2,2) . substr($part,0,2). substr($part, 4); + while ($code = substr($part,0,2)) { + $conditions .= $wxCode[$code] . ' '; + $part = substr($part,2); + } + $wxInfo['CONDITIONS'] = $conditions; + $metarPtr++; + } + else { + $wxInfo['CONDITIONS'] = $conditions; + $group++; + } + } + + protected function get_cloud_cover($part, &$metarPtr, &$group, &$wxInfo) { + // Decodes cloud cover information. This function maybe called several times + // to decode all cloud layer observations. Only the last layer is saved. + // Format is SKC or CLR for clear skies, or cccnnn where ccc = 3-letter code and + // nnn = altitude of cloud layer in hundreds of feet. 'VV' seems to be used for + // very low cloud layers. (Other conversion factor: 1 m = 3.28084 ft) + static $cloudCode = array( + 'SKC' => 'clear skies', + 'CLR' => 'clear skies', + 'FEW' => 'partly cloudy', + 'SCT' => 'scattered clouds', + 'BKN' => 'mostly cloudy', + 'OVC' => 'overcast', + 'VV' => 'vertical visibility'); + if ($part == 'SKC' || $part == 'CLR') { + $wxInfo['CLOUDS'] = $cloudCode[$part]; + $metarPtr++; + $group++; + } + else { + if (preg_match('/^([A-Z]{2,3})([0-9]{3})/',$part,$pieces)) { // codes for CB and TCU are ignored + $wxInfo['CLOUDS'] = $cloudCode[$pieces[1]]; + if ($pieces[1] == 'VV') { + $altitude = (integer) 100 * $pieces[2]; // units are feet + $wxInfo['CLOUDS'] .= " to $altitude ft"; + } + $metarPtr++; + } + else { + $group++; + } + } + } + + protected function get_temperature($part, &$metarPtr, &$group, &$wxInfo) { + // Decodes temperature and dew point information. Relative humidity is calculated. Also, + // depending on the temperature, Heat Index or Wind Chill Temperature is calculated. + // Format is tt/dd where tt = temperature and dd = dew point temperature. All units are + // in Celsius. A 'M' preceeding the tt or dd indicates a negative temperature. Some + // stations do not report dew point, so the format is tt/ or tt/XX. + + function get_heat_index($tempF, $rh, &$wxInfo) { + // Calculate Heat Index based on temperature in F and relative humidity (65 = 65%) + if ($tempF > 79 && $rh > 39) { + $hiF = -42.379 + 2.04901523 * $tempF + 10.14333127 * $rh - 0.22475541 * $tempF * $rh; + $hiF += -0.00683783 * pow($tempF, 2) - 0.05481717 * pow($rh, 2); + $hiF += 0.00122874 * pow($tempF, 2) * $rh + 0.00085282 * $tempF * pow($rh, 2); + $hiF += -0.00000199 * pow($tempF, 2) * pow($rh, 2); + $hiF = round($hiF); + $hiC = round(($hiF - 32) / 1.8); + $wxInfo['HEAT INDEX'] = "$hiF°F ($hiC°C)"; + } + } + + function get_wind_chill($tempF, &$wxInfo) { + // Calculate Wind Chill Temperature based on temperature in F and + // wind speed in miles per hour + if ($tempF < 51 && $wxInfo['WIND'] != 'calm') { + $pieces = explode(' ', $wxInfo['WIND']); + $windspeed = (integer) $pieces[2]; // wind speed must be in miles per hour + if ($windspeed > 3) { + $chillF = 35.74 + 0.6215 * $tempF - 35.75 * pow($windspeed, 0.16) + 0.4275 * $tempF * pow($windspeed, 0.16); + $chillF = round($chillF); + $chillC = round(($chillF - 32) / 1.8); + $wxInfo['WIND CHILL'] = "$chillF°F ($chillC°C)"; + } + } + } + + if (preg_match('/^(M?[0-9]{2})\/(M?[0-9]{2}|[X]{2})?$/',$part,$pieces)) { + $tempC = (integer) strtr($pieces[1], 'M', '-'); + $tempF = round(1.8 * $tempC + 32); + $wxInfo['TEMP'] = "$tempF°F ($tempC°C)"; + get_wind_chill($tempF, $wxInfo); + if (strlen($pieces[2]) != 0 && $pieces[2] != 'XX') { + $dewC = (integer) strtr($pieces[2], 'M', '-'); + $dewF = round(1.8 * $dewC + 32); + $wxInfo['DEWPT'] = "$dewF°F ($dewC°C)"; + $rh = round(100 * pow((112 - (0.1 * $tempC) + $dewC) / (112 + (0.9 * $tempC)), 8)); + $wxInfo['HUMIDITY'] = $rh . '%'; + get_heat_index($tempF, $rh, $wxInfo); + } + $metarPtr++; + $group++; + } + else { + $group++; + } + } + + protected function get_altimeter($part, &$metarPtr, &$group, &$wxInfo) { + // Decodes altimeter or barometer information. + // Format is Annnn where nnnn represents a real number as nn.nn in inches of Hg, + // or Qpppp where pppp = hectoPascals. + // Some other common conversion factors: + // 1 millibar = 1 hPa + // 1 in Hg = 0.02953 hPa + // 1 mm Hg = 25.4 in Hg = 0.750062 hPa + // 1 lb/sq in = 0.491154 in Hg = 0.014504 hPa + // 1 atm = 0.33421 in Hg = 0.0009869 hPa + if (preg_match('/^(A|Q)([0-9]{4})/',$part,$pieces)) { + if ($pieces[1] == 'A') { + $pressureIN = substr($pieces[2],0,2) . '.' . substr($pieces[2],2); // units are inches Hg + $pressureHPA = round($pressureIN / 0.02953); // convert to hectoPascals + } + else { + $pressureHPA = (integer) $pieces[2]; // units are hectoPascals + $pressureIN = round(0.02953 * $pressureHPA,2); // convert to inches Hg + } + $wxInfo['BAROMETER'] = "$pressureHPA hPa ($pressureIN in Hg)"; + $metarPtr++; + $group++; + } + else { + $group++; + } + } + +} + +?> \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |