View source
<?php
function weather_parse_metar($metar_raw_string) {
$metar = array();
$metar['#raw'] = $metar_raw_string;
$raw_items = preg_split('/\\s+/', strtoupper($metar_raw_string));
foreach ($raw_items as $metar_raw) {
if (_weather_parse_stop($metar_raw, $metar)) {
break;
}
_weather_parse_icao($metar_raw, $metar);
_weather_parse_timestamp($metar_raw, $metar);
_weather_parse_reporttype($metar_raw, $metar);
_weather_parse_wind($metar_raw, $metar);
_weather_parse_visibility($metar_raw, $metar);
_weather_parse_condition($metar_raw, $metar);
_weather_parse_phenomena($metar_raw, $metar);
_weather_parse_temperature($metar_raw, $metar);
_weather_parse_pressure($metar_raw, $metar);
}
_weather_calculate_sunrise_sunset($metar);
return $metar;
}
function _weather_parse_stop($metar_raw, &$metar) {
if (preg_match('/^(BECMG|TEMPO|NOSIG|RMK)$/', $metar_raw)) {
return TRUE;
}
else {
return FALSE;
}
}
function _weather_parse_icao($metar_raw, &$metar) {
if (preg_match('/^([A-Z]{4}|K[A-Z0-9]{3})$/', $metar_raw) and !isset($metar['icao'])) {
$metar['icao'] = $metar_raw;
}
}
function _weather_parse_timestamp($metar_raw, &$metar) {
if (preg_match('/^([0-9]{2})([0-9]{2})([0-9]{2})Z$/', $metar_raw, $matches)) {
$timestamp['year'] = gmdate('Y');
$timestamp['month'] = gmdate('n');
$timestamp['day'] = $matches[1];
$timestamp['hour'] = $matches[2];
$timestamp['minute'] = $matches[3];
if (gmdate('d') < $timestamp['day']) {
$timestamp['month']--;
}
$metar['reported_on'] = gmmktime($timestamp['hour'], $timestamp['minute'], 0, $timestamp['month'], $timestamp['day'], $timestamp['year']);
}
}
function _weather_parse_reporttype($metar_raw, &$metar) {
if (preg_match('/^(AUTO|COR)$/', $metar_raw)) {
$metar['reporttype'] = $metar_raw;
}
}
function _weather_parse_wind($metar_raw, &$metar) {
if (preg_match('/^' . '([0-9]{3}|VRB)' . '([0-9]{2,3})' . '(G([0-9]{2,3}))?' . '(KT|MPS|KMH)' . '$/', $metar_raw, $matches)) {
$metar['wind']['direction'] = (int) $matches[1];
$wind_speed = (int) $matches[2];
$wind_gusts = (int) $matches[4];
$wind_unit = $matches[5];
switch ($wind_unit) {
case 'KT':
$metar['wind']['speed_knots'] = $wind_speed;
$metar['wind']['gusts_knots'] = $wind_gusts;
$metar['wind']['speed_kmh'] = round($wind_speed * 1.852, 1);
$metar['wind']['gusts_kmh'] = round($wind_gusts * 1.852, 1);
$metar['wind']['speed_mph'] = round($wind_speed * 1.151, 1);
$metar['wind']['gusts_mph'] = round($wind_gusts * 1.151, 1);
$metar['wind']['speed_mps'] = round($wind_speed * 0.514, 1);
$metar['wind']['gusts_mps'] = round($wind_gusts * 0.514, 1);
break;
case 'MPS':
$metar['wind']['speed_knots'] = round($wind_speed * 1.944, 1);
$metar['wind']['gusts_knots'] = round($wind_gusts * 1.944, 1);
$metar['wind']['speed_kmh'] = round($wind_speed * 3.6, 1);
$metar['wind']['gusts_kmh'] = round($wind_gusts * 3.6, 1);
$metar['wind']['speed_mph'] = round($wind_speed * 2.237, 1);
$metar['wind']['gusts_mph'] = round($wind_gusts * 2.237, 1);
$metar['wind']['speed_mps'] = $wind_speed;
$metar['wind']['gusts_mps'] = $wind_gusts;
break;
case 'KMH':
$metar['wind']['speed_knots'] = round($wind_speed * 0.54, 1);
$metar['wind']['gusts_knots'] = round($wind_gusts * 0.54, 1);
$metar['wind']['speed_kmh'] = $wind_speed;
$metar['wind']['gusts_kmh'] = $wind_gusts;
$metar['wind']['speed_mph'] = round($wind_speed * 0.621, 1);
$metar['wind']['gusts_mph'] = round($wind_gusts * 0.621, 1);
$metar['wind']['speed_mps'] = round($wind_speed * 0.278, 1);
$metar['wind']['gusts_mps'] = round($wind_gusts * 0.278, 1);
break;
}
$metar['wind']['speed_beaufort'] = _weather_calculate_beaufort($metar['wind']['speed_kmh']);
$metar['wind']['gusts_beaufort'] = _weather_calculate_beaufort($metar['wind']['gusts_kmh']);
}
else {
if (preg_match('/^' . '([0-9]{3})' . 'V' . '([0-9]{3})' . '$/', $metar_raw, $matches)) {
$metar['wind']['variable_start'] = (int) $matches[1];
$metar['wind']['variable_end'] = (int) $matches[2];
}
}
}
function _weather_parse_visibility($metar_raw, &$metar) {
if (preg_match('/^([0-9])$/', $metar_raw, $matches)) {
$metar['visibility']['#visibility_miles'] = $matches[1];
}
else {
if (preg_match('/^' . '(M?)([0-9])(\\/?)([0-9]*)' . 'SM' . '$/', $metar_raw, $matches)) {
if ($matches[3] == '/') {
$visibility = $metar['visibility']['#visibility_miles'] + $matches[2] / $matches[4];
}
else {
$visibility = $matches[2] . $matches[4];
}
$metar['visibility']['miles'] = $visibility;
$metar['visibility']['kilometers'] = round($visibility * 1.609344, 1);
}
else {
if (preg_match('/^([0-9]{4})$/', $metar_raw, $matches)) {
$metar['visibility']['kilometers'] = round($matches[1] / 1000, 1);
$metar['visibility']['miles'] = round($metar['visibility']['kilometers'] / 1.609344, 1);
}
}
}
}
function _weather_parse_phenomena($metar_raw, &$metar) {
if (preg_match('/^' . '(-|\\+|VC)?' . '(SH|TS|FZ)?' . 'RA' . '$/', $metar_raw, $matches)) {
$phen = array();
if (isset($matches[1])) {
switch ($matches[1]) {
case '-':
$phen['#light'] = TRUE;
break;
case '+':
$phen['#heavy'] = TRUE;
break;
default:
$phen['#moderate'] = TRUE;
}
}
if (isset($matches[2])) {
switch ($matches[2]) {
case 'SH':
$phen['#showers'] = TRUE;
break;
case 'FZ':
$phen['#freezing'] = TRUE;
break;
}
}
$metar['phenomena']['rain'] = $phen;
}
else {
if (preg_match('/^' . '(-|\\+|VC)?' . '(FZ)?' . 'DZ' . '$/', $metar_raw, $matches)) {
$phen = array();
if (isset($matches[1])) {
switch ($matches[1]) {
case '-':
$phen['#light'] = TRUE;
break;
case '+':
$phen['#heavy'] = TRUE;
break;
default:
$phen['#moderate'] = TRUE;
}
}
if (isset($matches[2])) {
switch ($matches[2]) {
case 'FZ':
$phen['#freezing'] = TRUE;
break;
}
}
$metar['phenomena']['drizzle'] = $phen;
}
else {
if (preg_match('/^' . '(-|\\+|VC)?' . '(BL|DR)?' . 'SN' . '$/', $metar_raw, $matches)) {
$phen = array();
if (isset($matches[1])) {
switch ($matches[1]) {
case '-':
$phen['#light'] = TRUE;
break;
case '+':
$phen['#heavy'] = TRUE;
break;
default:
$phen['#moderate'] = TRUE;
}
}
if (isset($matches[2])) {
switch ($matches[2]) {
case 'BL':
$phen['#blowing'] = TRUE;
break;
case 'DR':
$phen['#low_drifting'] = TRUE;
break;
}
}
$metar['phenomena']['snow'] = $phen;
}
}
}
}
function _weather_parse_condition($metar_raw, &$metar) {
$ordering = array(
1 => array(
'CLR' => 'clear',
),
2 => array(
'FEW' => 'few',
),
3 => array(
'SCT' => 'scattered',
),
4 => array(
'BKN' => 'broken',
),
5 => array(
'OVC' => 'overcast',
),
);
if (preg_match('/^' . '(FEW|SCT|BKN|OVC)([0-9]{3})' . '(CB|TCU)?' . '$/', $metar_raw, $matches)) {
foreach ($ordering as $order => $data) {
if (key($data) == $matches[1]) {
$metar['#condition_text'][] = $data[key($data)];
$metar['#condition_order'][] = $order;
break;
}
}
}
else {
if (preg_match('/^' . '(CLR|SKC|CAVOK)' . '$/', $metar_raw, $matches)) {
$metar['#condition_text'][] = 'clear';
$metar['#condition_order'][] = 1;
if ($matches[1] == 'CAVOK') {
$metar['visibility']['kilometers'] = 10;
$metar['visibility']['miles'] = round($metar['visibility']['kilometers'] / 1.609344, 1);
}
}
else {
if (preg_match('/^' . '(NSC|NCD)' . '$/', $metar_raw, $matches)) {
$metar['#condition_text'][] = 'no-significant-clouds';
$metar['#condition_order'][] = 1;
}
else {
if (preg_match('/^' . 'VV[0-9]{3}' . '$/', $metar_raw, $matches)) {
$metar['#condition_text'][] = 'overcast';
$metar['#condition_order'][] = 5;
}
}
}
}
if (isset($metar['#condition_order'])) {
foreach ($metar['#condition_order'] as $index => $order) {
if ($order > $metar['condition_order']) {
$metar['condition_order'] = $order;
$metar['condition_text'] = $metar['#condition_text'][$index];
}
}
}
}
function _weather_parse_temperature($metar_raw, &$metar) {
if (preg_match('/^' . '(M?[0-9]{2})' . '\\/' . '(M?[0-9]{2})?' . '$/', $metar_raw, $matches)) {
$metar['temperature']['celsius'] = (int) strtr($matches[1], 'M', '-');
$metar['temperature']['fahrenheit'] = round($metar['temperature']['celsius'] * 9 / 5 + 32, 1);
if (isset($matches[2])) {
$metar['dewpoint']['celsius'] = (int) strtr($matches[2], 'M', '-');
$metar['dewpoint']['fahrenheit'] = round($metar['dewpoint']['celsius'] * 9 / 5 + 32, 1);
}
}
}
function _weather_parse_pressure($metar_raw, &$metar) {
if (preg_match('/^' . '(A|Q)([0-9]{4})' . '$/', $metar_raw, $matches)) {
if ($matches[1] == 'A') {
$metar['pressure']['inHg'] = $matches[2] / 100;
$metar['pressure']['mmHg'] = round($metar['pressure']['inHg'] * 25.4, 0);
$metar['pressure']['hPa'] = round($metar['pressure']['inHg'] * 33.8639, 0);
}
else {
$metar['pressure']['hPa'] = (int) $matches[2];
$metar['pressure']['inHg'] = round($metar['pressure']['hPa'] * 0.02953, 2);
$metar['pressure']['mmHg'] = round($metar['pressure']['inHg'] * 25.4, 0);
}
}
}
function _weather_calculate_sunrise_sunset(&$metar) {
require_once drupal_get_path('module', 'weather') . '/icao_codes.inc';
$info = _weather_get_lat_long($metar['icao']);
$day_of_year = date('z', $metar['reported_on']);
$year = date('Y', $metar['reported_on']);
$month = date('m', $metar['reported_on']);
$day = date('d', $metar['reported_on']);
$pi_05 = 1.570796;
$pi_10 = 3.141593;
$pi_15 = 4.712389;
$pi_20 = 6.283185;
$latitude = 0.017453 * $info['latitude'];
$longitude = 0.017453 * $info['longitude'];
$timezone = 0;
$altitude = -0.014539;
$sunrise = 0;
$sunset = 0;
foreach (array(
'sunrise' => $pi_05,
'sunset' => $pi_15,
) as $type => $factor) {
$a = $day_of_year + ($factor - $longitude) / $pi_20;
$sma = $a * 0.017202 - 0.0574039;
$stl = $sma + 0.0334405 * sin($sma);
$stl += 4.93289 + 0.000349066 * sin(2 * $sma);
while ($stl < 0) {
$stl += $pi_20;
}
while ($stl >= $pi_20) {
$stl -= $pi_20;
}
if ($stl / $pi_05 - intval($stl / $pi_05) == 0) {
$stl += 4.84814E-6;
}
$sra = sin($stl) / cos($stl);
$sra = atan2(0.9174600000000001 * $sra, 1);
if ($stl > $pi_15) {
$sra += $pi_20;
}
else {
if ($stl > $pi_05) {
$sra += $pi_10;
}
}
$sd = 0.39782 * sin($stl);
$sd = $sd / sqrt(1 - $sd * $sd);
$sd = atan2($sd, 1);
$diurnal_arc = ($altitude - sin($sd) * sin($latitude)) / (cos($sd) * cos($latitude));
if ($diurnal_arc >= 1) {
$no_sunrise = TRUE;
break;
}
if ($diurnal_arc <= -1) {
$no_sunset = TRUE;
break;
}
$diurnal_arc = $diurnal_arc / sqrt(1 - $diurnal_arc * $diurnal_arc);
$diurnal_arc = $pi_05 - atan2($diurnal_arc, 1);
if ($type == 'sunrise') {
$diurnal_arc = $pi_20 - $diurnal_arc;
}
$localtime = $diurnal_arc + $sra - 0.0172028 * $a - 1.73364;
$wallclock = $localtime - $longitude + $timezone;
while ($wallclock < 0) {
$wallclock += $pi_20;
}
while ($wallclock >= $pi_20) {
$wallclock -= $pi_20;
}
$wallclock = $wallclock * 3.81972;
$hour = intval($wallclock);
$minute = round(($wallclock - $hour) * 60, 0);
if ($type == 'sunrise') {
$sunrise = gmmktime($hour, $minute, 0, $month, $day, $year);
}
else {
$sunset = gmmktime($hour, $minute, 0, $month, $day, $year);
}
}
if (isset($no_sunset)) {
$condition = 'day';
}
else {
if (isset($no_sunrise)) {
$condition = 'night';
}
else {
if ($sunrise <= $sunset) {
if ($metar['reported_on'] >= $sunrise and $metar['reported_on'] < $sunset) {
$condition = 'day';
}
else {
$condition = 'night';
}
}
else {
if ($metar['reported_on'] >= $sunrise or $metar['reported_on'] <= $sunset) {
$condition = 'day';
}
else {
$condition = 'night';
}
}
}
}
$metar['daytime']['sunrise_on'] = $sunrise;
$metar['daytime']['sunset_on'] = $sunset;
if (isset($no_sunrise)) {
$metar['daytime']['no_sunrise'] = TRUE;
}
else {
$metar['daytime']['no_sunrise'] = FALSE;
}
if (isset($no_sunset)) {
$metar['daytime']['no_sunset'] = TRUE;
}
else {
$metar['daytime']['no_sunset'] = FALSE;
}
$metar['daytime']['condition'] = $condition;
}
function _weather_calculate_beaufort($wind_speed) {
if ($wind_speed >= 120) {
return 12;
}
if ($wind_speed >= 103) {
return 11;
}
if ($wind_speed >= 88) {
return 10;
}
if ($wind_speed >= 76) {
return 9;
}
if ($wind_speed >= 63) {
return 8;
}
if ($wind_speed >= 51) {
return 7;
}
if ($wind_speed >= 40) {
return 6;
}
if ($wind_speed >= 30) {
return 5;
}
if ($wind_speed >= 20) {
return 4;
}
if ($wind_speed >= 12) {
return 3;
}
if ($wind_speed >= 7) {
return 2;
}
if ($wind_speed >= 1) {
return 1;
}
return 0;
}