File indexing completed on 2024-05-12 15:31:19
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2009 Bastian Holst <bastianholst@gmx.de> 0004 // 0005 0006 // Self 0007 #include "BBCParser.h" 0008 0009 // Marble 0010 #include "MarbleGlobal.h" 0011 #include "BBCWeatherItem.h" 0012 #include "MarbleDebug.h" 0013 0014 // Qt 0015 #include <QDateTime> 0016 #include <QFile> 0017 #include <QMutexLocker> 0018 #include <QRegExp> 0019 0020 using namespace Marble; 0021 0022 BBCParser::BBCParser( QObject *parent ) : 0023 AbstractWorkerThread( parent ), 0024 m_dayConditions(), 0025 m_nightConditions(), 0026 m_windDirections(), 0027 m_pressureDevelopments(), 0028 m_visibilityStates(), 0029 m_monthNames() 0030 { 0031 m_dayConditions["sunny"] = WeatherData::ClearDay; 0032 m_dayConditions["clear"] = WeatherData::ClearDay; 0033 m_dayConditions["clear sky"] = WeatherData::ClearDay; 0034 m_dayConditions["sunny intervals"] = WeatherData::FewCloudsDay; 0035 m_dayConditions["partly cloudy"] = WeatherData::PartlyCloudyDay; 0036 m_dayConditions["white cloud"] = WeatherData::Overcast; 0037 m_dayConditions["grey cloud"] = WeatherData::Overcast; 0038 m_dayConditions["cloudy"] = WeatherData::Overcast; 0039 m_dayConditions["drizzle"] = WeatherData::LightRain; 0040 m_dayConditions["misty"] = WeatherData::Mist; 0041 m_dayConditions["mist"] = WeatherData::Mist; 0042 m_dayConditions["fog"] = WeatherData::Mist; 0043 m_dayConditions["foggy"] = WeatherData::Mist; 0044 m_dayConditions["dense fog"] = WeatherData::Mist; 0045 m_dayConditions["Thick Fog"] = WeatherData::Mist; 0046 m_dayConditions["tropical storm"] = WeatherData::Thunderstorm; 0047 m_dayConditions["hazy"] = WeatherData::Mist; 0048 m_dayConditions["light shower"] = WeatherData::LightShowersDay; 0049 m_dayConditions["light rain shower"] = WeatherData::LightShowersDay; 0050 m_dayConditions["light showers"] = WeatherData::LightShowersDay; 0051 m_dayConditions["light rain"] = WeatherData::ShowersDay; 0052 m_dayConditions["heavy rain"] = WeatherData::Rain; 0053 m_dayConditions["heavy showers"] = WeatherData::Rain; 0054 m_dayConditions["heavy shower"] = WeatherData::Rain; 0055 m_dayConditions["heavy rain shower"] = WeatherData::Rain; 0056 m_dayConditions["thundery shower"] = WeatherData::Thunderstorm; 0057 m_dayConditions["thunderstorm"] = WeatherData::Thunderstorm; 0058 m_dayConditions["thunder storm"] = WeatherData::Thunderstorm; 0059 m_dayConditions["cloudy with sleet"] = WeatherData::RainSnow; 0060 m_dayConditions["sleet shower"] = WeatherData::RainSnow; 0061 m_dayConditions["sleet showers"] = WeatherData::RainSnow; 0062 m_dayConditions["sleet"] = WeatherData::RainSnow; 0063 m_dayConditions["cloudy with hail"] = WeatherData::Hail; 0064 m_dayConditions["hail shower"] = WeatherData::Hail; 0065 m_dayConditions["hail showers"] = WeatherData::Hail; 0066 m_dayConditions["hail"] = WeatherData::Hail; 0067 m_dayConditions["light snow"] = WeatherData::LightSnow; 0068 m_dayConditions["light snow shower"] = WeatherData::ChanceSnowDay; 0069 m_dayConditions["light snow showers"] = WeatherData::ChanceSnowDay; 0070 m_dayConditions["cloudy with light snow"] = WeatherData::LightSnow; 0071 m_dayConditions["heavy snow"] = WeatherData::Snow; 0072 m_dayConditions["heavy snow shower"] = WeatherData::Snow; 0073 m_dayConditions["heavy snow showers"] = WeatherData::Snow; 0074 m_dayConditions["cloudy with heavy snow"] = WeatherData::Snow; 0075 m_dayConditions["sandstorm"] = WeatherData::SandStorm; 0076 m_dayConditions["na"] = WeatherData::ConditionNotAvailable; 0077 m_dayConditions["N/A"] = WeatherData::ConditionNotAvailable; 0078 0079 m_nightConditions["sunny"] = WeatherData::ClearNight; 0080 m_nightConditions["clear"] = WeatherData::ClearNight; 0081 m_nightConditions["clear sky"] = WeatherData::ClearNight; 0082 m_nightConditions["sunny intervals"] = WeatherData::FewCloudsNight; 0083 m_nightConditions["partly cloudy"] = WeatherData::PartlyCloudyNight; 0084 m_nightConditions["white cloud"] = WeatherData::Overcast; 0085 m_nightConditions["grey cloud"] = WeatherData::Overcast; 0086 m_nightConditions["cloudy"] = WeatherData::Overcast; 0087 m_nightConditions["drizzle"] = WeatherData::LightRain; 0088 m_nightConditions["misty"] = WeatherData::Mist; 0089 m_nightConditions["mist"] = WeatherData::Mist; 0090 m_nightConditions["fog"] = WeatherData::Mist; 0091 m_nightConditions["foggy"] = WeatherData::Mist; 0092 m_nightConditions["dense fog"] = WeatherData::Mist; 0093 m_nightConditions["Thick Fog"] = WeatherData::Mist; 0094 m_nightConditions["tropical storm"] = WeatherData::Thunderstorm; 0095 m_nightConditions["hazy"] = WeatherData::Mist; 0096 m_nightConditions["light shower"] = WeatherData::LightShowersNight; 0097 m_nightConditions["light rain shower"] = WeatherData::LightShowersNight; 0098 m_nightConditions["light showers"] = WeatherData::LightShowersNight; 0099 m_nightConditions["light rain"] = WeatherData::ShowersNight; 0100 m_nightConditions["heavy rain"] = WeatherData::Rain; 0101 m_nightConditions["heavy showers"] = WeatherData::Rain; 0102 m_nightConditions["heavy shower"] = WeatherData::Rain; 0103 m_nightConditions["heavy rain shower"] = WeatherData::Rain; 0104 m_nightConditions["thundery shower"] = WeatherData::Thunderstorm; 0105 m_nightConditions["thunderstorm"] = WeatherData::Thunderstorm; 0106 m_nightConditions["thunder storm"] = WeatherData::Thunderstorm; 0107 m_nightConditions["cloudy with sleet"] = WeatherData::RainSnow; 0108 m_nightConditions["sleet shower"] = WeatherData::RainSnow; 0109 m_nightConditions["sleet showers"] = WeatherData::RainSnow; 0110 m_nightConditions["sleet"] = WeatherData::RainSnow; 0111 m_nightConditions["cloudy with hail"] = WeatherData::Hail; 0112 m_nightConditions["hail shower"] = WeatherData::Hail; 0113 m_nightConditions["hail showers"] = WeatherData::Hail; 0114 m_nightConditions["hail"] = WeatherData::Hail; 0115 m_nightConditions["light snow"] = WeatherData::LightSnow; 0116 m_nightConditions["light snow shower"] = WeatherData::ChanceSnowNight; 0117 m_nightConditions["light snow showers"] = WeatherData::ChanceSnowNight; 0118 m_nightConditions["cloudy with light snow"] = WeatherData::LightSnow; 0119 m_nightConditions["heavy snow"] = WeatherData::Snow; 0120 m_nightConditions["heavy snow shower"] = WeatherData::Snow; 0121 m_nightConditions["heavy snow showers"] = WeatherData::Snow; 0122 m_nightConditions["cloudy with heavy snow"] = WeatherData::Snow; 0123 m_nightConditions["sandstorm"] = WeatherData::SandStorm; 0124 m_nightConditions["na"] = WeatherData::ConditionNotAvailable; 0125 m_nightConditions["N/A"] = WeatherData::ConditionNotAvailable; 0126 0127 m_windDirections["N"] = WeatherData::N; 0128 m_windDirections["NE"] = WeatherData::NE; 0129 m_windDirections["ENE"] = WeatherData::ENE; 0130 m_windDirections["NNE"] = WeatherData::NNE; 0131 m_windDirections["E"] = WeatherData::E; 0132 m_windDirections["SSE"] = WeatherData::SSE; 0133 m_windDirections["SE"] = WeatherData::SE; 0134 m_windDirections["ESE"] = WeatherData::ESE; 0135 m_windDirections["S"] = WeatherData::S; 0136 m_windDirections["NNW"] = WeatherData::NNW; 0137 m_windDirections["NW"] = WeatherData::NW; 0138 m_windDirections["WNW"] = WeatherData::WNW; 0139 m_windDirections["W"] = WeatherData::W; 0140 m_windDirections["SSW"] = WeatherData::SSW; 0141 m_windDirections["SW"] = WeatherData::SW; 0142 m_windDirections["WSW"] = WeatherData::WSW; 0143 m_windDirections["N/A"] = WeatherData::DirectionNotAvailable; 0144 0145 m_pressureDevelopments["falling"] = WeatherData::Falling; 0146 m_pressureDevelopments["no change"] = WeatherData::NoChange; 0147 m_pressureDevelopments["steady"] = WeatherData::NoChange; 0148 m_pressureDevelopments["rising"] = WeatherData::Rising; 0149 m_pressureDevelopments["N/A"] = WeatherData::PressureDevelopmentNotAvailable; 0150 0151 m_visibilityStates["excellent"] = WeatherData::VeryGood; 0152 m_visibilityStates["very good"] = WeatherData::VeryGood; 0153 m_visibilityStates["good"] = WeatherData::Good; 0154 m_visibilityStates["moderate"] = WeatherData::Normal; 0155 m_visibilityStates["poor"] = WeatherData::Poor; 0156 m_visibilityStates["very poor"] = WeatherData::VeryPoor; 0157 m_visibilityStates["fog"] = WeatherData::Fog; 0158 m_visibilityStates["n/a"] = WeatherData::VisibilityNotAvailable; 0159 0160 m_monthNames["Jan"] = 1; 0161 m_monthNames["Feb"] = 2; 0162 m_monthNames["Mar"] = 3; 0163 m_monthNames["Apr"] = 4; 0164 m_monthNames["May"] = 5; 0165 m_monthNames["Jun"] = 6; 0166 m_monthNames["Jul"] = 7; 0167 m_monthNames["Aug"] = 8; 0168 m_monthNames["Sep"] = 9; 0169 m_monthNames["Oct"] = 10; 0170 m_monthNames["Nov"] = 11; 0171 m_monthNames["Dec"] = 12; 0172 } 0173 0174 BBCParser::~BBCParser() 0175 { 0176 } 0177 0178 BBCParser *BBCParser::instance() 0179 { 0180 static BBCParser parser; 0181 return &parser; 0182 } 0183 0184 void BBCParser::scheduleRead( const QString& path, 0185 BBCWeatherItem *item, 0186 const QString& type ) 0187 { 0188 ScheduleEntry entry; 0189 entry.path = path; 0190 entry.item = item; 0191 entry.type = type; 0192 0193 m_scheduleMutex.lock(); 0194 m_schedule.push( entry ); 0195 m_scheduleMutex.unlock(); 0196 0197 ensureRunning(); 0198 } 0199 0200 bool BBCParser::workAvailable() 0201 { 0202 QMutexLocker locker( &m_scheduleMutex ); 0203 return !m_schedule.isEmpty(); 0204 } 0205 0206 void BBCParser::work() 0207 { 0208 m_scheduleMutex.lock(); 0209 ScheduleEntry entry = m_schedule.pop(); 0210 m_scheduleMutex.unlock(); 0211 0212 QFile file( entry.path ); 0213 if( !file.open( QIODevice::ReadOnly | QIODevice::Text ) ) { 0214 return; 0215 } 0216 0217 QList<WeatherData> data = read( &file ); 0218 0219 if( !data.isEmpty() && !entry.item.isNull() ) { 0220 if (entry.type == QLatin1String("bbcobservation")) { 0221 entry.item->setCurrentWeather( data.at( 0 ) ); 0222 } 0223 else if (entry.type == QLatin1String("bbcforecast")) { 0224 entry.item->addForecastWeather( data ); 0225 } 0226 0227 emit parsedFile(); 0228 } 0229 } 0230 0231 QList<WeatherData> BBCParser::read( QIODevice *device ) 0232 { 0233 m_list.clear(); 0234 setDevice( device ); 0235 0236 while ( !atEnd() ) { 0237 readNext(); 0238 0239 if ( isStartElement() ) { 0240 if (name() == QLatin1String("rss")) 0241 readBBC(); 0242 else 0243 raiseError( QObject::tr("The file is not a valid BBC answer.") ); 0244 } 0245 } 0246 0247 return m_list; 0248 } 0249 0250 void BBCParser::readUnknownElement() 0251 { 0252 Q_ASSERT( isStartElement() ); 0253 0254 while ( !atEnd() ) { 0255 readNext(); 0256 0257 if ( isEndElement() ) 0258 break; 0259 0260 if ( isStartElement() ) 0261 readUnknownElement(); 0262 } 0263 } 0264 0265 void BBCParser::readBBC() 0266 { 0267 Q_ASSERT( isStartElement() 0268 && name() == QLatin1String("rss")); 0269 0270 while( !atEnd() ) { 0271 readNext(); 0272 0273 if( isEndElement() ) 0274 break; 0275 0276 if( isStartElement() ) { 0277 if (name() == QLatin1String("channel")) 0278 readChannel(); 0279 else 0280 readUnknownElement(); 0281 } 0282 } 0283 } 0284 0285 void BBCParser::readChannel() 0286 { 0287 Q_ASSERT( isStartElement() 0288 && name() == QLatin1String("channel")); 0289 0290 while( !atEnd() ) { 0291 readNext(); 0292 0293 if( isEndElement() ) 0294 break; 0295 0296 if( isStartElement() ) { 0297 if (name() == QLatin1String("item")) 0298 readItem(); 0299 else 0300 readUnknownElement(); 0301 } 0302 } 0303 } 0304 0305 void BBCParser::readItem() 0306 { 0307 Q_ASSERT( isStartElement() 0308 && name() == QLatin1String("item")); 0309 0310 WeatherData item; 0311 0312 while( !atEnd() ) { 0313 readNext(); 0314 0315 if( isEndElement() ) 0316 break; 0317 0318 if( isStartElement() ) { 0319 if (name() == QLatin1String("description")) 0320 readDescription( &item ); 0321 else if(name() == QLatin1String("title")) 0322 readTitle( &item ); 0323 else if (name() == QLatin1String("pubDate")) 0324 readPubDate( &item ); 0325 else 0326 readUnknownElement(); 0327 } 0328 } 0329 0330 m_list.append( item ); 0331 } 0332 0333 void BBCParser::readDescription( WeatherData *data ) 0334 { 0335 Q_ASSERT( isStartElement() 0336 && name() == QLatin1String("description")); 0337 0338 while( !atEnd() ) { 0339 readNext(); 0340 0341 if( isEndElement() ) 0342 break; 0343 0344 if( isStartElement() ) { 0345 readUnknownElement(); 0346 } 0347 0348 if( isCharacters() ) { 0349 QString description = text().toString(); 0350 QRegExp regExp; 0351 0352 // Temperature 0353 regExp.setPattern( "(Temperature:\\s*)(-?\\d+)(.C)" ); 0354 int pos = regExp.indexIn( description ); 0355 if ( pos > -1 ) { 0356 QString value = regExp.cap( 2 ); 0357 data->setTemperature( value.toDouble(), WeatherData::Celsius ); 0358 } 0359 0360 // Max Temperature 0361 regExp.setPattern( "(Max Temp:\\s*)(-?\\d+)(.C)" ); 0362 pos = regExp.indexIn( description ); 0363 if ( pos > -1 ) { 0364 QString value = regExp.cap( 2 ); 0365 data->setMaxTemperature( value.toDouble(), WeatherData::Celsius ); 0366 } 0367 0368 // Min Temperature 0369 regExp.setPattern( "(Min Temp:\\s*)(-?\\d+)(.C)" ); 0370 pos = regExp.indexIn( description ); 0371 if ( pos > -1 ) { 0372 QString value = regExp.cap( 2 ); 0373 data->setMinTemperature( value.toDouble(), WeatherData::Celsius ); 0374 } 0375 0376 // Wind direction 0377 regExp.setPattern( "(Wind Direction:\\s*)([NESW]+)(,)" ); 0378 pos = regExp.indexIn( description ); 0379 if ( pos > -1 ) { 0380 QString wind = regExp.cap( 2 ); 0381 0382 if ( m_windDirections.contains( wind ) ) { 0383 data->setWindDirection( m_windDirections.value( wind ) ); 0384 } 0385 else { 0386 mDebug() << "UNHANDLED WIND DIRECTION, PLEASE REPORT: " << wind; 0387 } 0388 } 0389 0390 // Wind speed 0391 regExp.setPattern( "(Wind Speed:\\s*)(\\d+)(mph)" ); 0392 pos = regExp.indexIn( description ); 0393 if ( pos > -1 ) { 0394 QString speed = regExp.cap( 2 ); 0395 data->setWindSpeed( speed.toFloat(), WeatherData::mph ); 0396 } 0397 0398 // Relative Humidity 0399 regExp.setPattern( "(Relative Humidity:\\s*)(\\d+)(.,)" ); 0400 pos = regExp.indexIn( description ); 0401 if ( pos > -1 ) { 0402 QString humidity = regExp.cap( 2 ); 0403 data->setHumidity( humidity.toFloat() ); 0404 } 0405 0406 // Pressure 0407 regExp.setPattern( "(Pressure:\\s*)(\\d+mB|N/A)(, )([a-z ]+|N/A)(,)" ); 0408 pos = regExp.indexIn( description ); 0409 if ( pos > -1 ) { 0410 QString pressure = regExp.cap( 2 ); 0411 if (pressure != QLatin1String("N/A")) { 0412 pressure.chop( 2 ); 0413 data->setPressure( pressure.toFloat()/1000, WeatherData::Bar ); 0414 } 0415 0416 QString pressureDevelopment = regExp.cap( 4 ); 0417 0418 if ( m_pressureDevelopments.contains( pressureDevelopment ) ) { 0419 data->setPressureDevelopment( m_pressureDevelopments.value( pressureDevelopment ) ); 0420 } 0421 else { 0422 mDebug() << "UNHANDLED PRESSURE DEVELOPMENT, PLEASE REPORT: " 0423 << pressureDevelopment; 0424 } 0425 } 0426 0427 // Visibility 0428 regExp.setPattern( "(Visibility:\\s*)([^,]+)" ); 0429 pos = regExp.indexIn( description ); 0430 if ( pos > -1 ) { 0431 QString visibility = regExp.cap( 2 ); 0432 0433 if ( m_visibilityStates.contains( visibility.toLower() ) ) { 0434 data->setVisibilty( m_visibilityStates.value( visibility ) ); 0435 } 0436 else { 0437 mDebug() << "UNHANDLED VISIBILITY, PLEASE REPORT: " << visibility; 0438 } 0439 } 0440 } 0441 } 0442 } 0443 0444 void BBCParser::readTitle( WeatherData *data ) 0445 { 0446 Q_ASSERT( isStartElement() 0447 && name() == QLatin1String("title")); 0448 0449 while( !atEnd() ) { 0450 readNext(); 0451 0452 if( isEndElement() ) 0453 break; 0454 0455 if( isStartElement() ) { 0456 readUnknownElement(); 0457 } 0458 0459 if( isCharacters() ) { 0460 QString title = text().toString(); 0461 QRegExp regExp; 0462 0463 // Condition 0464 regExp.setPattern( "(^.*)(:\\s*)([\\w ]+)([\\,\\.]\\s*)" ); 0465 int pos = regExp.indexIn( title ); 0466 if ( pos > -1 ) { 0467 QString value = regExp.cap( 3 ); 0468 0469 if( m_dayConditions.contains( value ) ) { 0470 // TODO: Switch for day/night 0471 data->setCondition( m_dayConditions.value( value ) ); 0472 } 0473 else { 0474 mDebug() << "UNHANDLED BBC WEATHER CONDITION, PLEASE REPORT: " << value; 0475 } 0476 0477 QString dayString = regExp.cap( 1 ); 0478 Qt::DayOfWeek dayOfWeek = (Qt::DayOfWeek) 0; 0479 if (dayString.contains(QLatin1String("Monday"))) { 0480 dayOfWeek = Qt::Monday; 0481 } else if (dayString.contains(QLatin1String("Tuesday"))) { 0482 dayOfWeek = Qt::Tuesday; 0483 } else if (dayString.contains(QLatin1String("Wednesday"))) { 0484 dayOfWeek = Qt::Wednesday; 0485 } else if (dayString.contains(QLatin1String("Thursday"))) { 0486 dayOfWeek = Qt::Thursday; 0487 } else if (dayString.contains(QLatin1String("Friday"))) { 0488 dayOfWeek = Qt::Friday; 0489 } else if (dayString.contains(QLatin1String("Saturday"))) { 0490 dayOfWeek = Qt::Saturday; 0491 } else if (dayString.contains(QLatin1String("Sunday"))) { 0492 dayOfWeek = Qt::Sunday; 0493 } 0494 QDate date = QDate::currentDate(); 0495 date = date.addDays( -1 ); 0496 0497 for ( int i = 0; i < 7; i++ ) { 0498 if ( date.dayOfWeek() == dayOfWeek ) { 0499 data->setDataDate( date ); 0500 } 0501 date = date.addDays( 1 ); 0502 } 0503 } 0504 } 0505 } 0506 } 0507 0508 void BBCParser::readPubDate( WeatherData *data ) 0509 { 0510 Q_ASSERT( isStartElement() 0511 && name() == QLatin1String("pubDate")); 0512 0513 while( !atEnd() ) { 0514 readNext(); 0515 0516 if( isEndElement() ) 0517 break; 0518 0519 if( isStartElement() ) { 0520 readUnknownElement(); 0521 } 0522 0523 if( isCharacters() ) { 0524 QString pubDate = text().toString(); 0525 QRegExp regExp; 0526 0527 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})" ); 0528 int pos = regExp.indexIn( pubDate ); 0529 if ( pos > -1 ) { 0530 QDateTime dateTime; 0531 QDate date; 0532 QTime time; 0533 0534 dateTime.setTimeSpec( Qt::UTC ); 0535 date.setDate( regExp.cap( 6 ).toInt(), 0536 m_monthNames.value( regExp.cap( 4 ) ), 0537 regExp.cap( 2 ).toInt() ); 0538 time.setHMS( regExp.cap( 8 ).toInt(), 0539 regExp.cap( 10 ).toInt(), 0540 regExp.cap( 12 ).toInt() ); 0541 0542 dateTime.setDate( date ); 0543 dateTime.setTime( time ); 0544 0545 // Timezone 0546 if (regExp.cap(14) == QLatin1String("-")) { 0547 dateTime = dateTime.addSecs( 60*60*regExp.cap( 15 ).toInt() ); 0548 dateTime = dateTime.addSecs( 60 *regExp.cap( 16 ).toInt() ); 0549 } 0550 else { 0551 dateTime = dateTime.addSecs( -60*60*regExp.cap( 15 ).toInt() ); 0552 dateTime = dateTime.addSecs( -60 *regExp.cap( 16 ).toInt() ); 0553 } 0554 0555 data->setPublishingTime( dateTime ); 0556 } 0557 } 0558 } 0559 } 0560 0561 #include "moc_BBCParser.cpp"