Commit 7cd4012f authored by Harald Judt's avatar Harald Judt

New value: Apparent temperature (bug #9564).

Apparent temperature, also known as "felt temperature" or "feels like" value
is a debatable and rather subjective value, as can be deduced from the
various models available that try to compete for correctness and accuracy.

The different models that are offered by the plugin are collected from
various sources and/or derived from publicly available specifications,
the sources are mentioned in the code. Many commercial weather data
providers use their own proprietary (sometimes patented) models.

* Windchill/Heat index:
  Used in North America, wind chill will be reported for low temperatures
  and heat index for higher ones. At night, heat index will be replaced by
  the Summer Simmer Index.

* Windchill/Humidex:
  The Canadian counterpart, with the wind chill being similar to the
  previous model but with slightly different constraints. Instead of
  the heat index Humidex will be used.

* Steadman:
  This is the model used in Australia, especially adapted for the climate
  of this continent. Maybe in Central Europe too, but then windchill and
  similar values had never gained so much popularity as in the US or Canada,
  so information about its usage is scarce. It can be used for lower and
  higher temperatures, so no extra heat index is required.

* Quayle-Steadman:
  Improvements made to earlier experiments/developments by Steadman in
  1998.

These models are not better or worse than others; Usually it's advised to
use the model for your country as the formulas have been adapted to account
for the local climate and resemble what you get presented in the news.
The main reason for the existence of these models is being able to warn
about the risk of frostbite and heat strokes. Otherwise, apparent
temperature is a rather subjective value and both reliability and
significance are not very high.
parent fefdd9aa
......@@ -32,7 +32,7 @@
#include "weather-scrollbox.h"
#define UPDATE_TIMER_DELAY 7
#define OPTIONS_N 14
#define OPTIONS_N 15
#define BORDER 4
#define LOC_NAME_MAX_LEN 50
......@@ -100,6 +100,7 @@ static const labeloption labeloptions[OPTIONS_N] = {
{N_("Wind direction in degrees (WD)"), WIND_DIRECTION_DEG},
{N_("Humidity (H)"), HUMIDITY},
{N_("Dew point (D)"), DEWPOINT},
{N_("Apparent temperature (A)"), APPARENT_TEMPERATURE},
{N_("Low clouds (CL)"), CLOUDS_LOW},
{N_("Medium clouds (CM)"), CLOUDS_MED},
{N_("High clouds (CH)"), CLOUDS_HIGH},
......@@ -311,6 +312,8 @@ setup_units(xfceweather_dialog *dialog,
SET_COMBO_VALUE(dialog->combo_unit_windspeed, units->windspeed);
SET_COMBO_VALUE(dialog->combo_unit_precipitations, units->precipitations);
SET_COMBO_VALUE(dialog->combo_unit_altitude, units->altitude);
SET_COMBO_VALUE(dialog->combo_apparent_temperature,
units->apparent_temperature);
}
......@@ -623,6 +626,18 @@ combo_unit_temperature_changed(GtkWidget *combo,
}
static void
combo_apparent_temperature_changed(GtkWidget *combo,
gpointer user_data)
{
xfceweather_dialog *dialog = (xfceweather_dialog *) user_data;
dialog->pd->units->apparent_temperature =
gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
update_scrollbox(dialog->pd, TRUE);
update_summary_window(dialog, TRUE);
}
static void
combo_unit_pressure_changed(GtkWidget *combo,
gpointer user_data)
......@@ -675,7 +690,7 @@ combo_unit_altitude_changed(GtkWidget *combo,
static GtkWidget *
create_units_page(xfceweather_dialog *dialog)
{
GtkWidget *palign, *page, *hbox, *vbox, *label;
GtkWidget *palign, *page, *hbox, *vbox, *label, *sep;
GtkSizeGroup *sg_label;
ADD_PAGE(FALSE);
......@@ -740,6 +755,22 @@ create_units_page(xfceweather_dialog *dialog)
_("Feet (ft)"));
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, BORDER);
/* separator */
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, BORDER * 2);
/* apparent temperature model */
hbox = gtk_hbox_new(FALSE, BORDER);
ADD_LABEL(_("Apparent te_mperature:"), sg_label);
ADD_COMBO(dialog->combo_apparent_temperature);
ADD_COMBO_VALUE(dialog->combo_apparent_temperature,
_("Windchill/Heat index"));
ADD_COMBO_VALUE(dialog->combo_apparent_temperature,
_("Windchill/Humidex"));
ADD_COMBO_VALUE(dialog->combo_apparent_temperature, _("Steadman"));
ADD_COMBO_VALUE(dialog->combo_apparent_temperature, _("Quayle-Steadman"));
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, BORDER);
/* initialize widgets with current data */
if (dialog->pd)
setup_units(dialog, dialog->pd->units);
......@@ -1408,6 +1439,8 @@ setup_notebook_signals(xfceweather_dialog *dialog)
G_CALLBACK(combo_unit_precipitations_changed), dialog);
g_signal_connect(dialog->combo_unit_altitude, "changed",
G_CALLBACK(combo_unit_altitude_changed), dialog);
g_signal_connect(dialog->combo_apparent_temperature, "changed",
G_CALLBACK(combo_apparent_temperature_changed), dialog);
/* appearance page */
g_signal_connect(dialog->combo_icon_theme, "changed",
......
......@@ -47,6 +47,7 @@ typedef struct {
GtkWidget *combo_unit_windspeed;
GtkWidget *combo_unit_precipitations;
GtkWidget *combo_unit_altitude;
GtkWidget *combo_apparent_temperature;
/* appearance page */
GtkWidget *combo_icon_theme;
......
......@@ -165,6 +165,121 @@ calc_dewpoint(const xml_location *loc)
}
/* Calculate felt air temperature, using the chosen model. */
static gdouble
calc_apparent_temperature(const xml_location *loc,
const apparent_temp_models model,
const gboolean night_time)
{
gdouble temp = string_to_double(loc->temperature_value, 0);
gdouble windspeed = string_to_double(loc->wind_speed_mps, 0);
gdouble humidity = string_to_double(loc->humidity_value, 0);
gdouble dp, e;
switch (model) {
case WINDCHILL_HEATINDEX:
/* If temperature is lower than 10 °C, use wind chill index,
if above 26.7°C use the heat index / Summer Simmer Index. */
/* wind chill is only defined for wind speeds above 3.0 mph */
windspeed *= 3.6;
if (windspeed < 4.828032)
return temp;
/* Wind chill, source:
http://www.nws.noaa.gov/os/windchill/index.shtml */
if (temp <= 10.0)
return 13.12 + 0.6215 * temp - 11.37 * pow(windspeed, 0.16)
+ 0.3965 * temp * pow(windspeed, 0.16);
if (temp >= 26.7 || (night_time && temp >= 22.0)) {
/* humidity needs to be higher than 40% for a valid result */
if (humidity < 40)
return temp;
temp = temp * 9.0 / 5.0 + 32.0; /* both models use Fahrenheit */
if (!night_time)
/* Heat index, source:
Lans P. Rothfusz. "The Heat Index 'Equation' (or, More
Than You Ever Wanted to Know About Heat Index)",
Scientific Services Division (NWS Southern Region
Headquarters), 1 July 1990.
http://www.srh.noaa.gov/images/ffc/pdf/ta_htindx.PDF
*/
return ((-42.379
+ 2.04901523 * temp
+ 10.14333127 * humidity
- 0.22475541 * temp * humidity
- 0.00683783 * temp * temp
- 0.05481717 * humidity * humidity
+ 0.00122874 * temp * temp * humidity
+ 0.00085282 * temp * humidity * humidity
- 0.00000199 * temp * temp * humidity * humidity)
- 32.0) * 5.0 / 9.0; /* convert back to Celsius */
else
/* Summer Simmer Index, sources:
http://www.summersimmer.com/home.htm
http://www.gorhamschaffler.com/humidity_formulas.htm */
return ((1.98 * (temp - (0.55 - 0.0055 * humidity)
* (temp - 58)) - 56.83)
- 32.0) * 5.0 / 9.0; /* convert back to Celsius */
}
/* otherwise simply return the temperature */
return temp;
case WINDCHILL_HUMIDEX:
/* If temperature is equal or lower than 0 °C, use wind chill index,
if above 20.0 °C use humidex. Source:
http://www.weatheroffice.gc.ca/mainmenu/faq_e.html */
/* wind chill is only defined for wind speeds above 2.0 km/h */
windspeed *= 3.6;
if (windspeed < 2.0)
return temp;
if (temp <= 0)
/* wind chill, source:
http://www.nws.noaa.gov/os/windchill/index.shtml */
return 13.12 + 0.6215 * temp - 11.37 * pow(windspeed, 0.16)
+ 0.3965 * temp * pow(windspeed, 0.16);
if (temp >= 20.0) {
/* Canadian humidex, source:
http://www.weatheroffice.gc.ca/mainmenu/faq_e.html#weather6 */
dp = calc_dewpoint(loc);
/* dew point needs to be above a certain limit for
valid results, see
http://www.weatheroffice.gc.ca/mainmenu/faq_e.html#weather5 */
if (dp < 0)
return temp;
/* dew point needs to be converted to Kelvin (easy job ;-) */
e = 6.11 * exp(5417.7530 * (1/273.16 - 1/(dp + 273.15)));
return temp + 0.5555 * (e - 10.0);
}
return temp;
case STEADMAN:
/* Australians use a different formula. Source:
http://www.bom.gov.au/info/thermal_stress/#atapproximation */
e = humidity / 100 * 6.105 * exp(17.27 * temp / (237.7 + temp));
return temp + 0.33 * e - 0.7 * windspeed - 4.0;
case QUAYLE_STEADMAN:
/* R. G. Quayle, R. G. Steadman: The Steadman wind chill: an
improvement over present scales. In: Weather and
Forecasting. 13, 1998, S. 1187–1193 */
windspeed *= 3.6;
if (windspeed < 4.828032)
return temp;
return 1.41 - 1.162 * windspeed + 0.980 * temp
+ 0.0124 * windspeed * windspeed + 0.0185 * windspeed * temp;
}
}
gchar *
get_data(const xml_time *timeslice,
const units_config *units,
......@@ -256,6 +371,13 @@ get_data(const xml_time *timeslice,
val = val * 9.0 / 5.0 + 32.0;
return g_strdup_printf(ROUND_TO_INT("%.1f"), val);
case APPARENT_TEMPERATURE:
val = calc_apparent_temperature(loc, units->apparent_temperature,
night_time);
if (units->temperature == FAHRENHEIT)
val = val * 9.0 / 5.0 + 32.0;
return g_strdup_printf(ROUND_TO_INT("%.1f"), val);
case CLOUDS_LOW:
return LOCALE_DOUBLE(loc->clouds_percent[CLOUDS_PERC_LOW],
ROUND_TO_INT("%.1f"));
......@@ -301,6 +423,7 @@ get_unit(const units_config *units,
return (units->altitude == FEET) ? _("ft") : _("m");
case TEMPERATURE:
case DEWPOINT:
case APPARENT_TEMPERATURE:
return (units->temperature == FAHRENHEIT) ? _("°F") : _("°C");
case PRESSURE:
switch (units->pressure) {
......
......@@ -33,6 +33,7 @@ typedef enum {
WIND_DIRECTION_DEG,
HUMIDITY,
DEWPOINT,
APPARENT_TEMPERATURE,
CLOUDS_LOW,
CLOUDS_MED,
CLOUDS_HIGH,
......@@ -72,6 +73,13 @@ typedef enum {
FEET
} units_altitude;
typedef enum {
WINDCHILL_HEATINDEX,
WINDCHILL_HUMIDEX,
STEADMAN,
QUAYLE_STEADMAN
} apparent_temp_models;
typedef enum {
MORNING,
AFTERNOON,
......@@ -81,6 +89,7 @@ typedef enum {
typedef struct {
gint temperature;
gint apparent_temperature;
gint pressure;
gint windspeed;
gint precipitations;
......
......@@ -353,6 +353,13 @@ get_preferred_units(const gchar *country_code)
if (!strcmp(country_code, "RU")) /* Russian Federation */
units->pressure = TORR;
if (!strcmp(country_code, "US")) /* United States */
units->apparent_temperature = WINDCHILL_HEATINDEX;
else if (!strcmp(country_code, "CA")) /* Canada */
units->apparent_temperature = WINDCHILL_HUMIDEX;
else if (!strcmp(country_code, "AU")) /* Australia */
units->apparent_temperature = STEADMAN;
return units;
}
......
......@@ -421,6 +421,7 @@ create_summary_tab(plugin_data *data)
APPEND_BTEXT(_("\nTemperatures\n"));
APPEND_TEXT_ITEM(_("Temperature"), TEMPERATURE);
APPEND_TEXT_ITEM(_("Dew point"), DEWPOINT);
APPEND_TEXT_ITEM(_("Apparent temperature"), APPARENT_TEMPERATURE);
/* wind */
APPEND_BTEXT(_("\nWind\n"));
......@@ -548,7 +549,9 @@ forecast_cell_get_tooltip_text(plugin_data *data,
g_free(value);
g_string_append(text, _("<b>Temperatures</b>\n"));
APPEND_TOOLTIP_ITEM(_("Dew point:\t\t\t%s%s%s\n\n"), DEWPOINT);
APPEND_TOOLTIP_ITEM(_("Dew point:\t\t\t%s%s%s\n"), DEWPOINT);
APPEND_TOOLTIP_ITEM(_("Apparent temperature:\t%s%s%s\n\n"),
APPARENT_TEMPERATURE);
g_string_append(text, _("<b>Atmosphere</b>\n"));
APPEND_TOOLTIP_ITEM(_("Pressure:\t%s%s%s\n"), PRESSURE);
......
......@@ -137,6 +137,9 @@ make_label(const plugin_data *data,
case DEWPOINT:
lbl = _("D");
break;
case APPARENT_TEMPERATURE:
lbl = _("A");
break;
case CLOUDS_LOW:
lbl = _("CL");
break;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment