You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdetoys/kweather/metar_parser.cpp

874 lines
22 KiB

/***************************************************************************
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. *
* *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_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();
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();
weatherInfo.qsTemperature = TQString();
weatherInfo.qsDewPoint = TQString();
weatherInfo.qsRelHumidity = TQString();
weatherInfo.qsVisibility = TQString();
weatherInfo.qsWindSpeed = TQString();
weatherInfo.qsWindChill = TQString();
weatherInfo.qsHeatIndex = TQString();
weatherInfo.qsWindDirection = TQString();
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: " << TQString(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: " << TQString(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: " << TQString(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: " << TQString(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: " << TQString(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: " << TQString(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: " << TQString(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: " << TQString(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;
}