File indexing completed on 2024-05-12 15:31:20

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 "WeatherData.h"
0008 
0009 // Marble
0010 #include "MarbleGlobal.h"
0011 #include "MarbleDirs.h"
0012 #include "MarbleDebug.h"
0013 
0014 // Qt
0015 #include <QAtomicInt>
0016 #include <QDate>
0017 #include <QDateTime>
0018 #include <QHash>
0019 #include <QLocale>
0020 #include <QImage>
0021 
0022 #include <cmath>
0023 
0024 namespace Marble
0025 {
0026     
0027 // Factors
0028 // m/s to knots
0029 const qreal MPS2KN = 1.9437;
0030 const qreal KN2MPS = 1 / MPS2KN;
0031 // m/s to kilometer per hour
0032 const qreal MPS2KPH = 3.6;
0033 const qreal KPH2MPS = 1 / MPS2KPH;
0034 // m/s to miles per hour
0035 const qreal MPS2MPH = MPS2KPH * KM2MI;
0036 const qreal MPH2MPS = 1 / MPS2MPH;
0037 // HectoPascal to KiloPascal
0038 const qreal HPA2KPA = 10;
0039 const qreal KPA2HPA = 1/HPA2KPA;
0040 // Bar to HectoPascal
0041 const qreal BAR2HPA = 1000;
0042 const qreal HPA2BAR = 1/BAR2HPA;
0043 // mmHg to HectoPascal
0044 const qreal HG2HPA = 1.33;
0045 const qreal HPA2HG = 1/HG2HPA;
0046 // inchHg to HectoPascal
0047 const qreal IHG2HPA = HG2HPA * 25.4;
0048 const qreal HPA2IHG = HPA2HG / 25.4;
0049 
0050 // Summands
0051 // Kelvin to degree Celsius
0052 const qreal KEL2CEL = -273.15;
0053 const qreal CEL2KEL = -KEL2CEL;
0054 
0055 class WeatherDataPrivate
0056 {
0057     Q_DECLARE_TR_FUNCTIONS ( WeatherDataPrivate )
0058 
0059  public:
0060     WeatherDataPrivate()
0061         : m_pubTime(),
0062           m_dataDate(),
0063           m_condition( WeatherData::ConditionNotAvailable ),
0064           m_windDirection( WeatherData::DirectionNotAvailable ),
0065           m_windSpeed( -1.0 ),
0066           m_temperature( -1.0 ),
0067           m_maxTemperature( -1.0 ),
0068           m_minTemperature( -1.0 ),
0069           m_visibility( WeatherData::VisibilityNotAvailable ),
0070           m_pressure( -1.0 ),
0071           m_pressureDevelopment( WeatherData::PressureDevelopmentNotAvailable ),
0072           m_humidity( -1.0 ),
0073           ref( 1 )
0074     {
0075         initializeIcons();
0076     }
0077     
0078     WeatherDataPrivate( const WeatherDataPrivate &other )
0079         : m_pubTime( other.m_pubTime ),
0080           m_dataDate( other.m_dataDate ),
0081           m_condition( other.m_condition ),
0082           m_windDirection( other.m_windDirection ),
0083           m_windSpeed( other.m_windSpeed ),
0084           m_temperature( other.m_temperature ),
0085           m_maxTemperature( other.m_maxTemperature ),
0086           m_minTemperature( other.m_minTemperature ),
0087           m_visibility( other.m_visibility ),
0088           m_pressure( other.m_pressure ),
0089           m_pressureDevelopment( other.m_pressureDevelopment ),
0090           m_humidity( other.m_humidity ),
0091           ref( other.ref )
0092     {
0093         initializeIcons();
0094     }
0095     
0096     static void initializeIcons()
0097     {
0098         if( s_iconPath.size() == 0 ) {
0099             // Clouds
0100             s_iconPath.insert( WeatherData::ConditionNotAvailable,
0101                             MarbleDirs::path(QStringLiteral("weather/weather-none-available.png")));
0102             s_iconPath.insert( WeatherData::ClearDay,
0103                             MarbleDirs::path(QStringLiteral("weather/weather-clear.png")));
0104             s_iconPath.insert( WeatherData::ClearNight,
0105                             MarbleDirs::path(QStringLiteral("weather/weather-clear-night.png")));
0106             s_iconPath.insert( WeatherData::FewCloudsDay,
0107                             MarbleDirs::path(QStringLiteral("weather/weather-few-clouds.png")));
0108             s_iconPath.insert( WeatherData::FewCloudsNight,
0109                             MarbleDirs::path(QStringLiteral("weather/weather-few-clouds-night.png")));
0110             s_iconPath.insert( WeatherData::PartlyCloudyDay,
0111                             MarbleDirs::path(QStringLiteral("weather/weather-clouds.png")));
0112             s_iconPath.insert( WeatherData::PartlyCloudyNight,
0113                             MarbleDirs::path(QStringLiteral("weather/weather-clouds-night.png")));
0114             s_iconPath.insert( WeatherData::Overcast,
0115                             MarbleDirs::path(QStringLiteral("weather/weather-many-clouds.png")));
0116 
0117             // Rain
0118             s_iconPath.insert( WeatherData::LightShowersDay,
0119                             MarbleDirs::path(QStringLiteral("weather/weather-showers-scattered-day.png")));
0120             s_iconPath.insert( WeatherData::LightShowersNight,
0121                             MarbleDirs::path(QStringLiteral("weather/weather-showers-scattered-night.png")));
0122             s_iconPath.insert( WeatherData::ShowersDay,
0123                             MarbleDirs::path(QStringLiteral("weather/weather-showers-day.png")));
0124             s_iconPath.insert( WeatherData::ShowersNight,
0125                             MarbleDirs::path(QStringLiteral("weather/weather-showers-night.png")));
0126             s_iconPath.insert( WeatherData::LightRain,
0127                             MarbleDirs::path(QStringLiteral("weather/weather-showers-scattered.png")));
0128             s_iconPath.insert( WeatherData::Rain,
0129                             MarbleDirs::path(QStringLiteral("weather/weather-showers.png")));
0130 
0131             // Special
0132             s_iconPath.insert( WeatherData::ChanceThunderstormDay,
0133                             MarbleDirs::path(QStringLiteral("weather/weather-storm-day.png")));
0134             s_iconPath.insert( WeatherData::ChanceThunderstormNight,
0135                             MarbleDirs::path(QStringLiteral("weather/weather-storm-night.png")));
0136             s_iconPath.insert( WeatherData::Thunderstorm,
0137                             MarbleDirs::path(QStringLiteral("weather/weather-storm.png")));
0138             s_iconPath.insert( WeatherData::Hail,
0139                             MarbleDirs::path(QStringLiteral("weather/weather-hail.png")));
0140             s_iconPath.insert( WeatherData::ChanceSnowDay,
0141                             MarbleDirs::path(QStringLiteral("weather/weather-snow-scattered-day.png")));
0142             s_iconPath.insert( WeatherData::ChanceSnowNight,
0143                             MarbleDirs::path(QStringLiteral("weather/weather-snow-scattered-night.png")));
0144             s_iconPath.insert( WeatherData::LightSnow,
0145                             MarbleDirs::path(QStringLiteral("weather/weather-snow-scattered.png")));
0146             s_iconPath.insert( WeatherData::Snow,
0147                             MarbleDirs::path(QStringLiteral("weather/weather-snow.png")));
0148             s_iconPath.insert( WeatherData::RainSnow,
0149                             MarbleDirs::path(QStringLiteral("weather/weather-snow-rain.png")));
0150             s_iconPath.insert( WeatherData::Mist,
0151                             MarbleDirs::path(QStringLiteral("weather/weather-mist.png")));
0152             s_iconPath.insert( WeatherData::SandStorm,
0153                             MarbleDirs::path(QStringLiteral("weather/weather-none-available.png")));
0154         }
0155     }
0156     
0157     static qreal fromKelvin( qreal temp, WeatherData::TemperatureUnit format )
0158     {
0159         if( WeatherData::Kelvin == format ) {
0160             return temp;
0161         }
0162         else if ( WeatherData::Celsius == format ) {
0163             return temp + KEL2CEL;
0164         }
0165         else if ( WeatherData::Fahrenheit == format ) {
0166             return ( temp * 1.8 ) - 459.67;
0167         }
0168         else {
0169             mDebug() << "Wrong temperature format";
0170             return 0;
0171         }
0172     }
0173     
0174     static qreal toKelvin( qreal temp, WeatherData::TemperatureUnit format )
0175     {
0176         if( WeatherData::Kelvin == format ) {
0177             return temp;
0178         }
0179         else if ( WeatherData::Celsius == format ) {
0180             return temp + CEL2KEL;
0181         }
0182         else if ( WeatherData::Fahrenheit == format ) {
0183             return ( temp + 459.67 ) / 1.8;
0184         }
0185         else {
0186             mDebug() << "Wrong temperature format";
0187             return 0;
0188         }
0189     }
0190 
0191     static bool isPositiveValue( qreal value )
0192     {
0193         // A small tolerance
0194         return value > -0.5;
0195     }
0196     
0197     static QString generateTemperatureString( qreal temp, WeatherData::TemperatureUnit format )
0198     {
0199         QLocale locale = QLocale::system();
0200         // We round to integer.
0201         QString string = locale.toString( floor( fromKelvin( temp, format ) + 0.5 ) );
0202         switch ( format ) {
0203             case WeatherData::Kelvin:
0204                 string += QLatin1String(" K");
0205                 break;
0206             case WeatherData::Celsius:
0207                 string += QString::fromUtf8("°C");
0208                 break;
0209             case WeatherData::Fahrenheit:
0210                 string += QString::fromUtf8("°F");
0211                 break;
0212         }
0213         return string;
0214     }
0215             
0216     
0217     WeatherDataPrivate& operator=( const WeatherDataPrivate &other )
0218     {
0219         m_pubTime = other.m_pubTime;
0220         m_dataDate = other.m_dataDate;
0221         m_condition = other.m_condition;
0222         m_windDirection = other.m_windDirection;
0223         m_windSpeed = other.m_windSpeed;
0224         m_temperature = other.m_temperature;
0225         m_maxTemperature = other.m_maxTemperature;
0226         m_minTemperature = other.m_minTemperature;
0227         m_visibility = other.m_visibility;
0228         m_pressure = other.m_pressure;
0229         m_pressureDevelopment = other.m_pressureDevelopment;
0230         m_humidity = other.m_humidity;
0231         
0232         ref = other.ref;
0233         return *this;
0234     }
0235 
0236     QDateTime m_pubTime;
0237     QDate m_dataDate;
0238     WeatherData::WeatherCondition m_condition;
0239     WeatherData::WindDirection m_windDirection;
0240     
0241     // Wind speed stored in m/s
0242     qreal m_windSpeed;
0243     
0244     // Temperatures stored in Kelvin
0245     qreal m_temperature;
0246     qreal m_maxTemperature;
0247     qreal m_minTemperature;
0248     
0249     WeatherData::Visibility m_visibility;
0250     
0251     // Pressure stored in hecto pascal
0252     qreal m_pressure;
0253 
0254     WeatherData::PressureDevelopment m_pressureDevelopment;
0255     
0256     // Relative humidity
0257     qreal m_humidity;
0258     
0259     QAtomicInt ref;
0260     
0261     static QHash<WeatherData::WeatherCondition, QImage> s_icons;
0262     static QHash<WeatherData::WeatherCondition, QString> s_iconPath;
0263     static WeatherData::TemperatureUnit s_standardTemperatureUnit;
0264 };
0265 
0266 QHash<WeatherData::WeatherCondition, QImage> WeatherDataPrivate::s_icons = QHash<WeatherData::WeatherCondition, QImage>();
0267 QHash<WeatherData::WeatherCondition, QString> WeatherDataPrivate::s_iconPath = QHash<WeatherData::WeatherCondition, QString>();
0268 WeatherData::TemperatureUnit WeatherDataPrivate::s_standardTemperatureUnit = WeatherData::Celsius;
0269 
0270 WeatherData::WeatherData()
0271     : d( new WeatherDataPrivate() )
0272 {    
0273 }
0274 
0275 WeatherData::WeatherData( const WeatherData &other )
0276     : d( other.d )
0277 {
0278     d->ref.ref();
0279 }
0280 
0281 WeatherData::~WeatherData()
0282 {
0283     if ( !d->ref.deref() )
0284         delete d;
0285 }
0286 
0287 bool WeatherData::isValid() const
0288 {
0289     return hasValidPublishingTime()
0290            || hasValidDataDate()
0291            || hasValidCondition()
0292            || hasValidWindDirection()
0293            || hasValidWindSpeed()
0294            || hasValidTemperature()
0295            || hasValidMaxTemperature()
0296            || hasValidMinTemperature()
0297            || hasValidVisibility()
0298            || hasValidPressure()
0299            || hasValidPressureDevelopment()
0300            || hasValidHumidity();
0301 }
0302 
0303 QDateTime WeatherData::publishingTime() const
0304 {
0305     return d->m_pubTime;
0306 }
0307 
0308 void WeatherData::setPublishingTime( const QDateTime& dateTime )
0309 {
0310     detach();
0311     d->m_pubTime = dateTime.toUTC();
0312 }
0313 
0314 bool WeatherData::hasValidPublishingTime() const
0315 {
0316     return d->m_pubTime.isValid();
0317 }
0318 
0319 QDate WeatherData::dataDate() const
0320 {
0321     return d->m_dataDate;
0322 }
0323 
0324 void WeatherData::setDataDate( const QDate& date )
0325 {
0326     detach();
0327     d->m_dataDate = date;
0328 }
0329 
0330 bool WeatherData::hasValidDataDate() const
0331 {
0332     return d->m_dataDate.isValid();
0333 }
0334 
0335 WeatherData::WeatherCondition WeatherData::condition() const
0336 {
0337     return d->m_condition;
0338 }
0339 
0340 void WeatherData::setCondition( WeatherData::WeatherCondition condition )
0341 {
0342     detach();
0343     d->m_condition = condition;
0344 }
0345 
0346 bool WeatherData::hasValidCondition() const
0347 {
0348     return d->m_condition != WeatherData::ConditionNotAvailable;
0349 }
0350 
0351 QString WeatherData::conditionString() const
0352 {
0353     switch ( condition() ) {
0354         case ClearDay:
0355             return tr( "sunny" );
0356         case ClearNight:
0357             return tr( "clear" );
0358         case FewCloudsDay:
0359         case FewCloudsNight:
0360             return tr( "few clouds" );
0361         case PartlyCloudyDay:
0362         case PartlyCloudyNight:
0363             return tr( "partly cloudy" );
0364         case Overcast:
0365             return tr( "overcast" );
0366         case LightShowersDay:
0367         case LightShowersNight:
0368             return tr( "light showers" );
0369         case ShowersDay:
0370         case ShowersNight:
0371             return tr( "showers" );
0372         case LightRain:
0373             return tr( "light rain" );
0374         case Rain:
0375             return tr( "rain" );
0376         case ChanceThunderstormDay:
0377         case ChanceThunderstormNight:
0378             return tr( "occasionally thunderstorm" );
0379         case Thunderstorm:
0380             return tr( "thunderstorm" );
0381         case Hail:
0382             return tr( "hail" );
0383         case ChanceSnowDay:
0384         case ChanceSnowNight:
0385             return tr( "occasionally snow" );
0386         case LightSnow:
0387             return tr( "light snow" );
0388         case Snow:
0389             return tr( "snow" );
0390         case RainSnow:
0391             return tr( "rain and snow" );
0392         case Mist:
0393             return tr( "mist" );
0394         case SandStorm:
0395             return tr( "sandstorm" );
0396         default:
0397             return "Condition not available";
0398     }
0399 }
0400 
0401 QImage WeatherData::icon() const
0402 {
0403     QImage icon = WeatherDataPrivate::s_icons.value( condition() );
0404     
0405     // If the icon is in the hash, simply return it.
0406     if ( !icon.isNull() ) {
0407         return icon;
0408     }
0409     // If it isn't in the hash, the icon will be created (from the value stored in s_iconPath).
0410     else {
0411         icon = QImage( WeatherDataPrivate::s_iconPath.value( condition() ) );
0412         WeatherDataPrivate::s_icons.insert( condition(), icon );
0413         return icon;
0414     }
0415 }
0416 
0417 QString WeatherData::iconSource() const
0418 {
0419     QString const invalid = MarbleDirs::path(QStringLiteral("weather/weather-none-available.png"));
0420     QString const icon = WeatherDataPrivate::s_iconPath.value( condition() );
0421     return icon == invalid ? "" : icon;
0422 }
0423 
0424 WeatherData::WindDirection WeatherData::windDirection() const
0425 {
0426     return d->m_windDirection;
0427 }
0428 
0429 void WeatherData::setWindDirection( WeatherData::WindDirection direction )
0430 {
0431     detach();
0432     d->m_windDirection = direction;
0433 }
0434 
0435 bool WeatherData::hasValidWindDirection() const
0436 {
0437     return d->m_windDirection != WeatherData::DirectionNotAvailable;
0438 }
0439 
0440 QString WeatherData::windDirectionString() const
0441 {
0442     switch ( windDirection() ) {
0443         case N:
0444             return tr( "N" );
0445         case NNE:
0446             return tr( "NNE" );
0447         case NE:
0448             return tr( "NE" );
0449         case ENE:
0450             return tr( "ENE" );
0451         case E:
0452             return tr( "E" );
0453         case SSE:
0454             return tr( "SSE" );
0455         case SE:
0456             return tr( "SE" );
0457         case ESE:
0458             return tr( "ESE" );
0459         case S:
0460             return tr( "S" );
0461         case NNW:
0462             return tr( "NNW" );
0463         case NW:
0464             return tr( "NW" );
0465         case WNW:
0466             return tr( "WNW" );
0467         case W:
0468             return tr( "W" );
0469         case SSW:
0470             return tr( "SSW" );
0471         case SW:
0472             return tr( "SW" );
0473         case WSW:
0474             return tr( "WSW" );
0475         default:
0476             return "";
0477     }
0478 }
0479 
0480 qreal WeatherData::windSpeed( WeatherData::SpeedUnit format ) const
0481 {
0482     if ( WeatherData::mps == format ) {
0483         return d->m_windSpeed;
0484     }
0485     if ( WeatherData::kph == format ) {
0486         return d->m_windSpeed * MPS2KPH;
0487     }
0488     else if ( WeatherData::mph == format ) {
0489         return d->m_windSpeed * MPS2MPH;
0490     }
0491     else if ( WeatherData::knots == format ) {
0492         return d->m_windSpeed * MPS2KN;
0493     }
0494     else if ( WeatherData::beaufort == format ) {
0495         if( d->m_windSpeed < 0.3 )
0496             return 0;
0497         else if( d->m_windSpeed < 1.6 )
0498             return 1;
0499         else if( d->m_windSpeed < 3.4 )
0500             return 2;
0501         else if( d->m_windSpeed < 5.5 )
0502             return 3;
0503         else if( d->m_windSpeed < 8.0 )
0504             return 4;
0505         else if( d->m_windSpeed < 10.8 )
0506             return 5;
0507         else if( d->m_windSpeed < 13.9 )
0508             return 6;
0509         else if( d->m_windSpeed < 17.2 )
0510             return 7;
0511         else if( d->m_windSpeed < 20.8 )
0512             return 8;
0513         else if( d->m_windSpeed < 24.5 )
0514             return 9;
0515         else if( d->m_windSpeed < 28.5 )
0516             return 10;
0517         else if( d->m_windSpeed < 32.7 )
0518             return 11;
0519         else 
0520             return 12;
0521     }
0522     else {
0523         mDebug() << "Wrong speed format";
0524         return 0;
0525     }
0526 }
0527 
0528 void WeatherData::setWindSpeed( qreal speed, WeatherData::SpeedUnit format )
0529 {
0530     detach();
0531     if ( WeatherData::mps == format ) {
0532         d->m_windSpeed = speed;
0533     }
0534     if ( WeatherData::kph == format ) {
0535         d->m_windSpeed = speed * KPH2MPS;
0536     }
0537     else if ( WeatherData::mph == format ) {
0538         d->m_windSpeed = speed * MPH2MPS;
0539     }
0540     else if ( WeatherData::knots == format ) {
0541         d->m_windSpeed = speed * KN2MPS;
0542     }
0543     else if ( WeatherData::beaufort == format ) {
0544         int rounded = (int) speed;
0545         if( 0 == rounded )
0546             d->m_windSpeed = 0.15;
0547         else if( 1 == rounded )
0548             d->m_windSpeed = 0.95;
0549         else if( 2 == rounded )
0550             d->m_windSpeed = 2.5;
0551         else if( 3 == rounded )
0552             d->m_windSpeed = 4.45;
0553         else if( 4 == rounded )
0554             d->m_windSpeed = 6.75;
0555         else if( 5 == rounded )
0556             d->m_windSpeed = 9.4;
0557         else if( 6 == rounded )
0558             d->m_windSpeed = 12.35;
0559         else if( 7 == rounded )
0560             d->m_windSpeed = 15.55;
0561         else if( 8 == rounded )
0562             d->m_windSpeed = 19.0;
0563         else if( 9 == rounded )
0564             d->m_windSpeed = 22.65;
0565         else if( 10 == rounded )
0566             d->m_windSpeed = 26.5;
0567         else if( 11 == rounded )
0568             d->m_windSpeed = 30.6;
0569         else 
0570             d->m_windSpeed = 34;
0571     }
0572     else {
0573         mDebug() << "Wrong speed format";
0574     }
0575 }
0576 
0577 bool WeatherData::hasValidWindSpeed() const
0578 {
0579     return d->isPositiveValue( d->m_windSpeed );
0580 }
0581 
0582 QString WeatherData::windSpeedString( WeatherData::SpeedUnit unit ) const
0583 {
0584     QLocale locale = QLocale::system();
0585     // We round to integer.
0586     QString string = locale.toString( floor( windSpeed( unit ) + 0.5 ) );
0587     string += QLatin1Char(' ');
0588     switch ( unit ) {
0589         case WeatherData::kph:
0590             string += QObject::tr("km/h");
0591             break;
0592         case WeatherData::mph:
0593             string += QObject::tr("mph");
0594             break;
0595         case WeatherData::mps:
0596             string += QObject::tr( "m/s" );
0597             break;
0598         case WeatherData::knots:
0599             string += QObject::tr( "knots" );
0600             break;
0601         case WeatherData::beaufort:
0602             string += QObject::tr( "Beaufort" );
0603             break;
0604     }
0605     return string;
0606 }
0607 
0608 qreal WeatherData::temperature( WeatherData::TemperatureUnit format ) const
0609 {
0610     return d->fromKelvin( d->m_temperature, format );
0611 }
0612 
0613 void WeatherData::setTemperature( qreal temp, WeatherData::TemperatureUnit format )
0614 {
0615     detach();
0616     d->m_temperature = d->toKelvin( temp, format );
0617 }
0618 
0619 bool WeatherData::hasValidTemperature() const
0620 {
0621     return d->isPositiveValue( d->m_temperature );
0622 }
0623 
0624 QString WeatherData::temperatureString( WeatherData::TemperatureUnit format ) const
0625 {
0626     return d->generateTemperatureString( d->m_temperature,
0627                                          format );
0628 }
0629 
0630 qreal WeatherData::maxTemperature( WeatherData::TemperatureUnit format ) const
0631 {
0632     return d->fromKelvin( d->m_maxTemperature, format );
0633 }
0634 
0635 void WeatherData::setMaxTemperature( qreal temp, WeatherData::TemperatureUnit format )
0636 {
0637     detach();
0638     d->m_maxTemperature = d->toKelvin( temp, format );
0639 }
0640 
0641 QString WeatherData::maxTemperatureString( WeatherData::TemperatureUnit format ) const
0642 {
0643     return d->generateTemperatureString( d->m_maxTemperature,
0644                                          format );
0645 }
0646 
0647 bool WeatherData::hasValidMaxTemperature() const
0648 {
0649     return d->isPositiveValue( d->m_maxTemperature );
0650 }
0651 
0652 qreal WeatherData::minTemperature( WeatherData::TemperatureUnit format ) const
0653 {
0654     return d->fromKelvin( d->m_minTemperature, format );
0655 }
0656 
0657 QString WeatherData::minTemperatureString( WeatherData::TemperatureUnit format ) const
0658 {
0659     return d->generateTemperatureString( d->m_minTemperature,
0660                                          format );
0661 }
0662 
0663 void WeatherData::setMinTemperature( qreal temp, WeatherData::TemperatureUnit format )
0664 {
0665     detach();
0666     d->m_minTemperature = d->toKelvin( temp, format );
0667 }
0668 
0669 bool WeatherData::hasValidMinTemperature() const
0670 {
0671     return d->isPositiveValue( d->m_minTemperature );
0672 }
0673 
0674 WeatherData::Visibility WeatherData::visibility() const
0675 {
0676     return d->m_visibility;
0677 }
0678 
0679 void WeatherData::setVisibilty( WeatherData::Visibility visibility )
0680 {
0681     detach();
0682     d->m_visibility = visibility;
0683 }
0684 
0685 bool WeatherData::hasValidVisibility() const
0686 {
0687     return d->m_visibility != WeatherData::VisibilityNotAvailable;
0688 }
0689 
0690 qreal WeatherData::pressure( WeatherData::PressureUnit format ) const
0691 {
0692     if ( WeatherData::HectoPascal == format ) {
0693         return d->m_pressure;
0694     }
0695     else if ( WeatherData::KiloPascal == format ) {
0696         return d->m_pressure * HPA2KPA;
0697     }
0698     else if ( WeatherData::Bar == format ) {
0699         return d->m_pressure * HPA2BAR;
0700     }
0701     else if ( WeatherData::mmHg == format ) {
0702         return d->m_pressure * HPA2HG;
0703     }
0704     else if ( WeatherData::inchHg == format ) {
0705         return d->m_pressure * HPA2IHG;
0706     }
0707     else {
0708         mDebug() << "Wrong pressure format";
0709         return 0;
0710     }
0711 }
0712 
0713 void WeatherData::setPressure( qreal pressure, WeatherData::PressureUnit format )
0714 {
0715     detach();
0716     if ( WeatherData::HectoPascal == format ) {
0717         d->m_pressure = pressure;
0718     }
0719     else if ( WeatherData::KiloPascal == format ) {
0720         d->m_pressure = pressure * KPA2HPA;
0721     }
0722     else if ( WeatherData::Bar == format ) {
0723         d->m_pressure = pressure * BAR2HPA;
0724     }
0725     else if ( WeatherData::mmHg == format ) {
0726         d->m_pressure = pressure * HG2HPA;
0727     }
0728     else if ( WeatherData::inchHg == format ) {
0729         d->m_pressure = pressure * IHG2HPA;
0730     }
0731     else {
0732         mDebug() << "Wrong pressure format";
0733     }
0734 }
0735 
0736 bool WeatherData::hasValidPressure() const
0737 {
0738     return d->isPositiveValue( d->m_pressure );
0739 }
0740 
0741 QString WeatherData::pressureString( WeatherData::PressureUnit unit ) const
0742 {
0743     QLocale locale = QLocale::system();
0744     // We round to integer.
0745     QString string = locale.toString( pressure( unit ), 'f', 2 );
0746     string += QLatin1Char(' ');
0747     switch ( unit ) {
0748         case WeatherData::HectoPascal:
0749             string += tr( "hPa" );
0750             break;
0751         case WeatherData::KiloPascal:
0752             string += tr( "kPa" );
0753             break;
0754         case WeatherData::Bar:
0755             string += tr( "Bar" );
0756             break;
0757         case WeatherData::mmHg:
0758             string += tr( "mmHg" );
0759             break;
0760         case WeatherData::inchHg:
0761             string += tr( "inch Hg" );
0762             break;
0763     }
0764     return string;
0765 }
0766 
0767 WeatherData::PressureDevelopment WeatherData::pressureDevelopment() const
0768 {
0769     return d->m_pressureDevelopment;
0770 }
0771 
0772 void WeatherData::setPressureDevelopment( WeatherData::PressureDevelopment pressureDevelopment )
0773 {
0774     detach();
0775     d->m_pressureDevelopment = pressureDevelopment;
0776 }
0777 
0778 bool WeatherData::hasValidPressureDevelopment() const
0779 {
0780     return d->m_pressureDevelopment != WeatherData::PressureDevelopmentNotAvailable;
0781 }
0782 
0783 QString WeatherData::pressureDevelopmentString() const
0784 {
0785     switch ( pressureDevelopment() ) {
0786         case Rising:
0787             return tr( "rising", "air pressure is rising" );
0788         case NoChange:
0789             return tr( "steady", "air pressure has no change" );
0790         case Falling:
0791             return tr( "falling", "air pressure falls" );
0792         default:
0793             return "";
0794     }
0795 }
0796 
0797 qreal WeatherData::humidity() const
0798 {
0799     return d->m_humidity;
0800 }
0801 
0802 void WeatherData::setHumidity( qreal humidity )
0803 {
0804     detach();
0805     d->m_humidity = humidity;
0806 }
0807 
0808 bool WeatherData::hasValidHumidity() const
0809 {
0810     return d->isPositiveValue( d->m_humidity );
0811 }
0812 
0813 QString WeatherData::humidityString() const
0814 {
0815     return QString( "%1 %" ).arg( humidity() );
0816 }
0817 
0818 QString WeatherData::toHtml( WeatherData::TemperatureUnit temperatureUnit,
0819                              WeatherData::SpeedUnit speedUnit,
0820                              WeatherData::PressureUnit pressureUnit ) const
0821 {
0822     QString html;
0823     if ( hasValidPublishingTime() ) {
0824         html += tr( "Publishing time: %1<br>" )
0825                  .arg( publishingTime().toLocalTime().toString() );
0826     }
0827     if ( hasValidCondition() ) {
0828         html += tr( "Condition: %1<br>" )
0829                  .arg( conditionString() );
0830     }
0831     if ( hasValidTemperature() ) {
0832         html += tr( "Temperature: %1<br>" )
0833                  .arg( temperatureString( temperatureUnit ) );
0834     }
0835     if ( hasValidMaxTemperature() ) {
0836         html += tr( "Max temperature: %1<br>" )
0837                  .arg( maxTemperatureString( temperatureUnit ) );
0838     }
0839     if ( hasValidMinTemperature() ) {
0840         html += tr( "Min temperature: %1<br>" )
0841                  .arg( minTemperatureString( temperatureUnit ) );
0842     }
0843     if ( hasValidWindDirection() ) {
0844         html += tr( "Wind direction: %1<br>" )
0845                  .arg( windDirectionString() );
0846     }
0847     if ( hasValidWindSpeed() ) {
0848         html += tr( "Wind speed: %1<br>" )
0849                  .arg( windSpeedString( speedUnit ) );
0850     }
0851     if ( hasValidPressure() ) {
0852         html += tr( "Pressure: %1<br>" )
0853                  .arg( pressureString( pressureUnit ) );
0854     }
0855     if ( hasValidPressureDevelopment() ) {
0856         html += tr( "Pressure development: %1<br>")
0857                  .arg( pressureDevelopmentString() );
0858     }
0859     if ( hasValidHumidity() ) {
0860         html += tr( "Humidity: %1<br>" )
0861                  .arg( humidityString() );
0862     }
0863 
0864     return html;
0865 }
0866 
0867 WeatherData& WeatherData::operator=( const WeatherData &other )
0868 {
0869     qAtomicAssign( d, other.d );
0870     return *this;
0871 }
0872 
0873 void WeatherData::detach()
0874 {
0875     qAtomicDetach( d );
0876 }
0877 
0878 } // namespace Marble