// // This file is part of the Marble Desktop Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2009 Bastian Holst // // Self #include "BBCParser.h" // Marble #include "global.h" #include "WeatherData.h" #include "BBCWeatherItem.h" #include "MarbleDebug.h" // Qt #include #include #include #include #include using namespace Marble; QHash BBCParser::dayConditions = QHash(); QHash BBCParser::nightConditions = QHash(); QHash BBCParser::windDirections = QHash(); QHash BBCParser::pressureDevelopments = QHash(); QHash BBCParser::visibilityStates = QHash(); QHash BBCParser::monthNames = QHash(); BBCParser::BBCParser( QObject *parent ) : AbstractWorkerThread( parent ) { BBCParser::setupHashes(); } BBCParser::~BBCParser() { } BBCParser *BBCParser::instance() { static BBCParser parser; return &parser; } void BBCParser::scheduleRead( const QString& path, BBCWeatherItem *item, const QString& type ) { ScheduleEntry entry; entry.path = path; entry.item = item; entry.type = type; m_schedule.push( entry ); ensureRunning(); } bool BBCParser::workAvailable() { return !m_schedule.isEmpty(); } void BBCParser::work() { ScheduleEntry entry = m_schedule.pop(); QFile file( entry.path ); if( !file.open( QIODevice::ReadOnly | QIODevice::Text ) ) { return; } QList data = read( &file ); if( !data.isEmpty() && !entry.item.isNull() ) { if ( entry.type == "bbcobservation" ) { entry.item->setCurrentWeather( data.at( 0 ) ); } else if ( entry.type == "bbcforecast" ) { entry.item->addForecastWeather( data ); } emit parsedFile(); } } QList BBCParser::read( QIODevice *device ) { m_list.clear(); setDevice( device ); while ( !atEnd() ) { readNext(); if ( isStartElement() ) { if ( name() == "rss" ) readBBC(); else raiseError( "The file is not an valid BBC answer." ); } } return m_list; } void BBCParser::readUnknownElement() { Q_ASSERT( isStartElement() ); while ( !atEnd() ) { readNext(); if ( isEndElement() ) break; if ( isStartElement() ) readUnknownElement(); } } void BBCParser::readBBC() { Q_ASSERT( isStartElement() && name() == "rss" ); while( !atEnd() ) { readNext(); if( isEndElement() ) break; if( isStartElement() ) { if( name() == "channel" ) readChannel(); else readUnknownElement(); } } } void BBCParser::readChannel() { Q_ASSERT( isStartElement() && name() == "channel" ); while( !atEnd() ) { readNext(); if( isEndElement() ) break; if( isStartElement() ) { if( name() == "item" ) readItem(); else readUnknownElement(); } } } void BBCParser::readItem() { Q_ASSERT( isStartElement() && name() == "item" ); WeatherData item; while( !atEnd() ) { readNext(); if( isEndElement() ) break; if( isStartElement() ) { if( name() == "description" ) readDescription( &item ); else if( name() == "title" ) readTitle( &item ); else if( name() == "pubDate" ) readPubDate( &item ); else readUnknownElement(); } } m_list.append( item ); } void BBCParser::readDescription( WeatherData *data ) { Q_ASSERT( isStartElement() && name() == "description" ); while( !atEnd() ) { readNext(); if( isEndElement() ) break; if( isStartElement() ) { readUnknownElement(); } if( isCharacters() ) { QString description = text().toString(); QRegExp regExp; // Temperature regExp.setPattern( "(Temperature:\\s*)(-?\\d+)(.C)" ); int pos = regExp.indexIn( description ); if ( pos > -1 ) { QString value = regExp.cap( 2 ); data->setTemperature( value.toDouble(), WeatherData::Celsius ); } // Max Temperature regExp.setPattern( "(Max Temp:\\s*)(-?\\d+)(.C)" ); pos = regExp.indexIn( description ); if ( pos > -1 ) { QString value = regExp.cap( 2 ); data->setMaxTemperature( value.toDouble(), WeatherData::Celsius ); } // Min Temperature regExp.setPattern( "(Min Temp:\\s*)(-?\\d+)(.C)" ); pos = regExp.indexIn( description ); if ( pos > -1 ) { QString value = regExp.cap( 2 ); data->setMinTemperature( value.toDouble(), WeatherData::Celsius ); } // Wind direction regExp.setPattern( "(Wind Direction:\\s*)([NESW]+)(,)" ); pos = regExp.indexIn( description ); if ( pos > -1 ) { QString wind = regExp.cap( 2 ); if ( windDirections.contains( wind ) ) { data->setWindDirection( windDirections.value( wind ) ); } else { mDebug() << "UNHANDLED WIND DIRECTION, PLEASE REPORT: " << wind; } } // Wind speed regExp.setPattern( "(Wind Speed:\\s*)(\\d+)(mph)" ); pos = regExp.indexIn( description ); if ( pos > -1 ) { QString speed = regExp.cap( 2 ); data->setWindSpeed( speed.toFloat(), WeatherData::mph ); } // Relative Humidity regExp.setPattern( "(Relative Humidity:\\s*)(\\d+)(.,)" ); pos = regExp.indexIn( description ); if ( pos > -1 ) { QString humidity = regExp.cap( 2 ); data->setHumidity( humidity.toFloat() ); } // Pressure regExp.setPattern( "(Pressure:\\s*)(\\d+mB|N/A)(, )([a-z ]+|N/A)(,)" ); pos = regExp.indexIn( description ); if ( pos > -1 ) { QString pressure = regExp.cap( 2 ); if ( pressure != "N/A" ) { pressure.chop( 2 ); data->setPressure( pressure.toFloat()/1000, WeatherData::Bar ); } QString pressureDevelopment = regExp.cap( 4 ); if ( pressureDevelopments.contains( pressureDevelopment ) ) { data->setPressureDevelopment( pressureDevelopments.value( pressureDevelopment ) ); } else { mDebug() << "UNHANDLED PRESSURE DEVELOPMENT, PLEASE REPORT: " << pressureDevelopment; } } // Visibility regExp.setPattern( "(Visibility:\\s*)([^,]+)" ); pos = regExp.indexIn( description ); if ( pos > -1 ) { QString visibility = regExp.cap( 2 ); if ( visibilityStates.contains( visibility.toLower() ) ) { data->setVisibilty( visibilityStates.value( visibility ) ); } else { mDebug() << "UNHANDLED VISIBILITY, PLEASE REPORT: " << visibility; } } } } } void BBCParser::readTitle( WeatherData *data ) { Q_ASSERT( isStartElement() && name() == "title" ); while( !atEnd() ) { readNext(); if( isEndElement() ) break; if( isStartElement() ) { readUnknownElement(); } if( isCharacters() ) { QString title = text().toString(); QRegExp regExp; // Condition regExp.setPattern( "(^.*)(:\\s*)([\\w ]+)([\\,\\.]\\s*)" ); int pos = regExp.indexIn( title ); if ( pos > -1 ) { QString value = regExp.cap( 3 ); if( dayConditions.contains( value ) ) { // TODO: Switch for day/night data->setCondition( dayConditions.value( value ) ); } else { mDebug() << "UNHANDLED BBC WEATHER CONDITION, PLEASE REPORT: " << value; } QString dayString = regExp.cap( 1 ); Qt::DayOfWeek dayOfWeek = (Qt::DayOfWeek) 0; if ( dayString.contains( "Monday" ) ) dayOfWeek = Qt::Monday; else if ( dayString.contains( "Tuesday" ) ) dayOfWeek = Qt::Tuesday; else if ( dayString.contains( "Wednesday" ) ) dayOfWeek = Qt::Wednesday; else if ( dayString.contains( "Thursday" ) ) dayOfWeek = Qt::Thursday; else if ( dayString.contains( "Friday" ) ) dayOfWeek = Qt::Friday; else if ( dayString.contains( "Saturday" ) ) dayOfWeek = Qt::Saturday; else if ( dayString.contains( "Sunday" ) ) dayOfWeek = Qt::Sunday; QDate date = QDate::currentDate(); date = date.addDays( -1 ); for ( int i = 0; i < 7; i++ ) { if ( date.dayOfWeek() == dayOfWeek ) { data->setDataDate( date ); } date = date.addDays( 1 ); } } } } } void BBCParser::readPubDate( WeatherData *data ) { Q_ASSERT( isStartElement() && name() == "pubDate" ); while( !atEnd() ) { readNext(); if( isEndElement() ) break; if( isStartElement() ) { readUnknownElement(); } if( isCharacters() ) { QString pubDate = text().toString(); QRegExp regExp; regExp.setPattern( "([A-Za-z]+,\\s+)(\\d+)(\\s+)([A-Za-z]+)(\\s+)(\\d{4,4})(\\s+)(\\d+)(:)(\\d+)(:)(\\d+)(\\s+)([+-])(\\d{2,2})(\\d{2,2})" ); int pos = regExp.indexIn( pubDate ); if ( pos > -1 ) { QDateTime dateTime; QDate date; QTime time; dateTime.setTimeSpec( Qt::UTC ); date.setYMD( regExp.cap( 6 ).toInt(), monthNames.value( regExp.cap( 4 ) ), regExp.cap( 2 ).toInt() ); time.setHMS( regExp.cap( 8 ).toInt(), regExp.cap( 10 ).toInt(), regExp.cap( 12 ).toInt() ); dateTime.setDate( date ); dateTime.setTime( time ); // Timezone if( regExp.cap( 14 ) == "-" ) { dateTime = dateTime.addSecs( 60*60*regExp.cap( 15 ).toInt() ); dateTime = dateTime.addSecs( 60 *regExp.cap( 16 ).toInt() ); } else { dateTime = dateTime.addSecs( -60*60*regExp.cap( 15 ).toInt() ); dateTime = dateTime.addSecs( -60 *regExp.cap( 16 ).toInt() ); } data->setPublishingTime( dateTime ); } } } } void BBCParser::setupHashes() { if( !( ( dayConditions.isEmpty() ) || ( nightConditions.isEmpty() ) || ( windDirections.isEmpty() ) || ( pressureDevelopments.isEmpty() ) || ( visibilityStates.isEmpty() ) || ( monthNames.isEmpty() ) ) ) { return; } dayConditions["sunny"] = WeatherData::ClearDay; dayConditions["clear"] = WeatherData::ClearDay; dayConditions["clear sky"] = WeatherData::ClearDay; dayConditions["sunny intervals"] = WeatherData::FewCloudsDay; dayConditions["partly cloudy"] = WeatherData::PartlyCloudyDay; dayConditions["white cloud"] = WeatherData::Overcast; dayConditions["grey cloud"] = WeatherData::Overcast; dayConditions["cloudy"] = WeatherData::Overcast; dayConditions["drizzle"] = WeatherData::LightRain; dayConditions["misty"] = WeatherData::Mist; dayConditions["mist"] = WeatherData::Mist; dayConditions["fog"] = WeatherData::Mist; dayConditions["foggy"] = WeatherData::Mist; dayConditions["dense fog"] = WeatherData::Mist; dayConditions["Thick Fog"] = WeatherData::Mist; dayConditions["tropical storm"] = WeatherData::Thunderstorm; dayConditions["hazy"] = WeatherData::Mist; dayConditions["light shower"] = WeatherData::LightShowersDay; dayConditions["light rain shower"] = WeatherData::LightShowersDay; dayConditions["light showers"] = WeatherData::LightShowersDay; dayConditions["light rain"] = WeatherData::ShowersDay; dayConditions["heavy rain"] = WeatherData::Rain; dayConditions["heavy showers"] = WeatherData::Rain; dayConditions["heavy shower"] = WeatherData::Rain; dayConditions["heavy rain shower"] = WeatherData::Rain; dayConditions["thundery shower"] = WeatherData::Thunderstorm; dayConditions["thunderstorm"] = WeatherData::Thunderstorm; dayConditions["thunder storm"] = WeatherData::Thunderstorm; dayConditions["cloudy with sleet"] = WeatherData::RainSnow; dayConditions["sleet shower"] = WeatherData::RainSnow; dayConditions["sleet showers"] = WeatherData::RainSnow; dayConditions["sleet"] = WeatherData::RainSnow; dayConditions["cloudy with hail"] = WeatherData::Hail; dayConditions["hail shower"] = WeatherData::Hail; dayConditions["hail showers"] = WeatherData::Hail; dayConditions["hail"] = WeatherData::Hail; dayConditions["light snow"] = WeatherData::LightSnow; dayConditions["light snow shower"] = WeatherData::ChanceSnowDay; dayConditions["light snow showers"] = WeatherData::ChanceSnowDay; dayConditions["cloudy with light snow"] = WeatherData::LightSnow; dayConditions["heavy snow"] = WeatherData::Snow; dayConditions["heavy snow shower"] = WeatherData::Snow; dayConditions["heavy snow showers"] = WeatherData::Snow; dayConditions["cloudy with heavy snow"] = WeatherData::Snow; dayConditions["sandstorm"] = WeatherData::SandStorm; dayConditions["na"] = WeatherData::ConditionNotAvailable; dayConditions["N/A"] = WeatherData::ConditionNotAvailable; nightConditions["sunny"] = WeatherData::ClearNight; nightConditions["clear"] = WeatherData::ClearNight; nightConditions["clear sky"] = WeatherData::ClearNight; nightConditions["sunny intervals"] = WeatherData::FewCloudsNight; nightConditions["partly cloudy"] = WeatherData::PartlyCloudyNight; nightConditions["white cloud"] = WeatherData::Overcast; nightConditions["grey cloud"] = WeatherData::Overcast; nightConditions["cloudy"] = WeatherData::Overcast; nightConditions["drizzle"] = WeatherData::LightRain; nightConditions["misty"] = WeatherData::Mist; nightConditions["mist"] = WeatherData::Mist; nightConditions["fog"] = WeatherData::Mist; nightConditions["foggy"] = WeatherData::Mist; nightConditions["dense fog"] = WeatherData::Mist; nightConditions["Thick Fog"] = WeatherData::Mist; nightConditions["tropical storm"] = WeatherData::Thunderstorm; nightConditions["hazy"] = WeatherData::Mist; nightConditions["light shower"] = WeatherData::LightShowersNight; nightConditions["light rain shower"] = WeatherData::LightShowersNight; nightConditions["light showers"] = WeatherData::LightShowersNight; nightConditions["light rain"] = WeatherData::ShowersNight; nightConditions["heavy rain"] = WeatherData::Rain; nightConditions["heavy showers"] = WeatherData::Rain; nightConditions["heavy shower"] = WeatherData::Rain; nightConditions["heavy rain shower"] = WeatherData::Rain; nightConditions["thundery shower"] = WeatherData::Thunderstorm; nightConditions["thunderstorm"] = WeatherData::Thunderstorm; nightConditions["thunder storm"] = WeatherData::Thunderstorm; nightConditions["cloudy with sleet"] = WeatherData::RainSnow; nightConditions["sleet shower"] = WeatherData::RainSnow; nightConditions["sleet showers"] = WeatherData::RainSnow; nightConditions["sleet"] = WeatherData::RainSnow; nightConditions["cloudy with hail"] = WeatherData::Hail; nightConditions["hail shower"] = WeatherData::Hail; nightConditions["hail showers"] = WeatherData::Hail; nightConditions["hail"] = WeatherData::Hail; nightConditions["light snow"] = WeatherData::LightSnow; nightConditions["light snow shower"] = WeatherData::ChanceSnowNight; nightConditions["light snow showers"] = WeatherData::ChanceSnowNight; nightConditions["cloudy with light snow"] = WeatherData::LightSnow; nightConditions["heavy snow"] = WeatherData::Snow; nightConditions["heavy snow shower"] = WeatherData::Snow; nightConditions["heavy snow showers"] = WeatherData::Snow; nightConditions["cloudy with heavy snow"] = WeatherData::Snow; nightConditions["sandstorm"] = WeatherData::SandStorm; nightConditions["na"] = WeatherData::ConditionNotAvailable; nightConditions["N/A"] = WeatherData::ConditionNotAvailable; windDirections["N"] = WeatherData::N; windDirections["NE"] = WeatherData::NE; windDirections["ENE"] = WeatherData::ENE; windDirections["NNE"] = WeatherData::NNE; windDirections["E"] = WeatherData::E; windDirections["SSE"] = WeatherData::SSE; windDirections["SE"] = WeatherData::SE; windDirections["ESE"] = WeatherData::ESE; windDirections["S"] = WeatherData::S; windDirections["NNW"] = WeatherData::NNW; windDirections["NW"] = WeatherData::NW; windDirections["WNW"] = WeatherData::WNW; windDirections["W"] = WeatherData::W; windDirections["SSW"] = WeatherData::SSW; windDirections["SW"] = WeatherData::SW; windDirections["WSW"] = WeatherData::WSW; windDirections["N/A"] = WeatherData::DirectionNotAvailable; pressureDevelopments["falling"] = WeatherData::Falling; pressureDevelopments["no change"] = WeatherData::NoChange; pressureDevelopments["steady"] = WeatherData::NoChange; pressureDevelopments["rising"] = WeatherData::Rising; pressureDevelopments["N/A"] = WeatherData::PressureDevelopmentNotAvailable; visibilityStates["excellent"] = WeatherData::VeryGood; visibilityStates["very good"] = WeatherData::VeryGood; visibilityStates["good"] = WeatherData::Good; visibilityStates["moderate"] = WeatherData::Normal; visibilityStates["poor"] = WeatherData::Poor; visibilityStates["very poor"] = WeatherData::VeryPoor; visibilityStates["fog"] = WeatherData::Fog; visibilityStates["n/a"] = WeatherData::VisibilityNotAvailable; monthNames["Jan"] = 1; monthNames["Feb"] = 2; monthNames["Mar"] = 3; monthNames["Apr"] = 4; monthNames["May"] = 5; monthNames["Jun"] = 6; monthNames["Jul"] = 7; monthNames["Aug"] = 8; monthNames["Sep"] = 9; monthNames["Oct"] = 10; monthNames["Nov"] = 11; monthNames["Dec"] = 12; } #include "BBCParser.moc"