public function __construct($properties, Horde_Service_Weather_Base $weather) { $this->_properties = $properties; $this->_weather = $weather; $this->_typeMap = array('HUR' => Horde_Service_Weather_Translation::t("Hurricane Local Statement"), 'TOR' => _("Tornado Warning"), 'TOW' => _("Tornado Watch"), 'WRN' => _("Severe Thunderstorm Warning"), 'SEW' => _("Severe Thunderstorm Watch"), 'WIN' => _("Winter Weather Advisory"), 'FLO' => _("Flood Warning"), 'WAT' => _("Flood Watch / Statement"), 'WND' => _("High Wind Advisory"), 'SVR' => _("Severe Weather Statement"), 'HEA' => _("Heat Advisory"), 'FOG' => _("Dense Fog Advisory"), 'SPE' => _("Special Weather Statement"), 'FIR' => _("Fire Weather Advisory"), 'VOL' => _("Volcanic Activity Statement"), 'HWW' => _("Hurricane Wind Warning"), 'REC' => _("Record Set"), 'REP' => _("Public Reports"), 'PUB' => _("Public Information Statement")); $this->_significanceMap = array('W' => _("Warning"), 'A' => _("Watch"), 'Y' => _("Advisory"), 'S' => _("Statement"), 'F' => _("Forecast"), 'O' => _("Outlook"), 'N' => _("Synopsis")); }
/** * 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'); } }
public function testForecast() { setlocale(LC_MESSAGES, 'C'); $weather = $this->_getStub('boston_wunderground.json'); $forecast = $weather->getForecast('boston,ma'); //$this->assertEquals('2011-11-27 22:15:00', (string)$forecast->getForecastTime()); $dayOne = $forecast->getForecastDay(0); $this->assertInstanceOf('Horde_Service_Weather_Period_Base', $dayOne); $this->assertEquals(Horde_Service_Weather_Translation::t("Partly Cloudy"), $dayOne->conditions); $this->assertEquals('South', $dayOne->wind_direction); $this->assertEquals('187', $dayOne->wind_degrees); $this->assertEquals(75, $dayOne->humidity); $this->assertEquals(0, $dayOne->precipitation_percent); $this->assertEquals(58, $dayOne->high); $this->assertEquals(50, $dayOne->low); $this->assertEquals(8, $dayOne->wind_speed); $this->assertEquals(11, $dayOne->wind_gust); $this->assertEquals(0.63, $dayOne->rain_total); $weather->units = Horde_Service_Weather::UNITS_METRIC; $this->assertEquals(14, $dayOne->high); $this->assertEquals(10, $dayOne->low); $this->assertEquals(13, $dayOne->wind_speed); $this->assertEquals(18, $dayOne->wind_gust); $this->assertEquals(0, $dayOne->snow_total); $this->assertEquals(16, $dayOne->rain_total); }
public function testForecast() { $weather = $this->_getStub(); $weather->units = Horde_Service_Weather::UNITS_STANDARD; $forecast = $weather->getForecast('boston,ma'); $this->assertEquals('2014-04-01 18:34:00', (string) $forecast->getForecastTime()); $dayOne = $forecast->getForecastDay(0); $this->assertInstanceOf('Horde_Service_Weather_Period_Base', $dayOne); $this->assertEquals(Horde_Service_Weather_Translation::t("Sunny"), $dayOne->conditions); $this->assertEquals(52, $dayOne->high); $this->assertEquals(42, $dayOne->low); $this->assertEquals(10, $dayOne->wind_speed); $weather->units = Horde_Service_Weather::UNITS_METRIC; $this->assertEquals(11, $dayOne->high); $this->assertEquals(5, $dayOne->low); $this->assertEquals('ESE', $dayOne->wind_direction); $this->assertEquals('106', $dayOne->wind_degrees); $this->assertEquals(16, $dayOne->wind_speed); $weather->units = Horde_Service_Weather::UNITS_STANDARD; $dayTwo = $forecast->getForecastDay(1); $this->assertInstanceOf('Horde_Service_Weather_Period_Base', $dayTwo); $this->assertEquals(Horde_Service_Weather_Translation::t("Sunny"), $dayTwo->conditions); $this->assertEquals(57, $dayTwo->high); $this->assertEquals(50, $dayTwo->low); $weather->units = Horde_Service_Weather::UNITS_METRIC; $this->assertEquals(14, $dayTwo->high); $this->assertEquals(10, $dayTwo->low); // Test unsupported $this->assertEquals(false, $dayOne->rain_total); $this->assertEquals(false, $dayOne->snow_total); // Test unknown throws exception $this->setExpectedException('Horde_Service_Weather_Exception_InvalidProperty'); $this->assertEquals(false, $dayOne->foobar); }
public function __get($property) { // Maybe someday I can add a better $_map array with 'type' fields etc.. // for now, just as easy to manually check for these exceptions. switch ($property) { case 'wind_gust': case 'dewpoint': case 'heat_index': case 'wind_chill': case 'pressure_trend': case 'logo_url': return null; case 'condition': case 'conditions': return Horde_Service_Weather_Translation::t($this->_properties->weatherDesc[0]->value); // Note that even though this is "localObsDateTime", the timezone set // in the date object will be the server's default timezone since it's // impossible to figure out the timezone from the information given by // the API. // Note that even though this is "localObsDateTime", the timezone set // in the date object will be the server's default timezone since it's // impossible to figure out the timezone from the information given by // the API. case 'time': return new Horde_Date($this->_properties->localObsDateTime); case 'time_utc': return new Horde_Date($this->_properties->date . ' ' . $this->_properties->observation_time, 'UTC'); case 'temp': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties->temp_F; } return $this->_properties->temp_C; case 'wind_speed': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties->windspeedMiles; } return $this->_properties->windspeedKmph; case 'visibility': if ($this->_weather->units == Horde_Service_Weather::UNITS_METRIC) { return $this->_properties->visibility; } else { return round($this->_properties->visibility * Horde_Service_Weather::CONVERSION_KPH_TO_MPH); } case 'pressure': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return round($this->_properties->pressure * Horde_Service_Weather::CONVERSION_MB_TO_INCHES, 2); } return $this->_properties->pressure; case 'icon': return $this->_weather->iconMap[str_replace('.png', '', basename($this->_properties->weatherIconUrl[0]->value))]; case 'humidity': return $this->_properties->humidity . '%'; default: if (empty($this->_map[$property])) { throw new Horde_Service_Weather_Exception_InvalidProperty(); } return Horde_Service_Weather_Translation::t($this->_properties->{$this->_map[$property]}); } }
/** * 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')); }
/** * 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'); } }
public function __get($property) { // Maybe someday I can add a better $_map array with 'type' fields etc.. // for now, just as easy to manually check for these exceptions. switch ($property) { case 'wind_gust': case 'dewpoint': case 'heat_index': case 'wind_chill': case 'pressure_trend': case 'logo_url': case 'visibility': return null; case 'condition': case 'conditions': return Horde_Service_Weather_Translation::t($this->_properties->weather[0]->main); case 'time': return new Horde_Date($this->_properties->dt, 'UTC'); case 'time_utc': return $this->time; case 'temp': return $this->_properties->main->temp; case 'wind_speed': return $this->_properties->wind->speed; case 'wind_degrees': return $this->_properties->wind->deg; case 'wind_dir': // @todo - Map degrees to direction. return ''; case 'visibility': if ($this->_weather->units == Horde_Service_Weather::UNITS_METRIC) { return $this->_properties->visibility; } else { return round($this->_properties->visibility * Horde_Service_Weather::CONVERSION_KPH_TO_MPH); } case 'pressure': return $this->_properties->main->pressure; case 'humidity': return $this->_properties->main->humidity . '%'; case 'icon': return !empty($this->_weather->iconMap[$this->_properties->weather[0]->icon]) ? $this->_weather->iconMap[$this->_properties->weather[0]->icon] : 'na.png'; default: if (empty($this->_map[$property])) { throw new Horde_Service_Weather_Exception_InvalidProperty(); } return Horde_Service_Weather_Translation::t($this->_properties->{$this->_map[$property]}); } }
/** * 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'); } }
/** * 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'); } }
public function testForecast() { $weather = $this->_getStub(); $weather->units = Horde_Service_Weather::UNITS_STANDARD; $forecast = $weather->getForecast('clayton,nj'); $this->assertEquals('2015-08-28 23:53:00', (string) $forecast->getForecastTime()); $dayOne = $forecast->getForecastDay(0); $this->assertInstanceOf('Horde_Service_Weather_Period_Wwov2', $dayOne); $this->assertEquals(Horde_Service_Weather_Translation::t("Sunny"), $dayOne->conditions); $this->assertEquals(89, $dayOne->high); $this->assertEquals(65, $dayOne->low); $this->assertEquals(7, $dayOne->wind_speed); $weather->units = Horde_Service_Weather::UNITS_METRIC; $this->assertEquals(32, $dayOne->high); $this->assertEquals(18, $dayOne->low); $this->assertEquals('NW', $dayOne->wind_direction); $this->assertEquals('304', $dayOne->wind_degrees); $this->assertEquals(11, $dayOne->wind_speed); // Test unknown throws exception $this->setExpectedException('Horde_Service_Weather_Exception_InvalidProperty'); $this->assertEquals(false, $dayOne->foobar); }
public function testCurrentConditions() { $weather = $this->_getWeatherDriver(); // Note the location here doesn't matter, since we already // stubbed the http_client $conditions = $weather->getCurrentConditions('KPHL'); // Condition $this->assertEquals('Wind from SW at 15mph Visibility AT 10 miles Sky scattered', $conditions->condition); // Humidity (calculated from temp/duepoint) $this->assertEquals('50%', $conditions->humidity); // Temp (F), Wind Speed (MPH), Visibility (Miles), Pressure (inches) $weather->units = Horde_Service_Weather::UNITS_STANDARD; $this->assertEquals(97, $conditions->temp); $this->assertEquals(15, $conditions->wind_speed); $this->assertEquals(10, $conditions->visibility); $this->assertEquals(29.87, $conditions->pressure); // Temp (C), Wind Speed (KPH), Visibility (K), Pressure (mb) $weather->units = Horde_Service_Weather::UNITS_METRIC; $conditions = $weather->getCurrentConditions('KPHL'); $this->assertEquals(36, $conditions->temp); $this->assertEquals(24, $conditions->wind_speed); $this->assertEquals(16, $conditions->visibility); $this->assertEquals(1011.51, $conditions->pressure); // Wind $this->assertEquals('SW', $conditions->wind_direction); $this->assertEquals(220, $conditions->wind_degrees); $this->assertEquals('2016-08-12 19:54:00', (string) $conditions->time); // METAR specific stuff. $this->assertEquals(Horde_Service_Weather_Translation::t('scattered'), $conditions->clouds[0]['amount']); $this->assertEquals(4300, $conditions->clouds[0]['height']); $this->assertEquals(Horde_Service_Weather_Translation::t('broken'), $conditions->clouds[1]['amount']); $this->assertEquals(25000, $conditions->clouds[1]['height']); $this->assertEquals(Horde_Service_Weather_Translation::t('Automatic weatherstation w/ precipitation discriminator'), $conditions->remark['autostation']); $this->assertEquals(1115, $conditions->remark['seapressure']); $this->assertEquals(35.6, $conditions->remark['1htemp']); $this->assertEquals(23.9, $conditions->remark['1hdew']); }
public function __get($property) { switch ($property) { // These are unsupported case 'logo_url': case 'heat_index': case 'icon': case 'icon_url': return null; case 'time': return new Horde_Date($this->_properties['update'], 'GMT'); case 'pressure_trend': return !empty($this->_properties['remark']['presschg']) ? $this->_properties['remark']['presschg'] : null; case 'condition': case 'conditions': // Not really translatable from METAR data...but try to generate // some sensible human readable data. $units = $this->_weather->getUnits(); $conds = ''; 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']); // @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); 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(); } }
/** * 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')); }
/** * 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; }
/** * 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; }
/** * Accessor * * @param string $property Property to get * * @return mixed The property value */ public function __get($property) { // Maybe someday I can add a better $_map array with 'type' fields etc.. // for now, just as easy to manually check for these exceptions. switch ($property) { case 'logo_url': return $this->_properties['image']->url; case 'time': return new Horde_Date($this->_properties['observation_time_rfc822'], $this->_properties['local_tz_long']); case 'time_utc': $date = new Horde_Date($this->_properties['observation_time_rfc822'], $this->_properties['local_tz_long']); $date->setTimezone('UTC'); return $date; case 'temp': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return round($this->_properties['temp_f']); } return round($this->_properties['temp_c']); case 'wind_speed': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['wind_mph']; } return $this->_properties['wind_kph']; case 'wind_gust': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['wind_gust_mph']; } return $this->_properties['wind_gust_kph']; case 'dewpoint': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['dewpoint_f']; } return $this->_properties['dewpoint_c']; case 'heat_index': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['heat_index_f']; } return $this->_properties['heat_index_c']; case 'wind_chill': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['wind_chill_f']; } return $this->_properties['wind_chill_c']; case 'visibility': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return round($this->_properties['visibility_mi']); } return round($this->_properties['visibility_km']); case 'pressure': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['pressure_in']; } return $this->_properties['pressure_mb']; case 'pressure_trend': switch ($this->_properties['pressure_trend']) { case '0': return Horde_Service_Weather_Translation::t('steady'); case '+': return Horde_Service_Weather_Translation::t('rising'); case '-': return Horde_Service_Weather_Translation::t('falling'); } break; case 'icon': return $this->_weather->iconMap[$this->_properties['icon']]; default: if (empty($this->_map[$property])) { throw new Horde_Service_Weather_Exception_InvalidProperty(); } if (strpos($this->_properties[$this->_map[$property]], '-999') !== false) { return Horde_Service_Weather_Translation::t('N/A'); } return Horde_Service_Weather_Translation::t($this->_properties[$this->_map[$property]]); } }
/** * 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'); } }