Author: Michael J Rubinsky (mrubinsk@horde.org)
Exemplo n.º 1
0
 /**
  * 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');
     }
 }
Exemplo n.º 2
0
 /**
  */
 protected function _content()
 {
     global $injector, $prefs;
     try {
         $this->_weather->autocompleteLocation('test');
     } catch (Horde_Db_Exception $e) {
         return _("No metar station data found. Contact administrator to\n                run the horde-service-weather-metar-database script.");
     }
     // Set up the weather driver.
     $this->_weather->units = $this->_params['units'];
     $units = $this->_weather->getUnits();
     // Set up the view object.
     $view = $injector->getInstance('Horde_View');
     $view->units = $units;
     $view->timezone = $prefs->getValue('timezone');
     $view->date_format = $prefs->getValue('date_format');
     $view->time_format = $prefs->getValue('time_format');
     if (!empty($this->_refreshParams) && !empty($this->_refreshParams->location)) {
         $location = $view->requested_location = $this->_refreshParams->location;
         $view->instance = '';
     } else {
         $view->instance = hash('md5', mt_rand());
         $injector->getInstance('Horde_Core_Factory_Imple')->create('WeatherLocationAutoCompleter_Metar', array('id' => 'location' . $view->instance, 'instance' => $view->instance));
         $view->requested_location = $this->_params['location'];
         $location = $this->_params['location'];
     }
     // Get the data.
     try {
         $weather = $this->_weather->getCurrentConditions($location)->getRawData();
     } catch (Horde_Service_Weather_Exception $e) {
         $view->error = true;
     }
     $view->weather = $weather;
     // Station information.
     $station = $this->_weather->getStation();
     $view->location_title = sprintf('%s, %s (%s)', $station->name, $station->country_name, $station->code);
     // Wind.
     if (isset($weather['wind'])) {
         if ($weather['windDirection'] == 'Variable') {
             if (!empty($this->_params['knots'])) {
                 $view->wind = sprintf(_("%s at %s %s"), $weather['windDirection'], round(Horde_Service_Weather::convertSpeed($weather['wind'], $units['wind'], 'kt')), 'kt');
             } else {
                 $view->wind = sprintf(_("%s at %s %s"), $weather['windDirection'], round($weather['wind']), $units['wind']);
             }
         } elseif ($weather['windDegrees'] == '000' && $weather['wind'] == '0') {
             $view->wind = _("Calm");
         } else {
             $view->wind = sprintf(_("from the %s (%s) at %s %s"), $weather['windDirection'], $weather['windDegrees'], empty($this->_params['knots']) ? round($weather['wind']) : round(Horde_Service_Weather::convertSpeed($weather['wind'], $units['wind'], 'kt')), empty($this->_params['knots']) ? $units['wind'] : 'kt');
         }
     }
     // Gusts
     if (isset($weather['windGust'])) {
         if ($weather['windGust']) {
             if (!empty($this->_params['knots'])) {
                 $view->wind .= sprintf(_(", gusting %s %s"), round(Horde_Service_Weather::convertSpeed($weather['windGust'], $units['wind'], 'kt')), 'kt');
             } else {
                 $view->wind .= sprintf(_(", gusting %s %s"), round($weather['windGust']), $units['wind']);
             }
         }
     }
     // Variability
     if (isset($weather['windVariability'])) {
         if ($weather['windVariability']['from']) {
             $view->wind .= sprintf(_(", variable from %s to %s"), $weather['windVariability']['from'], $weather['windVariability']['to']);
         }
     }
     // Clouds.
     $view->clouds = isset($weather['clouds']) ? $weather['clouds'] : array();
     // Remarks.
     if (isset($weather['remark'])) {
         $view->remarks = '';
         $view->other = '';
         foreach ($weather['remark'] as $remark => $value) {
             switch ($remark) {
                 case 'seapressure':
                     $view->remarks .= '<br />' . _("Pressure at sea level: ") . $value . ' ' . $units['pres'];
                     break;
                 case 'precipitation':
                     foreach ($value as $precip) {
                         if (is_numeric($precip['amount'])) {
                             $view->remarks .= '<br />' . sprintf(ngettext("Precipitation for last %d hour: ", "Precipitation for last %d hours: ", $precip['hours']), $precip['hours']) . $precip['amount'] . ' ' . $units['rain'];
                         } else {
                             $view->remarks .= '<br />' . sprintf(ngettext("Precipitation for last %d hour: ", "Precipitation for last %d hours: ", $precip['hours']), $precip['hours']) . $precip['amount'];
                         }
                     }
                     break;
                 case 'snowdepth':
                     $view->remarks .= '<br />' . _("Snow depth: ") . $value . ' ' . $units['rain'];
                     break;
                 case 'snowequiv':
                     $view->remarks .= '<br />' . _("Snow equivalent in water: ") . $value . ' ' . $units['rain'];
                     break;
                 case 'sunduration':
                     $view->remarks .= '<br />' . sprintf(_("%d minutes"), $value);
                     break;
                 case '1htemp':
                     $view->remarks .= '<br />' . _("Temp for last hour: ") . round($value) . '&deg;' . Horde_String::upper($units['temp']);
                     break;
                 case '1hdew':
                     $view->remarks .= '<br />' . _("Dew Point for last hour: ") . round($value) . '&deg;' . Horde_String::upper($units['temp']);
                     break;
                 case '6hmaxtemp':
                     $view->remarks .= '<br />' . _("Max temp last 6 hours: ") . round($value) . '&deg;' . Horde_String::upper($units['temp']);
                     break;
                 case '6hmintemp':
                     $view->remarks .= '<br />' . _("Min temp last 6 hours: ") . round($value) . '&deg;' . Horde_String::upper($units['temp']);
                     break;
                 case '24hmaxtemp':
                     $view->remarks .= '<br />' . _("Max temp last 24 hours: ") . round($value) . '&deg;' . Horde_String::upper($units['temp']);
                     break;
                 case '24hmintemp':
                     $view->remarks .= '<br />' . _("Min temp last 24 hours: ") . round($value) . '&deg;' . Horde_String::upper($units['temp']);
                     break;
                 case 'sensors':
                     foreach ($value as $sensor) {
                         $view->remarks .= '<br />' . _("Sensor: ") . $sensor;
                     }
                     break;
                 case '3hpresstend':
                     $view->remarks .= '<br />' . sprintf(_("Pressure tendency last 3 hours: %s (%s %s)"), $value['description'], $value['presschng'], $units['pres']);
                     break;
                 default:
                     $view->other .= '<br />' . $value;
                     break;
             }
         }
     }
     // TAF
     if (!empty($this->_params['taf'])) {
         $taf = $this->_weather->getForecast($location)->getRawData();
         $view->item = 0;
         $view->periods = array();
         $view->taf = $taf;
         unset($view->taf['time']);
         foreach ($taf['time'] as $time => $entry) {
             $time_obj = new Horde_Date($time, 'UTC');
             $period = array('time' => $time_obj);
             // Wind
             if (isset($entry['wind'])) {
                 if ($entry['windDirection'] == 'Variable') {
                     if (!empty($this->_params['knots'])) {
                         $period['wind'] = sprintf(_("%s at %s %s"), strtolower($entry['windDirection']), round(Horde_Service_Weather::convertSpeed($entry['wind'], $units['wind'], 'kt')), 'kt');
                     } else {
                         $period['wind'] = sprintf(_("%s at %s %s"), $entry['windDirection'], round($entry['wind']), $units['wind']);
                     }
                 } elseif ($entry['windDegrees'] == '000' && $entry['wind'] == '0') {
                     $period['wind'] = _("Calm");
                 } else {
                     $period['wind'] = sprintf(_("from the %s (%s) at %s %s"), $entry['windDirection'], $entry['windDegrees'], empty($this->_params['knots']) ? round($entry['wind']) : round(Horde_Service_Weather::convertSpeed($entry['wind'], $units['wind'], 'kt')), empty($this->_params['knots']) ? $units['wind'] : 'kt');
                 }
             }
             // Temp
             if (isset($entry['temperatureLow']) || isset($entry['temperatureHigh'])) {
                 if (isset($entry['temperatureLow'])) {
                     $period['temperatureLow'] = $entry['temperatureLow'];
                 }
                 if (isset($entry['temperatureHigh'])) {
                     $period['temperatureHigh'] = $entry['temperatureHigh'];
                 }
             }
             // Wind Shear
             if (isset($entry['windshear'])) {
                 $period['shear'] = sprintf(_("from the %s (%s) at %s %s"), $entry['windshearDirection'], $entry['windshearDegrees'], $entry['windshearHeight'], $units['height']);
             }
             // Visibility
             if (isset($entry['visibility'])) {
                 $period['visibility'] = strtolower($entry['visQualifier']) . ' ' . $entry['visibility'] . ' ' . $units['vis'];
             }
             // Conditions
             if (isset($entry['condition'])) {
                 $period['condition'] = $entry['condition'];
             }
             // Clouds
             $period['clouds'] = $entry['clouds'];
             // FMC
             if (isset($entry['fmc'])) {
                 $period['fmc'] = $entry['fmc'];
                 $period['fmc']['clouds'] = !empty($period['fmc']['clouds']) ? $period['fmc']['clouds'] : array();
             }
             // Set the period in the view.
             $view->periods[] = $period;
         }
     }
     if (!empty($view->instance)) {
         return $view->render('block/metar');
     } else {
         return $view->render('block/metar_content');
     }
 }
Exemplo n.º 3
0
 /**
  * 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;
 }
Exemplo n.º 4
0
Arquivo: Taf.php Projeto: horde/horde
 /**
  * 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
                     // 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;
 }