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"