/***************************************************************************
metar_parser . cpp - Metar Parser
Based on code originally in weatherlib . cpp .
- - - - - - - - - - - - - - - - - - -
begin : Wed June 7 2004
copyright : ( C ) 2004 by John Ratke
: ( C ) 2002 - 2004 Nadeem Hasan < nhasan @ kde . org >
: ( C ) 2002 - 2004 Ian Geiser < geiseri @ kde . org >
email : jratke @ comcast . net
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/***************************************************************************
* *
* This program 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 . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "config.h"
# include <tqdatetime.h>
# include <kdebug.h>
# include <math.h>
# include "metar_parser.h"
# include "stationdatabase.h"
# include "sun.h"
// Temperature conversion macros
# define TEMP_C_TO_F(x) ( ((x) * 9 / 5) + 32 )
# define TEMP_F_TO_C(x) ( ((x) - 32) * 5 / 9 )
MetarParser : : MetarParser ( StationDatabase * stationDB ,
KLocale : : MeasureSystem units ,
TQDate date ,
TQTime time ,
unsigned int localUTCOffset ) :
m_stationDb ( stationDB ) , m_units ( units ) , m_date ( date ) , m_time ( time ) , m_localUTCOffset ( localUTCOffset )
{
CoverRegExp = TQRegExp ( " ^(FEW|SCT|BKN|OVC|SKC|CLR|CAVOK)([0-9]{3})?(?:TCU|CB)?$ " ) ;
CurrentRegExp = TQRegExp ( " ^( \\ +|-|VC)?([A-Z]{2,4})$ " ) ;
WindRegExp = TQRegExp ( " ^([0-9]{3}|VRB)([0-9]{2,3})(?:G([0-9]{2,3}))?(KT|KMH|MPS)$ " ) ;
VisRegExp = TQRegExp ( " ^([0-9]{1,2})SM$ " ) ;
VisFracRegExp = TQRegExp ( " ^1/(2|4)SM$ " ) ;
TempRegExp = TQRegExp ( " ^(M)?([0-9]{2})/(?:(M)?([0-9]{2}))?$ " ) ;
TimeRegExp = TQRegExp ( " ^([0-9]{2}:[0-9]{2})$ " ) ;
DateRegExp = TQRegExp ( " ^([0-9]{4}/[0-9]{2}/[0-9]{2})$ " ) ;
PressRegExp = TQRegExp ( " ^([AQ])([0-9]{4})$ " ) ;
TempTenRegExp = TQRegExp ( " ^T([01][0-9]{3})([01][0-9]{3})$ " ) ;
}
void MetarParser : : reset ( )
{
// Initialize the WeatherInfo structure
weatherInfo . theWeather = TQString : : null ;
weatherInfo . clouds = 0 ;
weatherInfo . windMPH = 0 ;
weatherInfo . tempC = 0 ;
weatherInfo . dewC = 0 ;
weatherInfo . heavy = false ;
weatherInfo . qsCoverList . clear ( ) ;
weatherInfo . qsCurrentList . clear ( ) ;
weatherInfo . qsDate = m_date ;
weatherInfo . qsTime = m_time ;
weatherInfo . qsPressure = TQString : : null ;
weatherInfo . qsTemperature = TQString : : null ;
weatherInfo . qsDewPoint = TQString : : null ;
weatherInfo . qsRelHumidity = TQString : : null ;
weatherInfo . qsVisibility = TQString : : null ;
weatherInfo . qsWindSpeed = TQString : : null ;
weatherInfo . qsWindChill = TQString : : null ;
weatherInfo . qsHeatIndex = TQString : : null ;
weatherInfo . qsWindDirection = TQString : : null ;
weatherInfo . stationNeedsMaintenance = false ;
}
struct WeatherInfo MetarParser : : processData ( const TQString & stationID , const TQString & metar )
{
reset ( ) ;
weatherInfo . reportLocation = stationID ;
kdDebug ( 12006 ) < < " Processing data: " < < metar < < endl ;
// Split at whitespace into tokens
TQStringList dataList = TQStringList : : split ( TQRegExp ( " \\ s+ " ) , metar ) ;
bool found = false ;
bool beforeRemark = true ;
for ( TQStringList : : ConstIterator it = dataList . begin ( ) ;
it ! = dataList . end ( ) ; + + it )
{
// Don't try to parse the ICAO location code
if ( ( ! found ) & & ( * it = = weatherInfo . reportLocation . upper ( ) . stripWhiteSpace ( ) ) )
{
found = true ;
continue ;
}
kdDebug ( 12006 ) < < " Processing Token: " < < * it < < endl ;
if ( * it = = " RMK " )
{
beforeRemark = false ;
continue ;
}
if ( beforeRemark )
{
if ( parseDate ( * it ) )
continue ;
if ( parseTime ( * it ) )
continue ;
if ( parseWindSpeed ( * it ) )
continue ;
if ( parseVisibility ( it ) ) // Note, pass in iterator.
continue ;
if ( parseTemperature ( * it ) )
continue ;
if ( parsePressure ( * it ) )
continue ;
if ( parseCover ( * it ) )
continue ;
if ( parseCurrent ( * it ) )
continue ;
}
else
{
if ( parseTemperatureTenths ( * it ) )
continue ;
if ( parseStationNeedsMaintenance ( * it ) )
continue ;
}
}
calcTemperatureVariables ( ) ;
calcWindChill ( ) ;
calcCurrentIcon ( ) ;
return weatherInfo ;
}
/** Parse the current cover type */
bool MetarParser : : parseCover ( const TQString & s )
{
if ( CoverRegExp . search ( s ) > - 1 )
{
kdDebug ( 12006 ) < < " Cover: " < < CoverRegExp . capturedTexts ( ) . join ( " - " )
< < endl ;
TQString sCode = CoverRegExp . cap ( 1 ) ;
float height = CoverRegExp . cap ( 2 ) . toFloat ( ) ; // initially in 100's of feet
TQString sClouds ;
TQString skycondition ;
height * = 100 ;
if ( m_units = = KLocale : : Metric )
{
height = height * 0.3048 ;
// using plural i18n form for proper translations
sClouds = i18n ( " 1 meter " , " %n meters " , ( int ) height ) ;
}
else
{
// using plural i18n form for proper translations
sClouds = i18n ( " 1 foot " , " %n feet " , ( int ) height ) ;
}
if ( sCode = = " FEW " )
{
skycondition = i18n ( " Few clouds at %1 " ) . arg ( sClouds ) ;
weatherInfo . clouds + = 2 ;
}
else if ( sCode = = " SCT " )
{
skycondition = i18n ( " Scattered clouds at %1 " ) . arg ( sClouds ) ;
weatherInfo . clouds + = 4 ;
}
else if ( sCode = = " BKN " )
{
skycondition = i18n ( " Broken clouds at %1 " ) . arg ( sClouds ) ;
weatherInfo . clouds + = 8 ;
}
else if ( sCode = = " OVC " )
{
skycondition = i18n ( " Overcast clouds at %1 " ) . arg ( sClouds ) ;
weatherInfo . clouds + = 64 ;
}
else if ( ( sCode = = " CLR " ) | | ( sCode = = " SKC " ) | | ( sCode = = " CAVOK " ) )
{
skycondition = i18n ( " Clear skies " ) ;
weatherInfo . clouds = 0 ;
}
kdDebug ( 12006 ) < < " *** Clouds: " < < weatherInfo . clouds < < endl ;
weatherInfo . qsCoverList < < skycondition ;
return true ;
}
return false ;
}
/** Parse the current weather conditions */
bool MetarParser : : parseCurrent ( const TQString & s )
{
if ( CurrentRegExp . search ( s ) > - 1 )
{
TQString sIntensity = CurrentRegExp . cap ( 1 ) ;
TQString sCode = CurrentRegExp . cap ( 2 ) ;
TQString intensity , descriptor , phenomena , currentWeather ;
kdDebug ( 12006 ) < < " Current: " < < CurrentRegExp . capturedTexts ( ) . join ( " - " ) < < endl ;
// Decode the intensity
if ( sIntensity = = " + " )
{
intensity = i18n ( " Heavy " ) ;
weatherInfo . heavy = true ;
}
else if ( sIntensity = = " - " )
{
intensity = i18n ( " Light " ) ;
weatherInfo . heavy = false ;
}
// Decode the descriptor
if ( sCode . contains ( " MI " ) )
descriptor = i18n ( " Shallow " ) ;
else if ( sCode . contains ( " PR " ) )
descriptor = i18n ( " Partial " ) ;
else if ( sCode . contains ( " BC " ) )
descriptor = i18n ( " Patches " ) ;
else if ( sCode . contains ( " DR " ) )
descriptor = i18n ( " Low Drifting " ) ;
else if ( sCode . contains ( " BL " ) )
descriptor = i18n ( " Blowing " ) ;
else if ( sCode . contains ( " SH " ) )
{
descriptor = i18n ( " Showers " ) ;
weatherInfo . theWeather = " shower " ;
}
else if ( sCode . contains ( " TS " ) )
{
descriptor = i18n ( " Thunder Storm " ) ;
weatherInfo . theWeather = " tstorm " ;
}
else if ( sCode . contains ( " FZ " ) )
{
descriptor = i18n ( " Freezing " ) ;
}
// Decode weather phenomena
if ( sCode . contains ( " DZ " ) )
{
phenomena = i18n ( " Drizzle " ) ;
weatherInfo . theWeather = " light_rain " ;
}
else if ( sCode . contains ( " RA " ) )
{
phenomena = i18n ( " Rain " ) ;
weatherInfo . theWeather = " shower " ;
}
else if ( sCode . contains ( " SN " ) )
{
phenomena = i18n ( " Snow " ) ;
weatherInfo . theWeather = " snow " ;
}
else if ( sCode . contains ( " SG " ) )
{
phenomena = i18n ( " Snow Grains " ) ;
weatherInfo . theWeather = " snow4 " ;
}
else if ( sCode . contains ( " IC " ) )
{
phenomena = i18n ( " Ice Crystals " ) ;
weatherInfo . theWeather = " hail " ;
}
else if ( sCode . contains ( " PE " ) )
{
phenomena = i18n ( " Ice Pellets " ) ;
weatherInfo . theWeather = " hail " ;
}
else if ( s . contains ( " GR " ) )
{
phenomena = i18n ( " Hail " ) ;
weatherInfo . theWeather = " hail " ;
}
else if ( sCode . contains ( " GS " ) )
{
phenomena = i18n ( " Small Hail Pellets " ) ;
weatherInfo . theWeather = " hail " ;
}
else if ( s . contains ( " UP " ) )
{
phenomena = i18n ( " Unknown Precipitation " ) ;
weatherInfo . theWeather = iconName ( " shower1 " ) ;
}
else if ( sCode . contains ( " BR " ) )
{
phenomena = i18n ( " Mist " ) ;
// Mist has lower priority than say rain or snow
if ( weatherInfo . theWeather . isEmpty ( ) )
{
weatherInfo . theWeather = " mist " ;
}
}
else if ( sCode . contains ( " FG " ) )
{
phenomena = i18n ( " Fog " ) ;
// Fog has lower priority than say rain or snow
if ( weatherInfo . theWeather . isEmpty ( ) )
{
weatherInfo . theWeather = " fog " ;
}
}
else if ( sCode . contains ( " FU " ) )
phenomena = i18n ( " Smoke " ) ;
else if ( sCode . contains ( " VA " ) )
phenomena = i18n ( " Volcanic Ash " ) ;
else if ( sCode . contains ( " DU " ) )
phenomena = i18n ( " Widespread Dust " ) ;
else if ( sCode . contains ( " SA " ) )
phenomena = i18n ( " Sand " ) ;
else if ( sCode . contains ( " HZ " ) )
phenomena = i18n ( " Haze " ) ;
else if ( sCode . contains ( " PY " ) )
phenomena = i18n ( " Spray " ) ;
else if ( sCode . contains ( " PO " ) )
phenomena = i18n ( " Dust/Sand Swirls " ) ;
else if ( sCode . contains ( " SQ " ) )
phenomena = i18n ( " Sudden Winds " ) ;
else if ( sCode . contains ( " FC " ) )
{
if ( sIntensity = = " + " )
currentWeather = i18n ( " Tornado " ) ;
else
phenomena = i18n ( " Funnel Cloud " ) ;
}
else if ( sCode . contains ( " SS " ) )
phenomena = i18n ( " Sand Storm " ) ;
else if ( sCode . contains ( " DS " ) )
phenomena = i18n ( " Dust Storm " ) ;
if ( currentWeather . isEmpty ( ) ) currentWeather = i18n ( " %1 is the intensity, %2 is the descriptor and %3 is the phenomena " , " %1 %2 %3 " ) . arg ( intensity ) . arg ( descriptor ) . arg ( phenomena ) ;
if ( ! currentWeather . isEmpty ( ) )
weatherInfo . qsCurrentList < < currentWeather ;
return true ;
}
return false ;
}
/** Parse out the current temperature */
bool MetarParser : : parseTemperature ( const TQString & s )
{
if ( TempRegExp . search ( s ) > - 1 )
{
kdDebug ( 12006 ) < < " Temp: " < < TempRegExp . capturedTexts ( ) . join ( " - " )
< < endl ;
float fTemp = TempRegExp . cap ( 2 ) . toFloat ( ) ;
if ( TempRegExp . cap ( 1 ) = = " M " & & fTemp ! = 0 )
fTemp * = - 1 ;
float fDew = TempRegExp . cap ( 4 ) . toFloat ( ) ;
if ( TempRegExp . cap ( 3 ) = = " M " & & fDew ! = 0 )
fDew * = - 1 ;
weatherInfo . tempC = fTemp ;
weatherInfo . dewC = fDew ;
return true ;
}
return false ;
}
bool MetarParser : : parseTemperatureTenths ( const TQString & s )
{
if ( TempTenRegExp . search ( s ) > - 1 )
{
kdDebug ( 12006 ) < < " Temp Tenths: " < < TempTenRegExp . capturedTexts ( ) . join ( " - " )
< < endl ;
float temperature = TempTenRegExp . cap ( 1 ) . toFloat ( ) / 10 ;
float dewPoint = TempTenRegExp . cap ( 2 ) . toFloat ( ) / 10 ;
if ( temperature > = 100 )
{
temperature - = 100 ;
temperature * = - 1 ;
}
if ( dewPoint > = 100 )
{
dewPoint - = 100 ;
dewPoint * = - 1 ;
}
weatherInfo . tempC = temperature ;
weatherInfo . dewC = dewPoint ;
return true ;
}
return false ;
}
void MetarParser : : calcTemperatureVariables ( )
{
# define E(t) ::pow(10, 7.5*t / (237.7+t))
float fRelHumidity = E ( weatherInfo . dewC ) / E ( weatherInfo . tempC ) * 100 ;
if ( fRelHumidity > 100.0 ) fRelHumidity = 100.0 ;
weatherInfo . qsRelHumidity . sprintf ( " %.1f " , fRelHumidity ) ;
removeTrailingDotZero ( weatherInfo . qsRelHumidity ) ;
weatherInfo . qsRelHumidity + = " % " ;
float fHeatIndex = 0 ;
float tempF = TEMP_C_TO_F ( weatherInfo . tempC ) ;
if ( tempF > = 80 )
{
# define SQR(a) ((a)*(a))
fHeatIndex = - 42.379 + ( 2.04901523 * tempF )
+ ( 10.14333127 * fRelHumidity )
- ( 0.22475541 * tempF * fRelHumidity )
- ( 0.00683783 * SQR ( tempF ) )
- ( 0.05481717 * SQR ( fRelHumidity ) )
+ ( 0.00122874 * SQR ( tempF ) * fRelHumidity )
+ ( 0.00085282 * tempF * SQR ( fRelHumidity ) )
- ( 0.00000199 * SQR ( tempF ) * SQR ( fRelHumidity ) ) ;
if ( fHeatIndex < = tempF )
fHeatIndex = 0 ;
}
TQString unit ;
if ( m_units = = KLocale : : Metric )
{
unit = i18n ( " °C " ) ;
weatherInfo . qsTemperature . sprintf ( " %.1f " , weatherInfo . tempC ) ;
weatherInfo . qsDewPoint . sprintf ( " %.1f " , weatherInfo . dewC ) ;
if ( fHeatIndex > = 80 )
weatherInfo . qsHeatIndex . sprintf ( " %.1f " , TEMP_F_TO_C ( fHeatIndex ) ) ;
}
else
{
unit = i18n ( " °F " ) ;
weatherInfo . qsTemperature . sprintf ( " %.1f " , tempF ) ;
weatherInfo . qsDewPoint . sprintf ( " %.1f " , TEMP_C_TO_F ( weatherInfo . dewC ) ) ;
if ( fHeatIndex > = 80 )
weatherInfo . qsHeatIndex . sprintf ( " %.1f " , ( fHeatIndex ) ) ;
}
removeTrailingDotZero ( weatherInfo . qsTemperature ) ;
removeTrailingDotZero ( weatherInfo . qsDewPoint ) ;
removeTrailingDotZero ( weatherInfo . qsHeatIndex ) ;
weatherInfo . qsTemperature + = unit ;
weatherInfo . qsDewPoint + = unit ;
if ( ! weatherInfo . qsHeatIndex . isEmpty ( ) )
weatherInfo . qsHeatIndex + = unit ;
}
void MetarParser : : removeTrailingDotZero ( TQString & string )
{
if ( string . right ( 2 ) = = " .0 " )
{
string = string . left ( string . length ( ) - 2 ) ;
}
}
/** Parse out the current date. */
bool MetarParser : : parseDate ( const TQString & s )
{
if ( DateRegExp . search ( s ) > - 1 )
{
kdDebug ( 12006 ) < < " Date: " < < DateRegExp . capturedTexts ( ) . join ( " - " )
< < endl ;
TQString dateString = DateRegExp . cap ( 1 ) ;
TQString day , month , year ;
day = dateString . mid ( 8 , 2 ) ;
month = dateString . mid ( 5 , 2 ) ;
year = dateString . mid ( 0 , 4 ) ;
TQDate theDate ( year . toInt ( ) , month . toInt ( ) , day . toInt ( ) ) ;
weatherInfo . qsDate = theDate ;
return true ;
}
return false ;
}
/** Parse out the current time. */
bool MetarParser : : parseTime ( const TQString & s )
{
if ( TimeRegExp . search ( s ) > - 1 )
{
kdDebug ( 12006 ) < < " Time: " < < TimeRegExp . capturedTexts ( ) . join ( " - " )
< < endl ;
TQString hour , minute , dateString ;
dateString = TimeRegExp . cap ( 1 ) ;
hour = dateString . mid ( 0 , 2 ) ;
minute = dateString . mid ( 3 , 2 ) ;
TQTime theTime ( hour . toInt ( ) , minute . toInt ( ) ) ;
weatherInfo . qsTime = theTime ;
return true ;
}
return false ;
}
/** Parse out the current visibility */
bool MetarParser : : parseVisibility ( TQStringList : : ConstIterator it )
{
float fVisibility = 0 ;
if ( VisRegExp . search ( * it ) > - 1 )
{
fVisibility = VisRegExp . cap ( 1 ) . toFloat ( ) ;
kdDebug ( 12006 ) < < " Visibility: " < < VisRegExp . capturedTexts ( ) . join ( " - " )
< < endl ;
}
else if ( VisFracRegExp . search ( * it ) > - 1 )
{
// got a fractional visibility, go back to previous string in the list
// and get the whole part.
fVisibility = ( * ( it - - ) ) . toFloat ( ) ;
// shouldn't be necessary?
//it++;
fVisibility + = ( 1 / VisFracRegExp . cap ( 1 ) . toFloat ( ) ) ;
}
if ( fVisibility > 0 )
{
if ( m_units = = KLocale : : Metric )
{
fVisibility * = 1.6 ;
weatherInfo . qsVisibility . setNum ( fVisibility ) ;
weatherInfo . qsVisibility + = i18n ( " km " ) ;
}
else
{
weatherInfo . qsVisibility . setNum ( fVisibility ) ;
weatherInfo . qsVisibility + = i18n ( " m " ) ;
}
return true ;
}
else
{
return false ;
}
}
/** Parse out the current pressure. */
bool MetarParser : : parsePressure ( const TQString & s )
{
if ( PressRegExp . search ( s ) > - 1 )
{
TQString type = PressRegExp . cap ( 1 ) ;
float fPressure = PressRegExp . cap ( 2 ) . toFloat ( ) ;
kdDebug ( 12006 ) < < " Pressure: " < < PressRegExp . capturedTexts ( ) . join ( " - " )
< < endl ;
if ( m_units = = KLocale : : Metric )
{
if ( type = = " A " )
fPressure * = ( 33.8639 / 100 ) ;
weatherInfo . qsPressure . setNum ( fPressure , ' f ' , 0 ) ;
weatherInfo . qsPressure + = i18n ( " hPa " ) ;
}
else
{
if ( type = = " Q " )
fPressure / = 33.8639 ;
else
fPressure / = 100 ;
weatherInfo . qsPressure . setNum ( fPressure , ' f ' , 2 ) ;
weatherInfo . qsPressure + = i18n ( " \" Hg " ) ;
}
return true ;
}
return false ;
}
struct wind_info
{
unsigned int number ;
TQString name ;
} ;
static const struct wind_info wind_direction [ ] =
{
{ 0 , i18n ( " N " ) } , // North is 0 to 11, and so on
{ 12 , i18n ( " NNE " ) } ,
{ 33 , i18n ( " NE " ) } ,
{ 57 , i18n ( " ENE " ) } ,
{ 79 , i18n ( " E " ) } ,
{ 102 , i18n ( " ESE " ) } ,
{ 124 , i18n ( " SE " ) } ,
{ 147 , i18n ( " SSE " ) } ,
{ 169 , i18n ( " S " ) } ,
{ 192 , i18n ( " SSW " ) } ,
{ 214 , i18n ( " SW " ) } ,
{ 237 , i18n ( " WSW " ) } ,
{ 259 , i18n ( " W " ) } ,
{ 282 , i18n ( " WNW " ) } ,
{ 304 , i18n ( " NW " ) } ,
{ 327 , i18n ( " NNW " ) } ,
{ 349 , i18n ( " N " ) } ,
{ 360 , i18n ( " N " ) }
} ;
TQString MetarParser : : parseWindDirection ( const unsigned int direction )
{
unsigned int i = 0 ;
for ( i = 0 ; i < ( sizeof ( wind_direction ) / sizeof ( wind_info ) ) - 1 ; i + + )
{
if ( direction > = wind_direction [ i ] . number & &
direction < wind_direction [ i + 1 ] . number )
{
break ;
}
}
return wind_direction [ i ] . name ;
}
/** Parse the wind speed */
bool MetarParser : : parseWindSpeed ( const TQString & s )
{
if ( WindRegExp . search ( s ) > - 1 )
{
unsigned int direction = WindRegExp . cap ( 1 ) . toInt ( ) ;
float windSpeed = WindRegExp . cap ( 2 ) . toFloat ( ) ;
float gustSpeed = WindRegExp . cap ( 3 ) . toFloat ( ) ;
TQString sWindUnit = WindRegExp . cap ( 4 ) ;
kdDebug ( 12006 ) < < " Wind: " < < WindRegExp . capturedTexts ( ) . join ( " - " )
< < endl ;
if ( m_units = = KLocale : : Metric )
{
if ( sWindUnit = = " KT " )
{
windSpeed = ( windSpeed * 3.6 / 1.94 ) ;
gustSpeed = ( gustSpeed * 3.6 / 1.94 ) ;
}
else if ( sWindUnit = = " MPS " )
{
windSpeed = ( windSpeed * 3.6 ) ;
gustSpeed = ( gustSpeed * 3.6 ) ;
}
weatherInfo . windMPH = ( windSpeed / 1.61 ) ;
weatherInfo . qsWindSpeed = i18n ( " 1 km/h " , " %n km/h " , ( int ) windSpeed ) ;
}
else
{
if ( sWindUnit = = " KT " )
{
windSpeed = ( windSpeed * 2.24 / 1.94 ) ;
gustSpeed = ( gustSpeed * 2.24 / 1.94 ) ;
}
else if ( sWindUnit = = " KMH " )
{
windSpeed = ( windSpeed / 1.61 ) ;
gustSpeed = ( gustSpeed / 1.61 ) ;
}
else if ( sWindUnit = = " MPS " )
{
windSpeed = ( windSpeed * 2.24 ) ;
gustSpeed = ( gustSpeed * 2.24 ) ;
}
weatherInfo . windMPH = windSpeed ;
weatherInfo . qsWindSpeed = i18n ( " 1 MPH " , " %n MPH " , ( int ) windSpeed ) ;
}
if ( gustSpeed > = 1 )
{
if ( m_units = = KLocale : : Metric )
{
weatherInfo . qsCurrentList < < i18n ( " Wind gusts up to 1 km/h " ,
" Wind gusts up to %n km/h " , ( int ) gustSpeed ) ;
}
else
{
weatherInfo . qsCurrentList < < i18n ( " Wind gusts up to 1 MPH " ,
" Wind gusts up to %n MPH " , ( int ) gustSpeed ) ;
}
}
if ( ( WindRegExp . cap ( 1 ) ! = " VRB " ) & & ( windSpeed > = 1 ) )
{
weatherInfo . qsWindDirection = parseWindDirection ( direction ) ;
}
return true ;
}
return false ;
}
bool MetarParser : : parseStationNeedsMaintenance ( const TQString & s )
{
if ( s = = " $ " )
{
weatherInfo . stationNeedsMaintenance = true ;
kdDebug ( 12006 ) < < " Station Needs Maintenance " < < endl ;
return true ;
}
return false ;
}
void MetarParser : : calcCurrentIcon ( )
{
// Default to overcast clouds
if ( weatherInfo . clouds = = - 1 )
weatherInfo . clouds = 64 ;
if ( weatherInfo . theWeather . isEmpty ( ) )
{
if ( weatherInfo . clouds = = 0 )
weatherInfo . theWeather = iconName ( " sunny " ) ;
else if ( weatherInfo . clouds > 0 & & weatherInfo . clouds < = 2 )
weatherInfo . theWeather = iconName ( " cloudy1 " ) ;
else if ( weatherInfo . clouds > 2 & & weatherInfo . clouds < = 4 )
weatherInfo . theWeather = iconName ( " cloudy2 " ) ;
else if ( weatherInfo . clouds > 4 & & weatherInfo . clouds < = 8 )
weatherInfo . theWeather = iconName ( " cloudy3 " ) ;
else if ( weatherInfo . clouds > 8 & & weatherInfo . clouds < 63 )
weatherInfo . theWeather = iconName ( " cloudy4 " ) ;
else
weatherInfo . theWeather = " cloudy5 " ;
}
else if ( weatherInfo . theWeather = = " tstorm " )
{
if ( weatherInfo . heavy )
weatherInfo . clouds = 30 ;
if ( weatherInfo . clouds > = 0 & & weatherInfo . clouds < = 10 )
weatherInfo . theWeather = iconName ( " tstorm1 " ) ;
else if ( weatherInfo . clouds > 10 & & weatherInfo . clouds < = 20 )
weatherInfo . theWeather = iconName ( " tstorm2 " ) ;
else
weatherInfo . theWeather = " tstorm3 " ;
}
else if ( weatherInfo . theWeather = = " shower " )
{
if ( weatherInfo . heavy )
weatherInfo . clouds = 30 ;
if ( weatherInfo . clouds > = 0 & & weatherInfo . clouds < = 10 )
weatherInfo . theWeather = iconName ( " shower1 " ) ;
else if ( weatherInfo . clouds > 10 & & weatherInfo . clouds < = 20 )
weatherInfo . theWeather = iconName ( " shower2 " ) ;
else
weatherInfo . theWeather = " shower3 " ;
}
else if ( weatherInfo . theWeather = = " snow " )
{
if ( weatherInfo . heavy )
weatherInfo . clouds = 30 ;
if ( weatherInfo . clouds > = 0 & & weatherInfo . clouds < = 8 )
weatherInfo . theWeather = iconName ( " snow1 " ) ;
else if ( weatherInfo . clouds > 8 & & weatherInfo . clouds < = 16 )
weatherInfo . theWeather = iconName ( " snow2 " ) ;
else if ( weatherInfo . clouds > 16 & & weatherInfo . clouds < = 24 )
weatherInfo . theWeather = iconName ( " snow3 " ) ;
else
weatherInfo . theWeather = " snow5 " ;
}
else if ( isNight ( weatherInfo . reportLocation ) & & weatherInfo . theWeather = = " mist " )
weatherInfo . theWeather = " mist_night " ;
else if ( isNight ( weatherInfo . reportLocation ) & & weatherInfo . theWeather = = " fog " )
weatherInfo . theWeather = " fog_night " ;
else if ( weatherInfo . theWeather = = " mist " | | weatherInfo . theWeather = = " fog " )
{
if ( weatherInfo . clouds > = 63 )
weatherInfo . theWeather = " cloudy5 " ;
}
kdDebug ( 12006 ) < < " Clouds: " < < weatherInfo . clouds < < " , Icon: "
< < weatherInfo . theWeather < < endl ;
}
void MetarParser : : calcWindChill ( )
{
float windChill = 35.74 + ( 0.6215 * TEMP_C_TO_F ( weatherInfo . tempC ) )
- ( 35.75 * : : pow ( weatherInfo . windMPH , 0.16 ) )
+ ( 0.4275 * TEMP_C_TO_F ( weatherInfo . tempC ) * : : pow ( weatherInfo . windMPH , 0.16 ) ) ;
kdDebug ( 12006 ) < < " Wind Chill: " < < windChill < < endl ;
if ( windChill < 48 )
{
if ( m_units = = KLocale : : Metric )
{
weatherInfo . qsWindChill . setNum ( TEMP_F_TO_C ( windChill ) , ' f ' , 1 ) ;
removeTrailingDotZero ( weatherInfo . qsWindChill ) ;
weatherInfo . qsWindChill + = i18n ( " °C " ) ;
}
else
{
weatherInfo . qsWindChill . setNum ( windChill , ' f ' , 1 ) ;
removeTrailingDotZero ( weatherInfo . qsWindChill ) ;
weatherInfo . qsWindChill + = i18n ( " °F " ) ;
}
}
}
bool MetarParser : : isNight ( const TQString & stationID ) const
{
TQString upperStationID = stationID . upper ( ) ;
TQString latitude = m_stationDb - > stationLatitudeFromID ( upperStationID ) ;
TQString longitude = m_stationDb - > stationLongitudeFromID ( upperStationID ) ;
if ( latitude . compare ( i18n ( " Unknown Station " ) ) = = 0 | |
longitude . compare ( i18n ( " Unknown Station " ) ) = = 0 )
{
return false ;
}
else
{
Sun theSun ( latitude , longitude , m_date , m_localUTCOffset ) ;
TQTime currently = m_time ;
TQTime civilStart = theSun . computeCivilTwilightStart ( ) ;
TQTime civilEnd = theSun . computeCivilTwilightEnd ( ) ;
kdDebug ( 12006 ) < < " station, current, lat, lon, start, end, offset: " < <
upperStationID < < " " < < currently < < " " < < latitude < < " " < <
longitude < < " " < < civilStart < < " " < < civilEnd < < " " < <
m_localUTCOffset < < endl ;
if ( civilStart ! = civilEnd )
{
if ( civilEnd < civilStart )
/* Handle daylight past midnight in local time */
/* for weather stations located at other timezones */
return ( currently < civilStart & & currently > civilEnd ) ;
else
return ( currently < civilStart | | currently > civilEnd ) ;
}
else
{
// Midnight Sun & Polar Night - In summer, the Sun is always
// over the horizon line ... so use latitude & today date to
// set isNight() value.
return ( ( m_date . daysInYear ( ) > = 80 | | m_date . daysInYear ( ) < = 264 ) & & latitude . contains ( " S " ) ) ;
}
}
}
TQString MetarParser : : iconName ( const TQString & icon ) const
{
TQString _iconName = icon ;
if ( isNight ( weatherInfo . reportLocation ) )
_iconName + = " _night " ;
return _iconName ;
}