Feels Like
Introduction
Feels Like is a concept often quoted in weather forecasts, as the name suggests it attempts to quantify what human skin detects as the temperature, taking into account any warming or cooling effect of exposure to different humidity levels, high wind, or shade/sunshine. The best way to quantify that has been discussed in many scientific papers following on from experimentation in various laboratories, and so different formulae have been used at different times as more is learnt about how the body reacts to different conditions.
All formulae involve the air temperature and at least one of the following:
- (relative) humidity
- wind (speed)
- sunshine (radiation)
Cumulus MX from version 3.6.0 fully supports the latest "Feels Like" definition as agreed by the Joint Action Group for temperature Indices (JAG/TI). If you understand PHP Hypertext Preprocessor language (other languages use similar operators) then here is how to calculate apparent temperature and feels like. The JAG/TI feels like scale follows wind chill below 10 degrees Celsius, apparent temperature above 20 degrees C, and blends the proportion of each of those at in-between temperatures. Note the function makes assumptions about units used for temperature (Celsius) and wind speed (kilometres per hour).
//---------------------------------------------------------------- // CALCULATE APPARENT TEMPERATURE AND FEELS LIKE TEMPRATURE FROM AIR TEMPERATURE, WIND SPEED, AND HUMIDITY function Calculate_FeelsLike ($temp_degC, $wind_kph, $humidity) { if($wind_kph <4.828) { $wind_chill = $temp_degC; }else{ $wind_chill = 13.12 +0.6125 * $temp_degC - 11.37 * pow($wind_kph, 0.16) + 0.3965 * $temp_degC * pow($wind_kph, 0.16); } // $v = $rh / 100 * 6.105 * pow (0.16, 17.27 * $tempC / (237.7 + $tempC) ); $vapour_pressure = (($humidity / 100) * 6.105) * 6.105 * exp(17.27 * $temp_degC / (237.7 + $temp_degC)) / 10; $apparent_temp = -2.7 + (1.04 * $temp_degC) + (2 * $vapour_pressure) - ($wind_kph * 01.1805553); if($temp_degC < 10) { $feels_like = $wind_chill; }elseif($temp_degC > 20) { $feels_like = $apparent_temp; }else{ $app_temp_mult = ($temp_degC - 10) / 10; $wind_chill_mult = 1 - $app_temp_mult; $feels_like = ($apparent_temp * $app_temp_mult) + ($wind_chill * $wind_chill_mult); } $return_array[0] = $apparent_temp; $return_array[1] = $feels_like; return $return_array; }
The rest of this article was written for versions of Cumulus that did not report JAG/TI feels like, and uses JavaScript for deciding whether to display Heat Index, Apparent Temperature, or Wind Chill, based on the current weather conditions. It was designed to work with standard Cumulus web templates that produce HTML and do not use PHP.
Version 3.6.0 and all earlier versions of MX, and versions of Cumulus 1 since those shown, support earlier definitions for feels like:
- From Version 1.9.1 (6 April 2011) Apparent_temperature, this index was introduced by Steadman in 1971 who later revised his formula and many implementations use his later 1998 formula. Cumulus adopted the Australian Bureau of Meteorology implementation of Steadman formula.
- Although Heat_index was available in earlier versions, it was improved from version 1.9.0 (11 Sept 2010) in Cumulus 1 with a new formula covering a greater range of humidities accurately.
- Although Humidex was available in earlier versions, it was first correctly implemented from version 1.8.9 (31 Mar 2010).
- Although Wind_chill was available in earlier versions, it was only from version 1.8.8 (1 Dec 2009) that Cumulus included it on graphs after introducing in same version (earlier build) a check that wind speed and temperature readings were available.
Davis stations also provide THW index and THWS index , other ways of expressing Feels Like.
Web implementation
Ever thought that you want to display a 'Feels Like' temperature?
- Would you like to display Wind chill when it is cold and windy, Heat index when it is hot and humid, and Apparent temperature at other times?
- Do you understand how to call JavaScript code from HTML?
- Can you modify a Cascading Stylesheets (CSS) file to include new classes to define the appearance of various items?
If you can answer yes to all these questions, the following JavaScript code may be useful to you. Depending on the element id that it finds on your HTML page, it either inserts an appropriate 'Feels Like temperature' as a figure, or it draws up a temperature bar graph in the standard Cumulus style, but shows the current value for a parameter selected depending on the current conditions (although it also draws any maximum and/or minimum values of that parameter that are available).
If you can understand the standard Cumulus gaugesT.htm template, then you can use that as a starting point. You might want to modify that template page, or you might want to modify (or create) another template page.
You need to include something like the script that appears at the bottom of the standard gaugesT.htm in respect of defining temperature, humidity and wind speed:
HTML code to translate web tags to JavaScript variables (as modified for additional parameters)
This is needed on any web page if you want to use the new script.
<script>/* -------------------------------- Convert Cumulus tags into Javascript variables for selecting 'Feels Like' parameter------------------------------------- */ var tempscale= "<#tempunitnodeg>"; // This is temperature unit, it contains F or C (without the HTML code for degree because JavaScript cannot understand it) var tempos = new Array("<#temp>","<#tempTL>","<#tempTH>","<#temptrendtext>","<#temptrendenglish>");/* OUTSIDE TEMPERATURE (current, today low, today high, trend description to display on mouseover, trend value to determine image to show) */ var ExtraSensor1Name = ""; /* Extra temperature/humidity sensor one name prefix */ var tempex1 = new Array("","","","");/* EXTRA TEMPERATURE ONE parameters (required even if not used) */ var ExtraSensor2Name = ""; /* Extra temperature/humidity sensor two name prefix */ var tempex2 = new Array("","","","");/* EXTRA TEMPERATURE TWO parameters (required even if not used) */ var TempColor = ""; // If set defines the bar chart column colour for standard Cumulus charts like dewpoint var tempdp = new Array("<#dew>","<#dewpointTL>","<#dewpointTH>","","");/* DEW POINT (current, today low, today high) */ var tempapp = new Array("<#apptemp>","<#apptempTL>","<#apptempTH>","","");/* APPARENT TEMPERATURE (current, today low, today high)*/ var temphi = new Array("<#heatindex>","unavailable","<#heatindexTH>","","");/* HEAT INDEX (current, - , today high) */ var tempwc = new Array("<#wchill>","<#wchillTL>","unavailable","","");/* WIND CHILL (current, today low) */ var tempis = new Array("not used","","","");/* INDOOR TEMPERATURE (not used by feels like routine) */ var humos = new Array("<#hum>","<#humTL>","<#ThumTL>","<#humTH>", "<#ThumTH>");/* OUTSIDE HUMIDITY latest, minimum, time of minimum, maximum, time of maximum*/ // Wind histogram data (Modified from Cumulus original - needs more work as degree symbol giving warning messages on validation for HTML5) var winddata = new Array("<#wgust>","<#bearing>","<#wspeed>","<#avgbearing>","<#wgustTM>", "<#windunit>", "<#bearingTM>° at <#TwgustTM>");/* WIND SPEEDS (latest gust, its bearing, average wind speed, average bearing, maximum gust today, unit for wind speeds, bearing of maximum gust today with symbol for degrees, time of maximum gust) */ </script>
HTML amendment to call new script
Supposing you stored the new script below in a file called "temperature.js" in the same directory as the calling html, then the HTML needs a call to the new script (insert just before the end of the HTML body) like this:
<script src="temperature.js"></script> </body>
Defining HTML insert position
The HTML needs an element id for determining where the script inserts the Feels Like data.
Option 1 - Modifying a table to display a figure
The element id shown below would result in the numerical value being inserted as per screen image on left. Moving your mouse over the figure would display the information about the parameter being shown. This could be suitable for your 'now' page (the standard Cumulus indexT.htm displays wind chill, heat index and apparent temperature, but maybe you want to alter that? (Note this element id is not relevant to the gauges page, unless you want to display numerical values without a graphic).
HTML table rows to replace
<tr class="td_temperature_data"> <td>Windchill</td> <td><#wchill> <#tempunit></td> <td>Humidity</td> <td><#hum>%</td> </tr> <tr class="td_temperature_data"> <td>Heat Index</td> <td><#heatindex> <#tempunit></td> <td>Apparent Temperature</td> <td><#apptemp> <#tempunit></td> </tr>
HTML code required for inserting figure
<tr class="td_temperature_data"><td colspan="4" id="variable_figure" title="'Feels Like' - need to have JavaScript enabled for automatic selection of parameter to show here"> <small>Feels Like (title varies)</small> <br><!-- insert position for variable numerical value --></td></tr>
[category:Terminology]
Option 2 - Modifying the Gauges page to display a different bar chart
Perhaps you use the standard Cumulus 1 gaugesT.htm template, but want to change it so it displays an appropriate Feels Like graphic (see screen shot)?
Then you need to note the differences between the temperature and wind histogram data transfer shown above and that appearing at the foot of the standard Cumulus page. If you are able to cope with the necessary coding changes, you could replace the following code with the code that follows it (but there may be sizing issues to sort out).
HTML code for Inside Temperature can be replaced
<td width="33%" align="center" bgcolor="#EFEFEF"><div id="insidetemp">small</div></td>
HTML code required for varying chart
<td class="plots"><div id="feels_like"></div></td>
The class plots will need adding to the CSS, it could contain the width and align clauses that you removed or any other formatting you require.
New JavaScript Code required
This script introduces a number of new classes, to move styling that in the standard Cumulus dashboard.js is defined inside the script, out to an external CSS. For brevity, the list of new classes is not listed here, it is assumed you know enough to work out the list yourself, and you will probably want to use your own styling anyway.
/* ----------------------Apparent Temperature, Wind Chill or Heat Index-------------------*/ // --- variables required --- var gauges_images=”dbimages”; // directory where temperature scale image is stored var divid = new Array("outsidetemp","apptemp","dewpoint","heatindex","windchill","windchill/heatindex");var ddata = new Array(tempos, tempapp, tempdp, temphi, tempwc);var dColour = new Array("#c60","#f90","sienna","purple","cyan");var esn1 = "Extra Temperature 1";var esn2 = "Extra Temperature 2";if(ExtraSensor1Name) esn1 = ExtraSensor1Name + " Temperature";if(ExtraSensor2Name) esn2 = ExtraSensor2Name + " Temperature";var divti = new Array("Outside Temperature","Apparent Temperature",esn1,esn2,"Dew Point","Heat Index","Wind Chill"); var dtext =new Array("This routine automatically decides which parameter best represents the 'Feels Like' temperature by checking the maximum wind gust speed and the minimum and maximum temperature for this meteorological day.: These are used to pick between 'Wind Chill' (low minimum temperature, high wind gust), 'Heat Index' (high maximum temperature, ) and 'Apparent Temperature' (all other cases).", "Apparent Temperature (AT) is displayed when minimum temperature is not low enough (or wind not strong enough) to show 'Wind Chill' and maximum temperature (and/or humidity) not high enough to show 'Heat Index'. The AT is defined (in Australia) as; the temperature, at the reference humidity level, producing the same amount of discomfort as that experienced under the current ambient temperature and humidity: The (Australian) Apparent Temperature (AT) is calculated from a formula combining measurements of air temperature (Ta), wind speed (ws) and relative humidity (rh). AT = Ta + 0.33×rh / 100 × 6.105 × exp ( 17.27 × Ta / ( 237.7 + Ta ) ) - 1.57×ws - 4.0 Taking account of temperature, wind and humidity and using a dewpoint of 14°C as a reference comfort level makes this a better indication of what your body feels than the other measures listed, but strictly it only applies in the shade.", "The (USA) Heat Index only applies if the actual temperature is at or above 27 °C (80 °F) and relative humidity peaks above 40% .: The index modifies air temperature using relative humidity in an attempt to determine the human-perceived equivalent temperature (how hot it feels).", "Wind Chill Temperature only applies for temperatures at or below 10 °C (50 °F) and wind speeds above 3.0 mph (4.8 kilometres per hour, 1.34 metres per second, or 2.6 knots) (it is selected if the outdoor temperature today is or has fallen below 10.5, 51 respectively and today's maximum measured gust has exceeded quoted figure).: Wind Chill modifies air temperature using wind speed in an attempt to determine the exposed skin-perceived equivalent temperature (how cold it feels)."); //--------------------------------------------------------------- function dovariablebarchart(eidh){ var m, n, mo=[], tu = "°" + tempscale; /*-- some javascript code that inserts 'Apparent Temperature' normally; but replaces that with 'Wind Chill' if minimum temperature is below 12 degrees Celsius, or replaces it with 'Heat Index' if maximum temperature is above 24 degrees Celsius */ /* are units Celsius or Fahrenheit? */ if(tempscale=="C"){ // Celsius n= 10.5; // wind chill m = 27; // heat index }else{ // Fahrenheit n = 51; // wind chill m = 80; // heat index } var gust; switch(winddata[5]){ case "mph" : gust=3.0; break; case "km/h" : gust=4.8; break; case "m/s" : gust= 1.34; break; case "kts" : gust=2.6 ; break; default : gust=0.0; } var motext = dtext[1];var modata = ddata[1];var selectid = divid[1];var selectColour = dColour[1];var tit=divti[1];// apparent is default plot if((tempos[2] > m) && (humos[3] > 40)) { // check maximum motext = dtext[2];modata = ddata[3];selectid = divid[3];selectColour = dColour[3];tit=divti[5]; //heat index plot } else if ((tempos[1] <n) && (gust < winddata[4])) { // check minimum temp and maximum wind gust motext = dtext[3];modata = ddata[4];selectid = divid[4];selectColour = dColour[4];tit=divti[6]; // wind chill plot }// end of if else group checking minimum and maximum temperatures mo[0]=dtext[0];eidh.title ="header=["+'<img class="vam" src="'+gauges_images+'/information.png" alt=" graphic with little i representing information">'+"Feels Like ("+tu+")] body=["+getmo(mo)+"]";eidh.innerHTML += tit;// variable title written, check if summary page, if so appropriate figure is required and heading box requires a title if(document.getElementById("variable_figure")){eidw=document.getElementById("variable_figure");n=modata[1];if(!isNaN(n)){ // check if minimum measurement is available n =", "+"Min="+modata[1] + " °" + tempscale;} else {n="";}m=modata[2];if(!isNaN(m)){ // check if maximum measurement is available m =", "+"Max="+modata[2] + " °" + tempscale;} else {m="";}eidw.innerHTML=("Latest="+modata[0] + " °" + tempscale+n+m);mo[0]=motext;eidw.title="header=["+'<img class="vam" src="'+gauges_images+'/information.png" alt=" graphic with little i representing information">'+tit+" ("+tu+")] body=["+getmo(mo)+"]";} // end of summary page coding // rest of function only followed if bar chart to be drawn if(document.getElementById("windchill/heatindex")){eidw=document.getElementById("windchill/heatindex");var size=eidw.innerHTMLdotemps(tit,selectid,eidw,size,modata, motext, selectColour);}} // end of function //------------------------------------------------------------------------------ //variables for mouseover popup var mod =0;//mouseover popup delay (msecs)) var fst = "position:relative; cursor:pointer; "; var tds = "font-family: Verdana,Arial,Helvetica; font-size: 9px; color: #000055; " //------------------------------------------------------------------------------ /* COMMON SUBROUTINE used by ALL CALLS FOR MOUSE OVER TEXT to create body table when using BOXOVER script */ function getmo(mot){ // Based on script included with Cumulus, but modified to use classes to refer to external Cascading Style Sheet instead of style attributes embedded in script – see comments that follow var st = " "; st += "<table class='b_green cmo'>"; // defines a background colour for (var j=0;j<mot.length;j++){ st += "<tr><td class='labels'>"; // for example labels could be bold and have a background colour st += mot[j].substring(0,mot[j].indexOf(":")+1); // Extract string up to colon into first cell of row st += "</td><td class='site-data'>";// numbers are centered or whatever suits you st += mot[j].substring(mot[j].indexOf(":")+1); // Extract string after colon into second cell of row st += "</td></tr>"; } st += "</table>"; return st; } /* ------------------------ ALL TEMPERATURE BASED BAR CHARTS ------------------ */ function dotemps(tit,id,eid,size,data, text, nowColour){ var tgd,factor, bottom, scale, maxm, t, h, l, r, tu, val, fst= "position:relative; cursor:pointer; "; if(size=="small"){ tgd="<div id='"+id+"' style='"+fst+"width:90px;'><img src='”+gauges_images+”/thermos.gif''></div>”; factor=2; bottom=195; }else{ tgd="<div id='"+id+"' style='"+fst+"width:90px;'><img src='”+gauges_images+”/thermol.gif'></div>"; factor=1; bottom=375; } eid.parentNode.innerHTML=tgd; eid=document.getElementById(id); tu = "°" + tempscale; if(tempscale=="C"){ // Celsius scale=3.6/factor; maxm=60; }else{ // Fahrenheit scale=2/factor; maxm=140; } var valnow= parseFloat(data[0]); t=parseInt(((maxm - valnow) * scale)+16); h=(bottom-t)+1;// calculate length of column (origin is top left, so draw downwards) l=25; // left hand end of horizontal lines for maximum r=66; // right hand end of horizontal lines for minimum // Other end of horizontal lines for maximum and minimum set at 45 - Modified so lines do not overlap and always show var jg=new jsGraphics(id); jg.setColor(nowColour || "#cc9"); // column fill colour taken from either calling code or default here var mo = new Array() var c = 0; mo[c]=text; // descriptive text re observation or calculation c++; // increment array index if(!isNaN(t)){ // check if measurement is numerical jg.fillEllipse(36,(bottom-16),20,20); // bulb of thermonmeter added by SFWS jg.fillRect(41,t,9,h); // left and width of column plot are constants mo[c]="<span class='sienna'>Current:</span>"+valnow+" "+tu; // semi-colon separates cells of table } else mo[c]="Current temperature: Not Available"; // colon separates cells of table c++; if(data[1]){ // if minimum available jg.setColor("navy"); jg.setStroke(2); val= parseFloat(data[1]); t=parseInt(((maxm - val)*scale)+16); if(!isNaN(t))jg.drawLine(45,t,r,t); mo[c]="<span class='navy'>Minimum This Meteorological Day:</span>"+val+" "+tu; c++; } if(data[2]){ // if maximum available jg.setColor("red"); jg.setStroke(2); val=parseFloat(data[2]); t=parseInt(((maxm - val)*scale)+16); if(!isNaN(t))jg.drawLine(l,t,45,t); mo[c]="<span class='red'>Maximum This Meteorological Day:</span>"+val+" "+tu; c++; if(tempscale=="C"){ val=Math.round((parseFloat(data[2])*1.8)+32) mo[c]="<span class='red'>Maximum after conversion:</span>"+val+" "+"°" +"Farenheit"; c++; } } if(data[3]){ // if trend available jg.drawImage('gauges_images/'+data[4]+'.gif',41,8,9,29);// Modified (different rising/falling images and different position so trend visible at top!) data[4] is fixed text to determine filename mo[c]="Trend:"+data[3];// data[3] uses text that can be set in 'samplestrings.ini' } jg.paint(); eid.title="header=["+'<img class="vam" src="'+gauges_images+'/information.png" alt=" graphic with little i representing information">'+tit+" ("+tu+")] body=["+getmo(mo)+"]"; } /* ------------------------------------------------------------------------------------------- Note that the code works for all temperature and wind speed units permitted by Cumulus, 'variable_figure' is id for HTML where numbers to be written (perhaps on indexT.htm template page) and "feels_like" is id for HTML element where graphic to be drawn (normally on gaugesT.htm template page) ------------------------------------------------------ */ // --- tabular display --- if(document.getElementById("variable_figure")){ eidh=document.getElementById("variable_figure"); // call subroutine that determines which figures are to be displayed dovariablebarchart(eidh) } // --- multipurpose bar chart --- if(document.getElementById("feels_like")){ eidh=document.getElementById("feels_like"); // call subroutine that determines which bar chart is to be plotted dovariablebarchart(eidh) }
Sfws 18:53, 27 December 2012 (UTC)