weather.module in Weather 5
Same filename and directory in other branches
Display <acronym title="METeorological Aerodrome Report">METAR</acronym> weather data from anywhere in the world
The module is compatible with Drupal 5.
@author Tobias Toedter
File
weather.moduleView source
<?php
/*
* Copyright (C) 2006 Tobias Toedter <t.toedter@gmx.net>
*
* This file is part of the Drupal Weather module.
* It is based on the Weather module which was written in 2004 by
* Gerard Ryan <gerardryan@canada.com>.
*
* Weather is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Weather is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Weather; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Display <acronym title="METeorological Aerodrome Report">METAR</acronym>
* weather data from anywhere in the world
*
* The module is compatible with Drupal 5.
*
* @author Tobias Toedter
*/
// Include the parser for METAR data
require_once drupal_get_path('module', 'weather') . '/weather_parser.inc';
/*********************************************************************
* General Drupal hooks for registering the module
********************************************************************/
/**
* Implementation of hook_perm().
*/
function weather_perm() {
return array(
'administer custom weather block',
);
}
/**
* Implementation of hook_menu().
*/
function weather_menu($may_cache) {
global $user;
$items = array();
if ($may_cache) {
$items[] = array(
'path' => 'admin/settings/weather',
'title' => t('Weather'),
'description' => t('Configure the system-wide weather block.'),
'callback' => 'weather_custom_block',
'callback arguments' => array(
0,
),
'access' => user_access('access administration pages'),
'type' => MENU_NORMAL_ITEM,
);
$items[] = array(
'path' => 'user/' . $user->uid . '/weather',
'title' => t('My weather'),
'description' => t('Configure your custom weather block.'),
'callback' => 'weather_custom_block',
'callback arguments' => array(
$user->uid,
),
'access' => user_access('administer custom weather block'),
'type' => MENU_LOCAL_TASK,
);
}
return $items;
}
/*********************************************************************
* Drupal hooks for the block content
********************************************************************/
/**
* Generate HTML for the weather block
* @param op operation from the URL
* @param delta offset
* @returns block HTML
*/
function weather_block($op = 'list', $delta = 0) {
global $user;
if ($op == 'list') {
$block[0]['info'] = t('Weather: system-wide');
$block[1]['info'] = t('Weather: custom user');
return $block;
}
else {
if ($op == 'view') {
if ($delta == 0 and user_access('access content')) {
// show the system weather block
$block['subject'] = t('Current weather');
$block['content'] = '';
$configs_in_use = _weather_get_configs_in_use(0);
if (count($configs_in_use) == 0) {
$configs_in_use = array(
'cid' => 1,
);
}
foreach ($configs_in_use as $index) {
$config = _weather_get_config(0, $index['cid']);
$metar = weather_get_metar($config['icao']);
$block['content'] .= theme('weather', $config, $metar);
}
return $block;
}
else {
if ($delta == 1 and user_access('administer custom weather block')) {
// show the user's custom weather block
$block['subject'] = t('Current weather');
$block['content'] = '';
$configs_in_use = _weather_get_configs_in_use($user->uid);
if (count($configs_in_use) == 0) {
$configs_in_use = array(
'cid' => 1,
);
}
foreach ($configs_in_use as $index) {
$config = _weather_get_config($user->uid, $index['cid']);
$metar = weather_get_metar($config['icao']);
$block['content'] .= theme('weather', $config, $metar);
}
return $block;
}
}
}
}
}
/**
* Custom theme function for the weather block output
*/
function theme_weather($config, $metar) {
$content .= '<p><strong>' . check_plain($config['real_name']) . '</strong></p>';
$image = _weather_get_image($metar);
$content .= '<div style="text-align:center;"><img src="';
$content .= $image['filename'];
$content .= '" alt="' . $image['alt_text'] . '" width="64" height="64" /></div>';
$content .= '<ul><li>';
$content .= _weather_format_condition($metar);
$content .= '</li><li>';
$content .= t('Temperature: !temperature', array(
'!temperature' => _weather_format_temperature($metar['temperature'], $config['units']),
));
$content .= '</li><li>';
$content .= t('Wind: !wind', array(
'!wind' => _weather_format_wind($metar['wind'], $config['units']),
));
$content .= '</li><li>';
$content .= t('Pressure: !pressure', array(
'!pressure' => _weather_format_pressure($metar['pressure'], $config['units']),
));
$content .= '</li><li>';
$content .= t('Rel. Humidity: !rel_humidity', array(
'!rel_humidity' => _weather_format_relative_humidity($metar['temperature'], $metar['dewpoint']),
));
$content .= '</li><li>';
$content .= t('Visibility: !visibility', array(
'!visibility' => _weather_format_visibility($metar['visibility'], $config['units']),
));
$content .= '</li></ul>';
$content .= '<small>';
$content .= t("Reported on:");
$content .= '<br />' . format_date($metar['reported_on']);
$content .= '</small>';
return $content;
}
function _weather_get_image($metar) {
// is there any data available?
if (!isset($metar['condition_text'])) {
$name = 'nodata';
}
else {
// handle special case: NSC, we just use few for the display
if ($metar['condition_text'] == 'no-significant-clouds') {
$metar['condition_text'] = 'few';
}
// calculate the sunrise and sunset times for day/night images
$day_night = _weather_sunrise_sunset($metar);
$name = $day_night . '-' . $metar['condition_text'];
// handle rain images
if (isset($metar['phenomena']['rain'])) {
$rain = $metar['phenomena']['rain'];
}
else {
if (isset($metar['phenomena']['drizzle'])) {
$rain = $metar['phenomena']['drizzle'];
}
}
if (isset($rain)) {
if ($rain['#light']) {
$name .= '-light-rain';
}
else {
if ($rain['#heavy']) {
$name .= '-heavy-rain';
}
else {
$name .= '-moderate-rain';
}
}
}
}
// set up final return array
$image['filename'] .= base_path() . drupal_get_path('module', 'weather') . '/images/' . $name . '.png';
$image['alt_text'] = $name;
return $image;
}
/**
* Internal helper function to calculate the sunrise and sunset times
* for a given location
*/
function _weather_sunrise_sunset($metar) {
// Include the ICAO codes
require_once drupal_get_path('module', 'weather') . '/icao_codes.inc';
$info = _weather_get_lat_long($metar['icao']);
// reported_on is already GMT, so don't use gmdate()
$day_of_year = date('z', $metar['reported_on']);
$year = date('Y', $metar['reported_on']);
$month = date('m', $metar['reported_on']);
$day = date('d', $metar['reported_on']);
// setup pi constants: 0.5*pi, 1.0*pi, 1.5*pi, 2.0*pi
$pi_05 = 1.570796;
$pi_10 = 3.141593;
$pi_15 = 4.712389;
$pi_20 = 6.283185;
// convert latitude and longitude degree into radian
// x rad = y° * pi / 180 = 0.017453 * y°
$latitude = 0.017453 * $info['lat'];
$longitude = 0.017453 * $info['long'];
// we want always GMT time, so set to 0. Otherwise,
// the timezone can be calculated as follows:
// $timezone = 0.261799 * offset;
$timezone = 0;
// the sunrise/sunset altitude in radian (-0.833°)
$altitude = -0.014539;
$sunrise = 0;
$sunset = 0;
foreach (array(
'sunrise' => $pi_05,
'sunset' => $pi_15,
) as $type => $factor) {
$a = $day_of_year + ($factor - $longitude) / $pi_20;
// solar mean anomaly
$sma = $a * 0.017202 - 0.0574039;
// solar true longitude
$stl = $sma + 0.0334405 * sin($sma);
$stl += 4.93289 + 0.000349066 * sin(2 * $sma);
// normalize the longitude to be between >= 0 and < 2.0*pi
while ($stl < 0) {
$stl += $pi_20;
}
while ($stl >= $pi_20) {
$stl -= $pi_20;
}
if ($stl / $pi_05 - intval($stl / $pi_05) == 0) {
$stl += 4.84814E-6;
}
// solar right ascension
$sra = sin($stl) / cos($stl);
$sra = atan2(0.9174600000000001 * $sra, 1);
// adjust quadrant
if ($stl > $pi_15) {
$sra += $pi_20;
}
else {
if ($stl > $pi_05) {
$sra += $pi_10;
}
}
// solar declination
$sd = 0.39782 * sin($stl);
$sd = $sd / sqrt(1 - $sd * $sd);
$sd = atan2($sd, 1);
$diurnal_arc = ($altitude - sin($sd) * sin($latitude)) / (cos($sd) * cos($latitude));
// is there a sunrise or sunset at all?
if ($diurnal_arc >= 1) {
// no sunrise
$no_sunrise = TRUE;
break;
}
if ($diurnal_arc <= -1) {
// no sunset
$no_sunset = TRUE;
break;
}
$diurnal_arc = $diurnal_arc / sqrt(1 - $diurnal_arc * $diurnal_arc);
$diurnal_arc = $pi_05 - atan2($diurnal_arc, 1);
if ($type == 'sunrise') {
$diurnal_arc = $pi_20 - $diurnal_arc;
}
// calculate the time
$localtime = $diurnal_arc + $sra - 0.0172028 * $a - 1.73364;
// wall clock time
$wallclock = $localtime - $longitude + $timezone;
// normalize wallclock to be between >= 0 and < 2.0*pi
while ($wallclock < 0) {
$wallclock += $pi_20;
}
while ($wallclock >= $pi_20) {
$wallclock -= $pi_20;
}
$wallclock = $wallclock * 3.81972;
$hour = intval($wallclock);
$minute = round(($wallclock - $hour) * 60, 0);
if ($type == 'sunrise') {
$sunrise = gmmktime($hour, $minute, 0, $month, $day, $year);
}
else {
$sunset = gmmktime($hour, $minute, 0, $month, $day, $year);
}
}
// handle special cases like no sunrise / sunset at all
if ($no_sunset) {
$condition = 'day';
}
else {
if ($no_sunrise) {
$condition = 'night';
}
else {
// correctly handle northern and southern hemisphere
if ($sunrise <= $sunset) {
// this should be on the northern hemisphere
if ($metar['reported_on'] >= $sunrise and $metar['reported_on'] < $sunset) {
$condition = 'day';
}
else {
$condition = 'night';
}
}
else {
// this should be on the southern hemisphere
if ($metar['reported_on'] >= $sunrise or $metar['reported_on'] <= $sunset) {
$condition = 'day';
}
else {
$condition = 'night';
}
}
}
}
return $condition;
}
/*********************************************************************
* Internal functions for custom weather blocks
********************************************************************/
/**
* Show a configuration page for a custom weather block
*/
function weather_custom_block($uid, $cid = 0) {
// Include the ICAO codes
require_once drupal_get_path('module', 'weather') . '/icao_codes.inc';
// Include a nice Javascript which makes the settings easier
drupal_add_js(drupal_get_path('module', 'weather') . '/helper.js');
$places_url .= base_path() . drupal_get_path('module', 'weather');
// if there's no configuration id provided, we get the first in use or 1
if ($cid == 0) {
$cid = _weather_get_first_valid_config($uid);
}
// if the provided config id is not currently used, we want the lowest
// free configuration id. This avoid cases like 56271 for config ids
$config_in_use = _weather_get_configs_in_use($uid);
$cid_found = FALSE;
foreach ($config_in_use as $index) {
if ($index['cid'] == $cid) {
$cid_found = TRUE;
break;
}
}
if (!$cid_found) {
$cid = _weather_get_free_config($uid);
}
// get the previously determined configuration
$config = _weather_get_config($uid, $cid);
$config['country'] = _weather_get_country_from_icao($config['icao']);
$config['countries'] = _weather_get_countries();
$config['places'] = _weather_get_places($config['country']);
$output = drupal_get_form('weather_custom_block_form', $uid, $cid, $config, $places_url);
// Add another location
if ($uid == 0) {
$url = 'admin/settings/weather/' . _weather_get_free_config($uid);
}
else {
$url = 'user/' . $uid . '/weather/' . _weather_get_free_config($uid);
}
$output .= '<p>';
$output .= l(t('Add another location to the block'), $url);
$output .= '</p>';
if (count($config_in_use)) {
$output .= '<p>';
$output .= t('Current locations in the block:');
$output .= '</p>';
$output .= '<ul>';
foreach ($config_in_use as $index) {
// do not add a link to the current configuration
$output .= '<li>';
if ($index['cid'] == $cid) {
$output .= check_plain($index['real_name']);
}
else {
if ($uid == 0) {
$url = 'admin/settings/weather/' . $index['cid'];
}
else {
$url = 'user/' . $uid . '/weather/' . $index['cid'];
}
$output .= l($index['real_name'], $url);
}
$output .= '</li>';
}
$output .= '</ul>';
}
return $output;
}
/**
* Construct the configuration form for a weather block
*/
function weather_custom_block_form($uid, $cid, $config, $places_url) {
// set up a selection box with all countries
$form['country'] = array(
'#type' => 'select',
'#title' => t('Country'),
'#description' => t('Select a country to narrow down your search.'),
'#default_value' => $config['country'],
'#options' => drupal_map_assoc($config['countries']),
'#attributes' => array(
'onchange' => "update_place('{$places_url}')",
),
);
// set up a selection box with all place names of the selected country
$form['icao'] = array(
'#type' => 'select',
'#title' => t('Place'),
'#description' => t('Select a place in that country for the weather display.'),
'#default_value' => $config['icao'],
'#options' => $config['places'],
'#attributes' => array(
'onchange' => 'update_icao()',
),
// we need to skip the check of valid options, because they get
// modified with AJAX after a new country has been selected.
'#DANGEROUS_SKIP_CHECK' => true,
);
$form['real_name'] = array(
'#type' => 'textfield',
'#title' => t('Real name for the selected place'),
'#default_value' => $config['real_name'],
'#description' => t('You may enter another name for the place selected above.'),
'#required' => true,
'#size' => '30',
);
$form['units'] = array(
'#type' => 'select',
'#title' => t('Display units'),
'#default_value' => $config['units'],
'#description' => t('Show values of temperature, speed etc. in metric or imperial units.'),
'#options' => array(
'metric' => t('metric'),
'imperial' => t('imperial'),
),
);
$form['weight'] = array(
'#type' => 'weight',
'#title' => t('Weight'),
'#default_value' => $config['weight'],
'#description' => t('Optional. In the block, the heavier locations will sink and the lighter locations will be positioned nearer the top. Locations with equal weights are sorted alphabetically.'),
);
$form['uid'] = array(
'#type' => 'value',
'#value' => $uid,
);
$form['cid'] = array(
'#type' => 'value',
'#value' => $cid,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save configuration'),
);
$form['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete configuration'),
);
return $form;
}
/**
* Check the submission of the custom weather block
*/
function weather_custom_block_form_validate($form_id, $form_values) {
// do not validate for deletion action
if ($form_values['op'] == t('Delete configuration')) {
return;
}
if (_weather_get_country_from_icao($form_values['icao']) == '') {
form_set_error('icao', t('The ICAO code is not valid.'));
}
}
/**
* Handle the submission of the custom weather block
*/
function weather_custom_block_form_submit($form_id, $form_values) {
// delete the previous entry
$sql = "DELETE FROM {weather_config} WHERE uid=%d AND cid=%d";
db_query($sql, $form_values['uid'], $form_values['cid']);
if ($form_values['op'] == t('Save configuration')) {
// insert the new configuration values into the DB
$sql = "INSERT INTO {weather_config}\n (uid, cid, icao, real_name, units, weight)\n VALUES(%d, %d, '%s', '%s', '%s', %d)";
db_query($sql, $form_values['uid'], $form_values['cid'], $form_values['icao'], $form_values['real_name'], $form_values['units'], $form_values['weight']);
drupal_set_message(t('The configuration options have been saved.'));
}
else {
drupal_set_message(t('The configuration has been deleted.'));
$form_values['cid'] = _weather_get_first_valid_config($form_values['uid']);
}
if ($form_values['uid'] == 0) {
// go back to the administration of the system weather block
drupal_goto('admin/settings/weather/' . $form_values['cid']);
}
else {
drupal_goto('user/' . $form_values['uid'] . '/weather/' . $form_values['cid']);
}
}
/**
* Return the configuration for the given user id.
*
* If there is no configuration yet, get the default
* configuration instead. The user is 0 is used for
* the system weather block.
*/
function _weather_get_config($uid, $cid) {
$sql = "SELECT * FROM {weather_config} WHERE uid=%d AND cid=%d";
$result = db_query($sql, $uid, $cid);
if (db_num_rows($result) != 1) {
// there was no configuration found. Provide it here.
$config['icao'] = 'EDDH';
$config['real_name'] = 'Hamburg-Fuhlsbuettel';
$config['units'] = 'metric';
}
else {
// get the user's configuration
$config = db_fetch_array($result);
}
return $config;
}
/**
* Return the first valid configuration or 1 if there is none
*/
function _weather_get_first_valid_config($uid) {
$sql = 'SELECT cid FROM {weather_config} WHERE uid=%d ORDER BY weight ASC, real_name ASC';
return max(db_result(db_query($sql, $uid)), 1);
}
/**
* Return the first unused configuration number
*/
function _weather_get_free_config($uid) {
$result = db_query("SELECT * FROM {weather_config}\n WHERE uid=%d\n ORDER BY cid ASC", $uid);
$free_config = 1;
while ($config = db_fetch_array($result)) {
if ($config['cid'] > $free_config) {
break;
}
$free_config++;
}
return $free_config;
}
/**
* Determine how many configurations exist for the given user id.
*/
function _weather_get_configs_in_use($uid) {
$configs = array();
$result = db_query("SELECT * FROM {weather_config}\n WHERE uid=%d\n ORDER BY weight ASC, real_name ASC", $uid);
while ($row = db_fetch_array($result)) {
$configs[] = array(
'cid' => $row['cid'],
'real_name' => $row['real_name'],
);
}
return $configs;
}
/*********************************************************************
* Internal functions for converting data
********************************************************************/
/**
* Format the weather condition and phenomena (rain, drizzle, ...)
*/
function _weather_format_condition($metar) {
if (!isset($metar) or !isset($metar['condition_text'])) {
return t('No data');
}
// sky conditions
switch ($metar['condition_text']) {
case 'clear':
$result[] = t('Clear sky');
break;
case 'few':
$result[] = t('Few clouds');
break;
case 'scattered':
$result[] = t('Scattered clouds');
break;
case 'broken':
$result[] = t('Broken clouds');
break;
case 'overcast':
$result[] = t('Overcast');
break;
case 'no-significant-clouds':
$result[] = t('No significant clouds');
break;
}
// weather phenomena
$rain = $metar['phenomena']['rain'];
if ($rain) {
if ($rain['#light']) {
if ($rain['#showers']) {
$result[] = t('light rain showers');
}
else {
if ($rain['#freezing']) {
$result[] = t('light freezing rain');
}
else {
$result[] = t('light rain');
}
}
}
else {
if ($rain['#heavy']) {
if ($rain['#showers']) {
$result[] = t('heavy rain showers');
}
else {
if ($rain['#freezing']) {
$result[] = t('heavy freezing rain');
}
else {
$result[] = t('heavy rain');
}
}
}
else {
if ($rain['#showers']) {
$result[] = t('rain showers');
}
else {
if ($rain['#freezing']) {
$result[] = t('freezing rain');
}
else {
$result[] = t('rain');
}
}
}
}
}
return join(", ", $result);
}
/**
* Convert temperatures
*
* @param int Temperature, always in degree celsius
* @param string The unit to be returned (metric, imperial)
* @return string Formatted representation, either in celsius or fahrenheit
*/
function _weather_format_temperature($temperature, $unit) {
if (!isset($temperature)) {
return t('No data');
}
if ($unit == 'imperial') {
return t('!temperature °F', array(
'!temperature' => $temperature['fahrenheit'],
));
}
else {
// default to metric units
return t('!temperature °C', array(
'!temperature' => $temperature['celsius'],
));
}
}
/**
* Convert wind
*
* @param int Wind
* @param string The unit to be returned (metric, imperial)
* @return string Formatted representation
*/
function _weather_format_wind($wind, $unit) {
if (!isset($wind)) {
return t('No data');
}
// shortcut for a special case
if ($wind['direction'] == 0 and $wind['speed_kmh'] == 0) {
return t('Calm');
}
// translate the wind direction
$directions = array(
t('North') => array(
'start' => -23,
'end' => 22,
),
t('Northeast') => array(
'start' => 22,
'end' => 67,
),
t('East') => array(
'start' => 67,
'end' => 112,
),
t('Southeast') => array(
'start' => 112,
'end' => 157,
),
t('South') => array(
'start' => 157,
'end' => 202,
),
t('Southwest') => array(
'start' => 202,
'end' => 247,
),
t('West') => array(
'start' => 247,
'end' => 292,
),
t('Northwest') => array(
'start' => 292,
'end' => 337,
),
);
// perform the wrap for North
if ($wind['direction'] >= 337) {
$wind['direction'] -= 360;
}
if ($wind['variable_start'] >= 337) {
$wind['variable_start'] -= 360;
}
if ($wind['variable_end'] >= 337) {
$wind['variable_end'] -= 360;
}
foreach ($directions as $description => $range) {
if ($wind['direction'] >= $range['start'] and $wind['direction'] < $range['end']) {
$wind['direction_text'] = $description;
}
if ($wind['variable_start'] >= $range['start'] and $wind['variable_start'] < $range['end']) {
$wind['variable_start_text'] = $description;
}
if ($wind['variable_end'] >= $range['start'] and $wind['variable_end'] < $range['end']) {
$wind['variable_end_text'] = $description;
}
}
// handle variable wind directions
if ($wind['direction'] == 'VRB' or isset($wind['variable_start'])) {
if (isset($wind['variable_start'])) {
$result[] = t('Variable from !direction_a to !direction_b', array(
'!direction_a' => $wind['variable_start_text'],
'!direction_b' => $wind['variable_end_text'],
));
}
else {
$result[] = t('Variable');
}
}
else {
// no variable wind direction, but an exact one
$result[] = $wind['direction_text'];
}
// set up the wind speed
if ($unit == 'imperial') {
$result[] = t('!speed mph', array(
'!speed' => $wind['speed_mph'],
));
}
else {
// default to metric units
$result[] = t('!speed km/h', array(
'!speed' => $wind['speed_kmh'],
));
}
// set up gusts, if applicable
if ($wind['gusts_kmh'] > 0) {
if ($unit == 'imperial') {
$result[] = t('gusts up to !speed mph', array(
'!speed' => $wind['gusts_mph'],
));
}
else {
// default to metric units
$result[] = t('gusts up to !speed km/h', array(
'!speed' => $wind['gusts_kmh'],
));
}
}
return join(', ', $result);
}
/**
* Convert pressure
*
* The function uses celsius as input
*/
function _weather_format_pressure($pressure, $unit) {
if (!isset($pressure)) {
return t('No data');
}
if ($unit == 'imperial') {
return t('!pressure inHg', array(
'!pressure' => $pressure['inHg'],
));
}
else {
// default to metric units
return t('!pressure hPa', array(
'!pressure' => $pressure['hPa'],
));
}
}
/**
* Calculate the relative humidity
*
* The function uses celsius as input
*/
function _weather_format_relative_humidity($temperature, $dewpoint) {
if (!isset($dewpoint)) {
return t('No data');
}
$e = 6.11 * pow(10, 7.5 * $dewpoint['celsius'] / (237.7 + $dewpoint['celsius']));
$es = 6.11 * pow(10, 7.5 * $temperature['celsius'] / (237.7 + $temperature['celsius']));
return max(0, min(100, round(100 * ($e / $es), 0))) . "%";
}
/**
* Convert the visibility
*/
function _weather_format_visibility($visibility, $unit) {
if (!isset($visibility['kilometers'])) {
return t('No data');
}
if ($unit == 'imperial') {
return t('!visibility miles', array(
'!visibility' => $visibility['miles'],
));
}
else {
// default to metric units
return t('!visibility kilometers', array(
'!visibility' => $visibility['kilometers'],
));
}
}
/*********************************************************************
* Internal function for retrieving data
********************************************************************/
/**
* Fetches the latest METAR data from the database or internet
*/
function weather_get_metar($icao) {
// see if there's a report in the database
$icao = strtoupper($icao);
$sql = "SELECT * FROM {weather} WHERE icao='%s'";
$result = db_query($sql, $icao);
$data = db_fetch_array($result);
// if there is no report, initialize the array
if (!isset($data['metar_raw']) or !isset($data['next_update_on'])) {
$data['next_update_on'] = 0;
$data['metar_raw'] = '';
}
// if the time has come, download again
if ($data['next_update_on'] <= time()) {
$data['metar_raw'] = '';
}
// fetch data from the internet
if ($data['metar_raw'] == '') {
$data['metar_raw'] = _weather_retrieve_data($icao);
if ($data['metar_raw']) {
$metar = weather_parse_metar($data['metar_raw']);
weather_store_metar($metar);
}
else {
// the internet retrieval has not been successful.
// try again in 10 minutes
$sql = "UPDATE {weather} SET next_update_on=%d WHERE icao='%s'";
db_query($sql, time() + 10 * 60, $icao);
}
}
else {
$metar = weather_parse_metar($data['metar_raw']);
}
return $metar;
}
/**
* Stores parsed METAR data in the database
*/
function weather_store_metar($metar) {
// if there's already a record in the database with the same ICAO
// overwrite it
$sql = "DELETE FROM {weather} WHERE icao='%s'";
db_query($sql, $metar['icao']);
// insert the new data
$sql = "INSERT INTO {weather}\n (icao, next_update_on, metar_raw)\n VALUES ('%s', %d, '%s')";
// calculate the next scheduled update: normally, we use 62
// minutes after the reported timestamp, to allow the data
// to propagate to the server servers.
$next_update_on = $metar['reported_on'] + 62 * 60;
// However, if the current time is more than 62 minutes
// over the reported timestamp, allow ten more minutes
// to not fetch the data on each page request.
if ($next_update_on < time()) {
$next_update_on = time() + 10 * 60;
}
db_query($sql, $metar['icao'], $next_update_on, $metar['#raw']);
}
/**
* Retrieve data from weather.noaa.gov
*/
function _weather_retrieve_data($icao) {
$icao = strtoupper($icao);
$metar_raw = FALSE;
// get information about the last successful data download
//
// we want to fetch via FTP, because it's much less bandwidth
// for downloads. if the FTP access did not work, we use
// HTTP POST instead.
$fetch = variable_get('weather_fetch', 'FTP');
// try alternative methods for fetching the METAR data via FTP
if ($fetch == 'FTP') {
$url = 'ftp://tgftp.nws.noaa.gov/data/observations/metar/stations/';
$url .= $icao . '.TXT';
if (function_exists('file_get_contents') and ini_get('allow_url_fopen')) {
$metar_raw = file_get_contents($url);
}
else {
if (function_exists('curl_init')) {
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_TRANSFERTEXT, TRUE);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
$metar_raw = curl_exec($curl);
curl_close($curl);
}
else {
if (function_exists('exec')) {
exec("wget --quiet -O- {$url}", $output, $return_val);
if ($return_val == 0) {
$metar_raw = join("\n", $output);
}
}
}
}
// if we had success, store the method
if ($metar_raw !== FALSE) {
variable_set('weather_fetch', 'FTP');
}
}
// if the FTP access does not work, try POSTing the ICAO via HTTP
if ($metar_raw === FALSE) {
if (function_exists('fsockopen')) {
$handle = fsockopen('weather.noaa.gov', 80);
if ($handle !== FALSE) {
$request = "POST /cgi-bin/mgetmetar.pl HTTP/1.1\r\n";
$request .= "Host: weather.noaa.gov\r\n";
$request .= "Content-Type: application/x-www-form-urlencoded\r\n";
$request .= "Content-Length: 9\r\n\r\n";
$request .= "cccc={$icao}\r\n";
fwrite($handle, $request);
while (!feof($handle)) {
$response .= fgets($handle, 1024);
}
fclose($handle);
// extract the valid METAR data from the received webpage
if (preg_match("/({$icao} [0-9]{6}Z .+)/", $response, $matches)) {
$metar_raw = $matches[1];
// store the method
variable_set('weather_fetch', 'HTTP');
}
}
}
}
// check on errors
if ($metar_raw === FALSE) {
drupal_set_message(t('Download location for METAR data is not accessible.'), 'error');
}
return $metar_raw;
}
Functions
Name | Description |
---|---|
theme_weather | Custom theme function for the weather block output |
weather_block | Generate HTML for the weather block |
weather_custom_block | Show a configuration page for a custom weather block |
weather_custom_block_form | Construct the configuration form for a weather block |
weather_custom_block_form_submit | Handle the submission of the custom weather block |
weather_custom_block_form_validate | Check the submission of the custom weather block |
weather_get_metar | Fetches the latest METAR data from the database or internet |
weather_menu | Implementation of hook_menu(). |
weather_perm | Implementation of hook_perm(). |
weather_store_metar | Stores parsed METAR data in the database |
_weather_format_condition | Format the weather condition and phenomena (rain, drizzle, ...) |
_weather_format_pressure | Convert pressure |
_weather_format_relative_humidity | Calculate the relative humidity |
_weather_format_temperature | Convert temperatures |
_weather_format_visibility | Convert the visibility |
_weather_format_wind | Convert wind |
_weather_get_config | Return the configuration for the given user id. |
_weather_get_configs_in_use | Determine how many configurations exist for the given user id. |
_weather_get_first_valid_config | Return the first valid configuration or 1 if there is none |
_weather_get_free_config | Return the first unused configuration number |
_weather_get_image | |
_weather_retrieve_data | Retrieve data from weather.noaa.gov |
_weather_sunrise_sunset | Internal helper function to calculate the sunrise and sunset times for a given location |