File indexing completed on 2024-05-12 15:31:20
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2011 Valery Kharitonov <kharvd@gmail.com> 0004 // 0005 0006 #include "GeoNamesWeatherService.h" 0007 0008 #include "GeoNamesWeatherItem.h" 0009 #include "GeoDataLatLonAltBox.h" 0010 #include "MarbleModel.h" 0011 #include "MarbleDebug.h" 0012 0013 #include <QUrl> 0014 #include <QDateTime> 0015 #include <QJsonDocument> 0016 #include <QJsonArray> 0017 #include <QJsonObject> 0018 0019 #include <QUrlQuery> 0020 0021 using namespace Marble; 0022 0023 QHash<QString, WeatherData::WeatherCondition> GeoNamesWeatherService::dayConditions 0024 = QHash<QString, WeatherData::WeatherCondition>(); 0025 QVector<WeatherData::WindDirection> GeoNamesWeatherService::windDirections 0026 = QVector<WeatherData::WindDirection>(16); 0027 0028 GeoNamesWeatherService::GeoNamesWeatherService( const MarbleModel *model, QObject *parent ) : 0029 AbstractWeatherService( model, parent ) 0030 { 0031 GeoNamesWeatherService::setupHashes(); 0032 } 0033 0034 GeoNamesWeatherService::~GeoNamesWeatherService() 0035 { 0036 } 0037 0038 void GeoNamesWeatherService::getAdditionalItems( const GeoDataLatLonAltBox& box, 0039 qint32 number ) 0040 { 0041 if (marbleModel()->planetId() != QLatin1String("earth")) { 0042 return; 0043 } 0044 0045 QUrl geonamesUrl( "http://api.geonames.org/weatherJSON" ); 0046 QUrlQuery urlQuery; 0047 urlQuery.addQueryItem( "north", QString::number( box.north( GeoDataCoordinates::Degree ) ) ); 0048 urlQuery.addQueryItem( "south", QString::number( box.south( GeoDataCoordinates::Degree ) ) ); 0049 urlQuery.addQueryItem( "east", QString::number( box.east( GeoDataCoordinates::Degree ) ) ); 0050 urlQuery.addQueryItem( "west", QString::number( box.west( GeoDataCoordinates::Degree ) ) ); 0051 urlQuery.addQueryItem( "maxRows", QString::number( number ) ); 0052 urlQuery.addQueryItem( "username", "marble" ); 0053 geonamesUrl.setQuery( urlQuery ); 0054 0055 emit downloadDescriptionFileRequested( geonamesUrl ); 0056 } 0057 0058 void GeoNamesWeatherService::getItem( const QString &id ) 0059 { 0060 if (marbleModel()->planetId() != QLatin1String("earth")) { 0061 return; 0062 } 0063 0064 if ( id.startsWith(QLatin1String("geonames_") ) ) { 0065 QUrl geonamesUrl( "http://api.geonames.org/weatherIcaoJSON" ); 0066 QUrlQuery urlQuery; 0067 urlQuery.addQueryItem( "ICAO", id.mid( 9 ) ); 0068 urlQuery.addQueryItem( "username", "marble" ); 0069 geonamesUrl.setQuery( urlQuery ); 0070 emit downloadDescriptionFileRequested( geonamesUrl ); 0071 } 0072 } 0073 0074 void GeoNamesWeatherService::parseFile( const QByteArray& file ) 0075 { 0076 QJsonDocument jsonDoc = QJsonDocument::fromJson(file); 0077 QJsonValue weatherObservationsValue = jsonDoc.object().value(QStringLiteral("weatherObservations")); 0078 0079 // Parse if any result exists 0080 QList<AbstractDataPluginItem*> items; 0081 if (weatherObservationsValue.isArray()) { 0082 // Add items to the list 0083 QJsonArray weatherObservationsArray = weatherObservationsValue.toArray(); 0084 for (int index = 0; index < weatherObservationsArray.size(); ++index) { 0085 QJsonObject weatherObservationObject = weatherObservationsArray[index].toObject(); 0086 0087 AbstractDataPluginItem* item = parse(weatherObservationObject); 0088 if ( item ) { 0089 items << item; 0090 } 0091 } 0092 } else { 0093 QJsonValue weatherObservationValue = jsonDoc.object().value(QStringLiteral("weatherObservation")); 0094 QJsonObject weatherObservationObject = weatherObservationValue.toObject(); 0095 AbstractDataPluginItem* item = parse(weatherObservationObject); 0096 if ( item ) { 0097 items << item; 0098 } 0099 } 0100 0101 emit createdItems( items ); 0102 } 0103 0104 AbstractDataPluginItem *GeoNamesWeatherService::parse(const QJsonObject &weatherObservationObject) 0105 { 0106 const QString condition = weatherObservationObject.value(QStringLiteral("weatherCondition")).toString(); 0107 const QString clouds = weatherObservationObject.value(QStringLiteral("clouds")).toString(); 0108 const int windDirection = weatherObservationObject.value(QStringLiteral("windDirection")).toInt(); 0109 QString id = weatherObservationObject.value(QStringLiteral("ICAO")).toString(); 0110 const double temperature = weatherObservationObject.value(QStringLiteral("temperature")).toString().toDouble(); //delivered as string 0111 const int windSpeed = weatherObservationObject.value(QStringLiteral("windSpeed")).toString().toInt(); //delivered as string 0112 const int humidity = weatherObservationObject.value(QStringLiteral("humidity")).toInt(); 0113 const double pressure = weatherObservationObject.value(QStringLiteral("seaLevelPressure")).toDouble(); 0114 const QString name = weatherObservationObject.value(QStringLiteral("stationName")).toString(); 0115 const QDateTime date = QDateTime::fromString( 0116 weatherObservationObject.value(QStringLiteral("datetime")).toString(), "yyyy-MM-dd hh:mm:ss" ); 0117 const double longitude = weatherObservationObject.value(QStringLiteral("lng")).toDouble(); 0118 const double latitude = weatherObservationObject.value(QStringLiteral("lat")).toDouble(); 0119 0120 if ( !id.isEmpty() ) { 0121 WeatherData data; 0122 0123 // Weather condition 0124 if (clouds != QLatin1String("n/a") && condition != QLatin1String("n/a")) { 0125 if ( dayConditions.contains( condition ) ) { 0126 data.setCondition( dayConditions[condition] ); 0127 } else { 0128 mDebug() << "UNHANDLED GEONAMES WEATHER CONDITION, PLEASE REPORT: " << condition; 0129 } 0130 } else { 0131 if ( dayConditions.contains( clouds ) ) { 0132 data.setCondition( dayConditions[clouds] ); 0133 } else { 0134 mDebug() << "UNHANDLED GEONAMES CLOUDS CONDITION, PLEASE REPORT: " << clouds; 0135 } 0136 } 0137 0138 // Wind direction. Finds the closest direction from windDirections array. 0139 if ( windDirection >= 0 ) { 0140 double tickSpacing = 360.0 / windDirections.size(); 0141 data.setWindDirection( windDirections[int(( windDirection / tickSpacing ) + 0.5) 0142 % windDirections.size()] ); 0143 } 0144 0145 // Wind speed 0146 if ( windSpeed != 0 ) { 0147 data.setWindSpeed( windSpeed, WeatherData::knots ); 0148 } 0149 0150 // Temperature 0151 data.setTemperature( temperature, WeatherData::Celsius ); 0152 0153 // Humidity 0154 data.setHumidity( humidity ); 0155 0156 // Pressure 0157 if ( pressure != 0.0 ) { 0158 data.setPressure( pressure, WeatherData::HectoPascal ); 0159 } 0160 0161 // Date 0162 data.setDataDate( date.date() ); 0163 data.setPublishingTime( date ); 0164 0165 // ID 0166 id = QLatin1String("geonames_") + id; 0167 0168 GeoDataCoordinates coordinates( longitude, latitude, 0.0, GeoDataCoordinates::Degree ); 0169 GeoNamesWeatherItem *item = new GeoNamesWeatherItem( this ); 0170 item->setMarbleWidget( marbleWidget() ); 0171 item->setId( id ); 0172 item->setCoordinate( coordinates ); 0173 item->setPriority( 0 ); 0174 item->setStationName( name ); 0175 item->setCurrentWeather( data ); 0176 return item; 0177 } else { 0178 return nullptr; 0179 } 0180 } 0181 0182 void GeoNamesWeatherService::setupHashes() 0183 { 0184 if( !( ( dayConditions.isEmpty() ) 0185 || ( windDirections.isEmpty() ) ) ) 0186 { 0187 return; 0188 } 0189 0190 dayConditions["sunny"] = WeatherData::ClearDay; 0191 dayConditions["clear"] = WeatherData::ClearDay; 0192 dayConditions["clear sky"] = WeatherData::ClearDay; 0193 dayConditions["sunny intervals"] = WeatherData::FewCloudsDay; 0194 dayConditions["few clouds"] = WeatherData::FewCloudsDay; 0195 dayConditions["scattered clouds"] = WeatherData::FewCloudsDay; 0196 dayConditions["partly cloudy"] = WeatherData::PartlyCloudyDay; 0197 dayConditions["broken clouds"] = WeatherData::PartlyCloudyDay; 0198 dayConditions["white cloud"] = WeatherData::Overcast; 0199 dayConditions["overcast"] = WeatherData::Overcast; 0200 dayConditions["grey cloud"] = WeatherData::Overcast; 0201 dayConditions["cloudy"] = WeatherData::Overcast; 0202 dayConditions["drizzle"] = WeatherData::LightRain; 0203 dayConditions["light drizzle"] = WeatherData::LightRain; 0204 dayConditions["misty"] = WeatherData::Mist; 0205 dayConditions["mist"] = WeatherData::Mist; 0206 dayConditions["fog"] = WeatherData::Mist; 0207 dayConditions["foggy"] = WeatherData::Mist; 0208 dayConditions["dense fog"] = WeatherData::Mist; 0209 dayConditions["Thick Fog"] = WeatherData::Mist; 0210 dayConditions["tropical storm"] = WeatherData::Thunderstorm; 0211 dayConditions["thunderstorm"] = WeatherData::Thunderstorm; 0212 dayConditions["hazy"] = WeatherData::Mist; 0213 dayConditions["haze"] = WeatherData::Mist; 0214 dayConditions["in vicinity: showers "] = WeatherData::ShowersDay; 0215 dayConditions["light shower"] = WeatherData::LightShowersDay; 0216 dayConditions["light rain shower"] = WeatherData::LightShowersDay; 0217 dayConditions["light showers"] = WeatherData::LightShowersDay; 0218 dayConditions["light rain"] = WeatherData::ShowersDay; 0219 dayConditions["heavy rain"] = WeatherData::Rain; 0220 dayConditions["heavy showers"] = WeatherData::Rain; 0221 dayConditions["heavy shower"] = WeatherData::Rain; 0222 dayConditions["heavy rain shower"] = WeatherData::Rain; 0223 dayConditions["thundery shower"] = WeatherData::Thunderstorm; 0224 dayConditions["thunderstorm"] = WeatherData::Thunderstorm; 0225 dayConditions["thunder storm"] = WeatherData::Thunderstorm; 0226 dayConditions["cloudy with sleet"] = WeatherData::RainSnow; 0227 dayConditions["sleet shower"] = WeatherData::RainSnow; 0228 dayConditions["sleet showers"] = WeatherData::RainSnow; 0229 dayConditions["sleet"] = WeatherData::RainSnow; 0230 dayConditions["cloudy with hail"] = WeatherData::Hail; 0231 dayConditions["hail shower"] = WeatherData::Hail; 0232 dayConditions["hail showers"] = WeatherData::Hail; 0233 dayConditions["hail"] = WeatherData::Hail; 0234 dayConditions["light snow"] = WeatherData::LightSnow; 0235 dayConditions["light snow shower"] = WeatherData::ChanceSnowDay; 0236 dayConditions["light snow showers"] = WeatherData::ChanceSnowDay; 0237 dayConditions["cloudy with light snow"] = WeatherData::LightSnow; 0238 dayConditions["heavy snow"] = WeatherData::Snow; 0239 dayConditions["heavy snow shower"] = WeatherData::Snow; 0240 dayConditions["heavy snow showers"] = WeatherData::Snow; 0241 dayConditions["cloudy with heavy snow"] = WeatherData::Snow; 0242 dayConditions["sandstorm"] = WeatherData::SandStorm; 0243 dayConditions["na"] = WeatherData::ConditionNotAvailable; 0244 dayConditions["n/a"] = WeatherData::ConditionNotAvailable; 0245 0246 windDirections[0] = WeatherData::N; 0247 windDirections[1] = WeatherData::NNE; 0248 windDirections[2] = WeatherData::NE; 0249 windDirections[3] = WeatherData::ENE; 0250 windDirections[4] = WeatherData::E; 0251 windDirections[5] = WeatherData::ESE; 0252 windDirections[6] = WeatherData::SE; 0253 windDirections[7] = WeatherData::SSE; 0254 windDirections[8] = WeatherData::S; 0255 windDirections[9] = WeatherData::SSW; 0256 windDirections[10] = WeatherData::SW; 0257 windDirections[11] = WeatherData::WSW; 0258 windDirections[12] = WeatherData::W; 0259 windDirections[13] = WeatherData::WNW; 0260 windDirections[14] = WeatherData::NW; 0261 windDirections[15] = WeatherData::NNW; 0262 } 0263 0264 #include "moc_GeoNamesWeatherService.cpp"