diff --git a/dataengines/weather/ions/envcan/ion_envcan.cpp b/dataengines/weather/ions/envcan/ion_envcan.cpp index 78001c220..e06432ae2 100644 --- a/dataengines/weather/ions/envcan/ion_envcan.cpp +++ b/dataengines/weather/ions/envcan/ion_envcan.cpp @@ -1,1671 +1,1671 @@ /*************************************************************************** * Copyright (C) 2007-2011 by Shawn Starr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * ***************************************************************************/ /* Ion for Environment Canada XML data */ #include "ion_envcan.h" #include "ion_envcandebug.h" #include #include #include #include #include WeatherData::WeatherData() : stationLatitude(qQNaN()) , stationLongitude(qQNaN()) , temperature(qQNaN()) , dewpoint(qQNaN()) , windchill(qQNaN()) , pressure(qQNaN()) , visibility(qQNaN()) , humidity(qQNaN()) , windSpeed(qQNaN()) , windGust(qQNaN()) , normalHigh(qQNaN()) , normalLow(qQNaN()) , prevHigh(qQNaN()) , prevLow(qQNaN()) , recordHigh(qQNaN()) , recordLow(qQNaN()) , recordRain(qQNaN()) , recordSnow(qQNaN()) { } WeatherData::ForecastInfo::ForecastInfo() : tempHigh(qQNaN()) , tempLow(qQNaN()) , popPrecent(qQNaN()) { } // ctor, dtor EnvCanadaIon::EnvCanadaIon(QObject *parent, const QVariantList &args) : IonInterface(parent, args) { // Get the real city XML URL so we can parse this getXMLSetup(); } void EnvCanadaIon::deleteForecasts() { QMutableHashIterator it(m_weatherData); while (it.hasNext()) { it.next(); WeatherData &item = it.value(); qDeleteAll(item.warnings); item.warnings.clear(); qDeleteAll(item.watches); item.watches.clear(); qDeleteAll(item.forecasts); item.forecasts.clear(); } } void EnvCanadaIon::reset() { deleteForecasts(); emitWhenSetup = true; m_sourcesToReset = sources(); getXMLSetup(); } EnvCanadaIon::~EnvCanadaIon() { // Destroy each watch/warning stored in a QVector deleteForecasts(); } QMap EnvCanadaIon::setupConditionIconMappings() const { return QMap { // Explicit periods { QStringLiteral("mainly sunny"), FewCloudsDay }, { QStringLiteral("mainly clear"), FewCloudsNight }, { QStringLiteral("sunny"), ClearDay }, { QStringLiteral("clear"), ClearNight }, // Available conditions { QStringLiteral("blowing snow"), Snow }, { QStringLiteral("cloudy"), Overcast }, { QStringLiteral("distant precipitation"), LightRain }, { QStringLiteral("drifting snow"), Flurries }, { QStringLiteral("drizzle"), LightRain }, { QStringLiteral("dust"), NotAvailable }, { QStringLiteral("dust devils"), NotAvailable }, { QStringLiteral("fog"), Mist }, { QStringLiteral("fog bank near station"), Mist }, { QStringLiteral("fog depositing ice"), Mist }, { QStringLiteral("fog patches"), Mist }, { QStringLiteral("freezing drizzle"), FreezingDrizzle }, { QStringLiteral("freezing rain"), FreezingRain }, { QStringLiteral("funnel cloud"), NotAvailable }, { QStringLiteral("hail"), Hail }, { QStringLiteral("haze"), Haze }, { QStringLiteral("heavy blowing snow"), Snow }, { QStringLiteral("heavy drifting snow"), Snow }, { QStringLiteral("heavy drizzle"), LightRain }, { QStringLiteral("heavy hail"), Hail }, { QStringLiteral("heavy mixed rain and drizzle"), LightRain }, { QStringLiteral("heavy mixed rain and snow shower"), RainSnow }, { QStringLiteral("heavy rain"), Rain }, { QStringLiteral("heavy rain and snow"), RainSnow }, { QStringLiteral("heavy rainshower"), Rain }, { QStringLiteral("heavy snow"), Snow }, { QStringLiteral("heavy snow pellets"), Snow }, { QStringLiteral("heavy snowshower"), Snow }, { QStringLiteral("heavy thunderstorm with hail"), Thunderstorm }, { QStringLiteral("heavy thunderstorm with rain"), Thunderstorm }, { QStringLiteral("ice crystals"), Flurries }, { QStringLiteral("ice pellets"), Hail }, { QStringLiteral("increasing cloud"), Overcast }, { QStringLiteral("light drizzle"), LightRain }, { QStringLiteral("light freezing drizzle"), FreezingRain }, { QStringLiteral("light freezing rain"), FreezingRain }, { QStringLiteral("light rain"), LightRain }, { QStringLiteral("light rainshower"), LightRain }, { QStringLiteral("light snow"), LightSnow }, { QStringLiteral("light snow pellets"), LightSnow }, { QStringLiteral("light snowshower"), Flurries }, { QStringLiteral("lightning visible"), Thunderstorm }, { QStringLiteral("mist"), Mist }, { QStringLiteral("mixed rain and drizzle"), LightRain }, { QStringLiteral("mixed rain and snow shower"), RainSnow }, { QStringLiteral("not reported"), NotAvailable }, { QStringLiteral("rain"), Rain }, { QStringLiteral("rain and snow"), RainSnow }, { QStringLiteral("rainshower"), LightRain }, { QStringLiteral("recent drizzle"), LightRain }, { QStringLiteral("recent dust or sand storm"), NotAvailable }, { QStringLiteral("recent fog"), Mist }, { QStringLiteral("recent freezing precipitation"), FreezingDrizzle }, { QStringLiteral("recent hail"), Hail }, { QStringLiteral("recent rain"), Rain }, { QStringLiteral("recent rain and snow"), RainSnow }, { QStringLiteral("recent rainshower"), Rain }, { QStringLiteral("recent snow"), Snow }, { QStringLiteral("recent snowshower"), Flurries }, { QStringLiteral("recent thunderstorm"), Thunderstorm }, { QStringLiteral("recent thunderstorm with hail"), Thunderstorm }, { QStringLiteral("recent thunderstorm with heavy hail"), Thunderstorm }, { QStringLiteral("recent thunderstorm with heavy rain"), Thunderstorm }, { QStringLiteral("recent thunderstorm with rain"), Thunderstorm }, { QStringLiteral("sand or dust storm"), NotAvailable }, { QStringLiteral("severe sand or dust storm"), NotAvailable }, { QStringLiteral("shallow fog"), Mist }, { QStringLiteral("smoke"), NotAvailable }, { QStringLiteral("snow"), Snow }, { QStringLiteral("snow crystals"), Flurries }, { QStringLiteral("snow grains"), Flurries }, { QStringLiteral("squalls"), Snow }, { QStringLiteral("thunderstorm with hail"), Thunderstorm }, { QStringLiteral("thunderstorm with rain"), Thunderstorm }, { QStringLiteral("thunderstorm with sand or dust storm"), Thunderstorm }, { QStringLiteral("thunderstorm without precipitation"), Thunderstorm }, { QStringLiteral("tornado"), NotAvailable }, }; } QMap EnvCanadaIon::setupForecastIconMappings() const { return QMap { // Abbreviated forecast descriptions { QStringLiteral("a few flurries"), Flurries }, { QStringLiteral("a few flurries mixed with ice pellets"), RainSnow }, { QStringLiteral("a few flurries or rain showers"), RainSnow }, { QStringLiteral("a few flurries or thundershowers"), RainSnow }, { QStringLiteral("a few rain showers or flurries"), RainSnow }, { QStringLiteral("a few rain showers or wet flurries"), RainSnow }, { QStringLiteral("a few showers"), LightRain }, { QStringLiteral("a few showers or drizzle"), LightRain }, { QStringLiteral("a few showers or thundershowers"), Thunderstorm }, { QStringLiteral("a few showers or thunderstorms"), Thunderstorm }, { QStringLiteral("a few thundershowers"), Thunderstorm }, { QStringLiteral("a few thunderstorms"), Thunderstorm }, { QStringLiteral("a few wet flurries"), RainSnow }, { QStringLiteral("a few wet flurries or rain showers"), RainSnow }, { QStringLiteral("a mix of sun and cloud"), PartlyCloudyDay }, { QStringLiteral("cloudy with sunny periods"), PartlyCloudyDay }, { QStringLiteral("partly cloudy"), PartlyCloudyDay }, { QStringLiteral("mainly cloudy"), PartlyCloudyDay }, { QStringLiteral("mainly sunny"), FewCloudsDay }, { QStringLiteral("sunny"), ClearDay }, { QStringLiteral("blizzard"), Snow }, { QStringLiteral("clear"), ClearNight }, { QStringLiteral("cloudy"), Overcast }, { QStringLiteral("drizzle"), LightRain }, { QStringLiteral("drizzle mixed with freezing drizzle"), FreezingDrizzle }, { QStringLiteral("drizzle mixed with rain"), LightRain }, { QStringLiteral("drizzle or freezing drizzle"), LightRain }, { QStringLiteral("drizzle or rain"), LightRain }, { QStringLiteral("flurries"), Flurries }, { QStringLiteral("flurries at times heavy"), Flurries }, { QStringLiteral("flurries at times heavy or rain snowers"), RainSnow }, { QStringLiteral("flurries mixed with ice pellets"), FreezingRain }, { QStringLiteral("flurries or ice pellets"), FreezingRain }, { QStringLiteral("flurries or rain showers"), RainSnow }, { QStringLiteral("flurries or thundershowers"), Flurries }, { QStringLiteral("fog"), Mist }, { QStringLiteral("fog developing"), Mist }, { QStringLiteral("fog dissipating"), Mist }, { QStringLiteral("fog patches"), Mist }, { QStringLiteral("freezing drizzle"), FreezingDrizzle }, { QStringLiteral("freezing rain"), FreezingRain }, { QStringLiteral("freezing rain mixed with rain"), FreezingRain }, { QStringLiteral("freezing rain mixed with snow"), FreezingRain }, { QStringLiteral("freezing rain or ice pellets"), FreezingRain }, { QStringLiteral("freezing rain or rain"), FreezingRain }, { QStringLiteral("freezing rain or snow"), FreezingRain }, { QStringLiteral("ice fog"), Mist }, { QStringLiteral("ice fog developing"), Mist }, { QStringLiteral("ice fog dissipating"), Mist }, { QStringLiteral("ice pellets"), Hail }, { QStringLiteral("ice pellets mixed with freezing rain"), Hail }, { QStringLiteral("ice pellets mixed with snow"), Hail }, { QStringLiteral("ice pellets or snow"), RainSnow }, { QStringLiteral("light snow"), LightSnow }, { QStringLiteral("light snow and blizzard"), LightSnow }, { QStringLiteral("light snow and blizzard and blowing snow"), Snow }, { QStringLiteral("light snow and blowing snow"), LightSnow }, { QStringLiteral("light snow mixed with freezing drizzle"), FreezingDrizzle }, { QStringLiteral("light snow mixed with freezing rain"), FreezingRain }, { QStringLiteral("light snow or ice pellets"), LightSnow }, { QStringLiteral("light snow or rain"), RainSnow }, { QStringLiteral("light wet snow"), RainSnow }, { QStringLiteral("light wet snow or rain"), RainSnow }, { QStringLiteral("local snow squalls"), Snow }, { QStringLiteral("near blizzard"), Snow }, { QStringLiteral("overcast"), Overcast }, { QStringLiteral("increasing cloudiness"), Overcast }, { QStringLiteral("increasing clouds"), Overcast }, { QStringLiteral("periods of drizzle"), LightRain }, { QStringLiteral("periods of drizzle mixed with freezing drizzle"), FreezingDrizzle }, { QStringLiteral("periods of drizzle mixed with rain"), LightRain }, { QStringLiteral("periods of drizzle or freezing drizzle"), FreezingDrizzle }, { QStringLiteral("periods of drizzle or rain"), LightRain }, { QStringLiteral("periods of freezing drizzle"), FreezingDrizzle }, { QStringLiteral("periods of freezing drizzle or drizzle"), FreezingDrizzle }, { QStringLiteral("periods of freezing drizzle or rain"), FreezingDrizzle }, { QStringLiteral("periods of freezing rain"), FreezingRain }, { QStringLiteral("periods of freezing rain mixed with ice pellets"), FreezingRain }, { QStringLiteral("periods of freezing rain mixed with rain"), FreezingRain }, { QStringLiteral("periods of freezing rain mixed with snow"), FreezingRain }, { QStringLiteral("periods of freezing rain mixed with freezing drizzle"), FreezingRain }, { QStringLiteral("periods of freezing rain or ice pellets"), FreezingRain }, { QStringLiteral("periods of freezing rain or rain"), FreezingRain }, { QStringLiteral("periods of freezing rain or snow"), FreezingRain }, { QStringLiteral("periods of ice pellets"), Hail }, { QStringLiteral("periods of ice pellets mixed with freezing rain"), Hail }, { QStringLiteral("periods of ice pellets mixed with snow"), Hail }, { QStringLiteral("periods of ice pellets or freezing rain"), Hail }, { QStringLiteral("periods of ice pellets or snow"), Hail }, { QStringLiteral("periods of light snow"), LightSnow }, { QStringLiteral("periods of light snow and blizzard"), Snow }, { QStringLiteral("periods of light snow and blizzard and blowing snow"), Snow }, { QStringLiteral("periods of light snow and blowing snow"), LightSnow }, { QStringLiteral("periods of light snow mixed with freezing drizzle"), RainSnow }, { QStringLiteral("periods of light snow mixed with freezing rain"), RainSnow }, - { QStringLiteral("periods of light snow mixed with ice pelletS"), LightSnow }, + { QStringLiteral("periods of light snow mixed with ice pellets"), LightSnow }, { QStringLiteral("periods of light snow mixed with rain"), RainSnow }, { QStringLiteral("periods of light snow or freezing drizzle"), RainSnow }, { QStringLiteral("periods of light snow or freezing rain"), RainSnow }, { QStringLiteral("periods of light snow or ice pellets"), LightSnow }, { QStringLiteral("periods of light snow or rain"), RainSnow }, { QStringLiteral("periods of light wet snow"), LightSnow }, { QStringLiteral("periods of light wet snow mixed with rain"), RainSnow }, { QStringLiteral("periods of light wet snow or rain"), RainSnow }, { QStringLiteral("periods of rain"), Rain }, { QStringLiteral("periods of rain mixed with freezing rain"), Rain }, { QStringLiteral("periods of rain mixed with snow"), RainSnow }, { QStringLiteral("periods of rain or drizzle"), Rain }, { QStringLiteral("periods of rain or freezing rain"), Rain }, { QStringLiteral("periods of rain or thundershowers"), Showers }, { QStringLiteral("periods of rain or thunderstorms"), Thunderstorm }, { QStringLiteral("periods of rain or snow"), RainSnow }, { QStringLiteral("periods of snow"), Snow }, { QStringLiteral("periods of snow and blizzard"), Snow }, { QStringLiteral("periods of snow and blizzard and blowing snow"), Snow }, { QStringLiteral("periods of snow and blowing snow"), Snow }, { QStringLiteral("periods of snow mixed with freezing drizzle"), RainSnow }, { QStringLiteral("periods of snow mixed with freezing rain"), RainSnow }, { QStringLiteral("periods of snow mixed with ice pellets"), Snow }, { QStringLiteral("periods of snow mixed with rain"), RainSnow }, { QStringLiteral("periods of snow or freezing drizzle"), RainSnow }, { QStringLiteral("periods of snow or freezing rain"), RainSnow }, { QStringLiteral("periods of snow or ice pellets"), Snow }, { QStringLiteral("periods of snow or rain"), RainSnow }, { QStringLiteral("periods of rain or snow"), RainSnow }, { QStringLiteral("periods of wet snow"), Snow }, { QStringLiteral("periods of wet snow mixed with rain"), RainSnow }, { QStringLiteral("periods of wet snow or rain"), RainSnow }, { QStringLiteral("rain"), Rain }, { QStringLiteral("rain at times heavy"), Rain }, { QStringLiteral("rain at times heavy mixed with freezing rain"), FreezingRain }, { QStringLiteral("rain at times heavy mixed with snow"), RainSnow }, { QStringLiteral("rain at times heavy or drizzle"), Rain }, { QStringLiteral("rain at times heavy or freezing rain"), Rain }, { QStringLiteral("rain at times heavy or snow"), RainSnow }, { QStringLiteral("rain at times heavy or thundershowers"), Showers }, { QStringLiteral("rain at times heavy or thunderstorms"), Thunderstorm }, { QStringLiteral("rain mixed with freezing rain"), FreezingRain }, { QStringLiteral("rain mixed with snow"), RainSnow }, { QStringLiteral("rain or drizzle"), Rain }, { QStringLiteral("rain or freezing rain"), Rain }, { QStringLiteral("rain or snow"), RainSnow }, { QStringLiteral("rain or thundershowers"), Showers }, { QStringLiteral("rain or thunderstorms"), Thunderstorm }, { QStringLiteral("rain showers or flurries"), RainSnow }, { QStringLiteral("rain showers or wet flurries"), RainSnow }, { QStringLiteral("showers"), Showers }, { QStringLiteral("showers at times heavy"), Showers }, { QStringLiteral("showers at times heavy or thundershowers"), Showers }, { QStringLiteral("showers at times heavy or thunderstorms"), Thunderstorm }, { QStringLiteral("showers or drizzle"), Showers }, { QStringLiteral("showers or thundershowers"), Thunderstorm }, { QStringLiteral("showers or thunderstorms"), Thunderstorm }, { QStringLiteral("smoke"), NotAvailable }, { QStringLiteral("snow"), Snow }, { QStringLiteral("snow and blizzard"), Snow }, { QStringLiteral("snow and blizzard and blowing snow"), Snow }, { QStringLiteral("snow and blowing snow"), Snow }, { QStringLiteral("snow at times heavy"), Snow }, { QStringLiteral("snow at times heavy and blizzard"), Snow }, { QStringLiteral("snow at times heavy and blowing snow"), Snow }, { QStringLiteral("snow at times heavy mixed with freezing drizzle"), RainSnow }, { QStringLiteral("snow at times heavy mixed with freezing rain"), RainSnow }, { QStringLiteral("snow at times heavy mixed with ice pellets"), Snow }, { QStringLiteral("snow at times heavy mixed with rain"), RainSnow }, { QStringLiteral("snow at times heavy or freezing rain"), RainSnow }, { QStringLiteral("snow at times heavy or ice pellets"), Snow }, { QStringLiteral("snow at times heavy or rain"), RainSnow }, { QStringLiteral("snow mixed with freezing drizzle"), RainSnow }, { QStringLiteral("snow mixed with freezing rain"), RainSnow }, { QStringLiteral("snow mixed with ice pellets"), Snow }, { QStringLiteral("snow mixed with rain"), RainSnow }, { QStringLiteral("snow or freezing drizzle"), RainSnow }, { QStringLiteral("snow or freezing rain"), RainSnow }, { QStringLiteral("snow or ice pellets"), Snow }, { QStringLiteral("snow or rain"), RainSnow }, { QStringLiteral("snow squalls"), Snow }, { QStringLiteral("sunny"), ClearDay }, { QStringLiteral("sunny with cloudy periods"), PartlyCloudyDay }, { QStringLiteral("thunderstorms"), Thunderstorm }, { QStringLiteral("thunderstorms and possible hail"), Thunderstorm }, { QStringLiteral("wet flurries"), Flurries }, { QStringLiteral("wet flurries at times heavy"), Flurries }, { QStringLiteral("wet flurries at times heavy or rain snowers"), RainSnow }, { QStringLiteral("wet flurries or rain showers"), RainSnow }, { QStringLiteral("wet snow"), Snow }, { QStringLiteral("wet snow at times heavy"), Snow }, { QStringLiteral("wet snow at times heavy mixed with rain"), RainSnow }, { QStringLiteral("wet snow mixed with rain"), RainSnow }, { QStringLiteral("wet snow or rain"), RainSnow }, { QStringLiteral("windy"), NotAvailable }, { QStringLiteral("chance of drizzle mixed with freezing drizzle"), LightRain }, { QStringLiteral("chance of flurries mixed with ice pellets"), Flurries }, { QStringLiteral("chance of flurries or ice pellets"), Flurries }, { QStringLiteral("chance of flurries or rain showers"), RainSnow }, { QStringLiteral("chance of flurries or thundershowers"), RainSnow }, { QStringLiteral("chance of freezing drizzle"), FreezingDrizzle }, { QStringLiteral("chance of freezing rain"), FreezingRain }, { QStringLiteral("chance of freezing rain mixed with snow"), RainSnow }, { QStringLiteral("chance of freezing rain or rain"), FreezingRain }, { QStringLiteral("chance of freezing rain or snow"), RainSnow }, { QStringLiteral("chance of light snow and blowing snow"), LightSnow }, { QStringLiteral("chance of light snow mixed with freezing drizzle"), LightSnow }, { QStringLiteral("chance of light snow mixed with ice pellets"), LightSnow }, { QStringLiteral("chance of light snow mixed with rain"), RainSnow }, { QStringLiteral("chance of light snow or freezing rain"), RainSnow }, { QStringLiteral("chance of light snow or ice pellets"), LightSnow }, { QStringLiteral("chance of light snow or rain"), RainSnow }, { QStringLiteral("chance of light wet snow"), Snow }, { QStringLiteral("chance of rain"), Rain }, { QStringLiteral("chance of rain at times heavy"), Rain }, { QStringLiteral("chance of rain mixed with snow"), RainSnow }, { QStringLiteral("chance of rain or drizzle"), Rain }, { QStringLiteral("chance of rain or freezing rain"), Rain }, { QStringLiteral("chance of rain or snow"), RainSnow }, { QStringLiteral("chance of rain showers or flurries"), RainSnow }, { QStringLiteral("chance of rain showers or wet flurries"), RainSnow }, { QStringLiteral("chance of severe thunderstorms"), Thunderstorm }, { QStringLiteral("chance of showers at times heavy"), Rain }, { QStringLiteral("chance of showers at times heavy or thundershowers"), Thunderstorm }, { QStringLiteral("chance of showers at times heavy or thunderstorms"), Thunderstorm }, { QStringLiteral("chance of showers or thundershowers"), Thunderstorm }, { QStringLiteral("chance of showers or thunderstorms"), Thunderstorm }, { QStringLiteral("chance of snow"), Snow }, { QStringLiteral("chance of snow and blizzard"), Snow }, { QStringLiteral("chance of snow mixed with freezing drizzle"), Snow }, { QStringLiteral("chance of snow mixed with freezing rain"), RainSnow }, { QStringLiteral("chance of snow mixed with rain"), RainSnow }, { QStringLiteral("chance of snow or rain"), RainSnow }, { QStringLiteral("chance of snow squalls"), Snow }, { QStringLiteral("chance of thundershowers"), Showers }, { QStringLiteral("chance of thunderstorms"), Thunderstorm }, { QStringLiteral("chance of thunderstorms and possible hail"), Thunderstorm }, { QStringLiteral("chance of wet flurries"), Flurries }, { QStringLiteral("chance of wet flurries at times heavy"), Flurries }, { QStringLiteral("chance of wet flurries or rain showers"), RainSnow }, { QStringLiteral("chance of wet snow"), Snow }, { QStringLiteral("chance of wet snow mixed with rain"), RainSnow }, { QStringLiteral("chance of wet snow or rain"), RainSnow }, }; } QMap const& EnvCanadaIon::conditionIcons() const { static QMap const condval = setupConditionIconMappings(); return condval; } QMap const& EnvCanadaIon::forecastIcons() const { static QMap const foreval = setupForecastIconMappings(); return foreval; } QStringList EnvCanadaIon::validate(const QString& source) const { QStringList placeList; QString sourceNormalized = source.toUpper(); QHash::const_iterator it = m_places.constBegin(); while (it != m_places.constEnd()) { if (it.key().toUpper().contains(sourceNormalized)) { placeList.append(QStringLiteral("place|") + it.key()); } ++it; } placeList.sort(); return placeList; } // Get a specific Ion's data bool EnvCanadaIon::updateIonSource(const QString& source) { //qCDebug(IONENGINE_ENVCAN) << "updateIonSource()" << source; // We expect the applet to send the source in the following tokenization: // ionname|validate|place_name - Triggers validation of place // ionname|weather|place_name - Triggers receiving weather of place const QStringList sourceAction = source.split(QLatin1Char('|')); // Guard: if the size of array is not 2 then we have bad data, return an error if (sourceAction.size() < 2) { setData(source, QStringLiteral("validate"), QStringLiteral("envcan|malformed")); return true; } if (sourceAction[1] == QLatin1String("validate") && sourceAction.size() > 2) { const QStringList result = validate(sourceAction[2]); const QString reply = (result.size() == 1) ? QStringLiteral("envcan|valid|single|") + result[0] : (result.size() > 1) ? QStringLiteral("envcan|valid|multiple|") + result.join(QLatin1Char('|')) : /*else*/ QStringLiteral("envcan|invalid|single|") + sourceAction[2]; setData(source, QStringLiteral("validate"), reply); return true; } if (sourceAction[1] == QLatin1String("weather") && sourceAction.size() > 2) { getXMLData(source); return true; } setData(source, QStringLiteral("validate"), QStringLiteral("envcan|malformed")); return true; } // Parses city list and gets the correct city based on ID number void EnvCanadaIon::getXMLSetup() { //qCDebug(IONENGINE_ENVCAN) << "getXMLSetup()"; // If network is down, we need to spin and wait const QUrl url(QStringLiteral("http://dd.weatheroffice.ec.gc.ca/citypage_weather/xml/siteList.xml")); KIO::TransferJob* getJob = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo); m_xmlSetup.clear(); connect(getJob, &KIO::TransferJob::data, this, &EnvCanadaIon::setup_slotDataArrived); connect(getJob, &KJob::result, this, &EnvCanadaIon::setup_slotJobFinished); } // Gets specific city XML data void EnvCanadaIon::getXMLData(const QString& source) { for (const QString& fetching : qAsConst(m_jobList)) { if (fetching == source) { // already getting this source and awaiting the data return; } } //qCDebug(IONENGINE_ENVCAN) << source; // Demunge source name for key only. QString dataKey = source; dataKey.remove(QStringLiteral("envcan|weather|")); const XMLMapInfo& place = m_places[dataKey]; const QUrl url(QLatin1String("http://dd.weatheroffice.ec.gc.ca/citypage_weather/xml/") + place.territoryName + QLatin1Char('/') + place.cityCode + QStringLiteral("_e.xml")); //url="file:///home/spstarr/Desktop/s0000649_e.xml"; //qCDebug(IONENGINE_ENVCAN) << "Will Try URL: " << url; if (place.territoryName.isEmpty() && place.cityCode.isEmpty()) { setData(source, QStringLiteral("validate"), QStringLiteral("envcan|malformed")); return; } KIO::TransferJob* getJob = KIO::get(url, KIO::Reload, KIO::HideProgressInfo); m_jobXml.insert(getJob, new QXmlStreamReader); m_jobList.insert(getJob, source); connect(getJob, &KIO::TransferJob::data, this, &EnvCanadaIon::slotDataArrived); connect(getJob, &KJob::result, this, &EnvCanadaIon::slotJobFinished); } void EnvCanadaIon::setup_slotDataArrived(KIO::Job *job, const QByteArray &data) { Q_UNUSED(job) if (data.isEmpty()) { //qCDebug(IONENGINE_ENVCAN) << "done!"; return; } // Send to xml. //qCDebug(IONENGINE_ENVCAN) << data; m_xmlSetup.addData(data); } void EnvCanadaIon::slotDataArrived(KIO::Job *job, const QByteArray &data) { if (data.isEmpty() || !m_jobXml.contains(job)) { return; } // Send to xml. m_jobXml[job]->addData(data); } void EnvCanadaIon::slotJobFinished(KJob *job) { // Dual use method, if we're fetching location data to parse we need to do this first const QString source = m_jobList.value(job); //qCDebug(IONENGINE_ENVCAN) << source << m_sourcesToReset.contains(source); setData(source, Data()); QXmlStreamReader *reader = m_jobXml.value(job); if (reader) { readXMLData(m_jobList[job], *reader); } m_jobList.remove(job); delete m_jobXml[job]; m_jobXml.remove(job); if (m_sourcesToReset.contains(source)) { m_sourcesToReset.removeAll(source); // so the weather engine updates it's data forceImmediateUpdateOfAllVisualizations(); // update the clients of our engine emit forceUpdate(this, source); } } void EnvCanadaIon::setup_slotJobFinished(KJob *job) { Q_UNUSED(job) const bool success = readXMLSetup(); m_xmlSetup.clear(); //qCDebug(IONENGINE_ENVCAN) << success << m_sourcesToReset; setInitialized(success); } // Parse the city list and store into a QMap bool EnvCanadaIon::readXMLSetup() { bool success = false; QString territory; QString code; QString cityName; //qCDebug(IONENGINE_ENVCAN) << "readXMLSetup()"; while (!m_xmlSetup.atEnd()) { m_xmlSetup.readNext(); const QStringRef elementName = m_xmlSetup.name(); if (m_xmlSetup.isStartElement()) { // XML ID code to match filename if (elementName == QLatin1String("site")) { code = m_xmlSetup.attributes().value(QStringLiteral("code")).toString(); } if (elementName == QLatin1String("nameEn")) { cityName = m_xmlSetup.readElementText(); // Name of cities } if (elementName == QLatin1String("provinceCode")) { territory = m_xmlSetup.readElementText(); // Provinces/Territory list } } if (m_xmlSetup.isEndElement() && elementName == QLatin1String("site")) { EnvCanadaIon::XMLMapInfo info; QString tmp = cityName + QStringLiteral(", ") + territory; // Build the key name. // Set the mappings info.cityCode = code; info.territoryName = territory; info.cityName = cityName; // Set the string list, we will use for the applet to display the available cities. m_places[tmp] = info; success = true; } } return (success && !m_xmlSetup.error()); } void EnvCanadaIon::parseWeatherSite(WeatherData& data, QXmlStreamReader& xml) { while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isStartElement()) { if (elementName == QLatin1String("license")) { xml.readElementText(); } else if (elementName == QLatin1String("location")) { parseLocations(data, xml); } else if (elementName == QLatin1String("warnings")) { // Cleanup warning list on update data.warnings.clear(); data.watches.clear(); parseWarnings(data, xml); } else if (elementName == QLatin1String("currentConditions")) { parseConditions(data, xml); } else if (elementName == QLatin1String("forecastGroup")) { // Clean up forecast list on update data.forecasts.clear(); parseWeatherForecast(data, xml); } else if (elementName == QLatin1String("yesterdayConditions")) { parseYesterdayWeather(data, xml); } else if (elementName == QLatin1String("riseSet")) { parseAstronomicals(data, xml); } else if (elementName == QLatin1String("almanac")) { parseWeatherRecords(data, xml); } else { parseUnknownElement(xml); } } } } // Parse Weather data main loop, from here we have to decend into each tag pair bool EnvCanadaIon::readXMLData(const QString& source, QXmlStreamReader& xml) { WeatherData data; //qCDebug(IONENGINE_ENVCAN) << "readXMLData()"; QString dataKey = source; dataKey.remove(QStringLiteral("envcan|weather|")); data.shortTerritoryName = m_places[dataKey].territoryName; while (!xml.atEnd()) { xml.readNext(); if (xml.isEndElement()) { break; } if (xml.isStartElement()) { if (xml.name() == QLatin1String("siteData")) { parseWeatherSite(data, xml); } else { parseUnknownElement(xml); } } } bool solarDataSourceNeedsConnect = false; Plasma::DataEngine* timeEngine = dataEngine(QStringLiteral("time")); if (timeEngine) { const bool canCalculateElevation = (data.observationDateTime.isValid() && (!qIsNaN(data.stationLatitude) && !qIsNaN(data.stationLongitude))); if (canCalculateElevation) { data.solarDataTimeEngineSourceName = QStringLiteral("%1|Solar|Latitude=%2|Longitude=%3|DateTime=%4") .arg(QString::fromUtf8(data.observationDateTime.timeZone().id())) .arg(data.stationLatitude) .arg(data.stationLongitude) .arg(data.observationDateTime.toString(Qt::ISODate)); solarDataSourceNeedsConnect = true; } // check any previous data const auto it = m_weatherData.constFind(source); if (it != m_weatherData.constEnd()) { const QString& oldSolarDataTimeEngineSource = it.value().solarDataTimeEngineSourceName; if (oldSolarDataTimeEngineSource == data.solarDataTimeEngineSourceName) { // can reuse elevation source (if any), copy over data data.isNight = it.value().isNight; solarDataSourceNeedsConnect = false; } else if (!oldSolarDataTimeEngineSource.isEmpty()) { // drop old elevation source timeEngine->disconnectSource(oldSolarDataTimeEngineSource, this); } } } m_weatherData[source] = data; // connect only after m_weatherData has the data, so the instant data push handling can see it if (solarDataSourceNeedsConnect) { timeEngine->connectSource(data.solarDataTimeEngineSourceName, this); } else { updateWeather(source); } return !xml.error(); } void EnvCanadaIon::parseFloat(float& value, QXmlStreamReader& xml) { bool ok = false; const float result = xml.readElementText().toFloat(&ok); if (ok) { value = result; } } void EnvCanadaIon::parseDateTime(WeatherData& data, QXmlStreamReader& xml, WeatherData::WeatherEvent *event) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("dateTime")); // What kind of date info is this? const QString dateType = xml.attributes().value(QStringLiteral("name")).toString(); const QString dateZone = xml.attributes().value(QStringLiteral("zone")).toString(); const QString dateUtcOffset = xml.attributes().value(QStringLiteral("UTCOffset")).toString(); QString selectTimeStamp; while (!xml.atEnd()) { xml.readNext(); if (xml.isEndElement()) { break; } const QStringRef elementName = xml.name(); if (xml.isStartElement()) { if (dateType == QLatin1String("xmlCreation")) { return; } if (dateZone == QLatin1String("UTC")) { return; } if (elementName == QLatin1String("year")) { xml.readElementText(); } else if (elementName == QLatin1String("month")) { xml.readElementText(); } else if (elementName == QLatin1String("day")) { xml.readElementText(); } else if (elementName == QLatin1String("hour")) xml.readElementText(); else if (elementName == QLatin1String("minute")) xml.readElementText(); else if (elementName == QLatin1String("timeStamp")) selectTimeStamp = xml.readElementText(); else if (elementName == QLatin1String("textSummary")) { if (dateType == QLatin1String("eventIssue")) { if (event) { event->timestamp = xml.readElementText(); } } else if (dateType == QLatin1String("observation")) { xml.readElementText(); QDateTime observationDateTime = QDateTime::fromString(selectTimeStamp, QStringLiteral("yyyyMMddHHmmss")); QTimeZone timeZone = QTimeZone(dateZone.toUtf8()); // if timezone id not recognized, fallback to utcoffset if (!timeZone.isValid()) { timeZone = QTimeZone(dateUtcOffset.toInt() * 3600); } if (observationDateTime.isValid() && timeZone.isValid()) { data.observationDateTime = observationDateTime; data.observationDateTime.setTimeZone(timeZone); } data.obsTimestamp = observationDateTime.toString(QStringLiteral("dd.MM.yyyy @ hh:mm")); } else if (dateType == QLatin1String("forecastIssue")) { data.forecastTimestamp = xml.readElementText(); } else if (dateType == QLatin1String("sunrise")) { data.sunriseTimestamp = xml.readElementText(); } else if (dateType == QLatin1String("sunset")) { data.sunsetTimestamp = xml.readElementText(); } else if (dateType == QLatin1String("moonrise")) { data.moonriseTimestamp = xml.readElementText(); } else if (dateType == QLatin1String("moonset")) { data.moonsetTimestamp = xml.readElementText(); } } } } } void EnvCanadaIon::parseLocations(WeatherData& data, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("location")); while (!xml.atEnd()) { xml.readNext(); if (xml.isEndElement()) { break; } const QStringRef elementName = xml.name(); if (xml.isStartElement()) { if (elementName == QLatin1String("country")) { data.countryName = xml.readElementText(); } else if (elementName == QLatin1String("province") || elementName == QLatin1String("territory")) { data.longTerritoryName = xml.readElementText(); } else if (elementName == QLatin1String("name")) { data.cityName = xml.readElementText(); } else if (elementName == QLatin1String("region")) { data.regionName = xml.readElementText(); } else { parseUnknownElement(xml); } } } } void EnvCanadaIon::parseWindInfo(WeatherData& data, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("wind")); while (!xml.atEnd()) { xml.readNext(); if (xml.isEndElement()) { break; } const QStringRef elementName = xml.name(); if (xml.isStartElement()) { if (elementName == QLatin1String("speed")) { parseFloat(data.windSpeed, xml); } else if (elementName == QLatin1String("gust")) { parseFloat(data.windGust, xml); } else if (elementName == QLatin1String("direction")) { data.windDirection = xml.readElementText(); } else if (elementName == QLatin1String("bearing")) { data.windDegrees = xml.attributes().value(QStringLiteral("degrees")).toString(); } else { parseUnknownElement(xml); } } } } void EnvCanadaIon::parseConditions(WeatherData& data, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("currentConditions")); data.temperature = qQNaN(); data.dewpoint = qQNaN(); data.condition = i18n("N/A"); data.humidex.clear(); data.stationID = i18n("N/A"); data.stationLatitude = qQNaN(); data.stationLongitude = qQNaN(); data.pressure = qQNaN(); data.visibility = qQNaN(); data.humidity = qQNaN(); while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("currentConditions")) break; if (xml.isStartElement()) { if (elementName == QLatin1String("station")) { data.stationID = xml.attributes().value(QStringLiteral("code")).toString(); QRegularExpression dumpDirection(QStringLiteral("[^0-9.]")); data.stationLatitude = xml.attributes().value(QStringLiteral("lat")).toString().remove(dumpDirection).toDouble(); data.stationLongitude = xml.attributes().value(QStringLiteral("lon")).toString().remove(dumpDirection).toDouble(); } else if (elementName == QLatin1String("dateTime")) { parseDateTime(data, xml); } else if (elementName == QLatin1String("condition")) { data.condition = xml.readElementText(); } else if (elementName == QLatin1String("temperature")) { // prevent N/A text to result in 0.0 value parseFloat(data.temperature, xml); } else if (elementName == QLatin1String("dewpoint")) { // prevent N/A text to result in 0.0 value parseFloat(data.dewpoint, xml); } else if (elementName == QLatin1String("humidex")) { data.humidex = xml.readElementText(); } else if (elementName == QLatin1String("windChill")) { // prevent N/A text to result in 0.0 value parseFloat(data.windchill, xml); } else if (elementName == QLatin1String("pressure")) { data.pressureTendency = xml.attributes().value(QStringLiteral("tendency")).toString(); if (data.pressureTendency.isEmpty()) { data.pressureTendency = QStringLiteral("steady"); } parseFloat(data.pressure, xml); } else if (elementName == QLatin1String("visibility")) { parseFloat(data.visibility, xml); } else if (elementName == QLatin1String("relativeHumidity")) { parseFloat(data.humidity, xml); } else if (elementName == QLatin1String("wind")) { parseWindInfo(data, xml); } //} else { // parseUnknownElement(xml); //} } } } void EnvCanadaIon::parseWarnings(WeatherData &data, QXmlStreamReader& xml) { WeatherData::WeatherEvent *watch = new WeatherData::WeatherEvent; WeatherData::WeatherEvent *warning = new WeatherData::WeatherEvent; Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("warnings")); QString eventURL = xml.attributes().value(QStringLiteral("url")).toString(); int flag = 0; while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("warnings")) { break; } if (xml.isStartElement()) { if (elementName == QLatin1String("dateTime")) { if (flag == 1) { parseDateTime(data, xml, watch); } if (flag == 2) { parseDateTime(data, xml, warning); } if (!warning->timestamp.isEmpty() && !warning->url.isEmpty()) { data.warnings.append(warning); warning = new WeatherData::WeatherEvent; } if (!watch->timestamp.isEmpty() && !watch->url.isEmpty()) { data.watches.append(watch); watch = new WeatherData::WeatherEvent; } } else if (elementName == QLatin1String("event")) { // Append new event to list. QString eventType = xml.attributes().value(QStringLiteral("type")).toString(); if (eventType == QLatin1String("watch")) { watch->url = eventURL; watch->type = eventType; watch->priority = xml.attributes().value(QStringLiteral("priority")).toString(); watch->description = xml.attributes().value(QStringLiteral("description")).toString(); flag = 1; } if (eventType == QLatin1String("warning")) { warning->url = eventURL; warning->type = eventType; warning->priority = xml.attributes().value(QStringLiteral("priority")).toString(); warning->description = xml.attributes().value(QStringLiteral("description")).toString(); flag = 2; } } else { if (xml.name() != QLatin1String("dateTime")) { parseUnknownElement(xml); } } } } delete watch; delete warning; } void EnvCanadaIon::parseWeatherForecast(WeatherData& data, QXmlStreamReader& xml) { WeatherData::ForecastInfo* forecast = new WeatherData::ForecastInfo; Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("forecastGroup")); while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("forecastGroup")) { break; } if (xml.isStartElement()) { if (elementName == QLatin1String("dateTime")) { parseDateTime(data, xml); } else if (elementName == QLatin1String("regionalNormals")) { parseRegionalNormals(data, xml); } else if (elementName == QLatin1String("forecast")) { parseForecast(data, xml, forecast); forecast = new WeatherData::ForecastInfo; } else { parseUnknownElement(xml); } } } delete forecast; } void EnvCanadaIon::parseRegionalNormals(WeatherData& data, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("regionalNormals")); while (!xml.atEnd()) { xml.readNext(); if (xml.isEndElement()) { break; } const QStringRef elementName = xml.name(); if (xml.isStartElement()) { if (elementName == QLatin1String("textSummary")) { xml.readElementText(); } else if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("high")) { // prevent N/A text to result in 0.0 value parseFloat(data.normalHigh, xml); } else if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("low")) { // prevent N/A text to result in 0.0 value parseFloat(data.normalLow, xml); } } } } void EnvCanadaIon::parseForecast(WeatherData& data, QXmlStreamReader& xml, WeatherData::ForecastInfo *forecast) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("forecast")); while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("forecast")) { data.forecasts.append(forecast); break; } if (xml.isStartElement()) { if (elementName == QLatin1String("period")) { forecast->forecastPeriod = xml.attributes().value(QStringLiteral("textForecastName")).toString(); } else if (elementName == QLatin1String("textSummary")) { forecast->forecastSummary = xml.readElementText(); } else if (elementName == QLatin1String("abbreviatedForecast")) { parseShortForecast(forecast, xml); } else if (elementName == QLatin1String("temperatures")) { parseForecastTemperatures(forecast, xml); } else if (elementName == QLatin1String("winds")) { parseWindForecast(forecast, xml); } else if (elementName == QLatin1String("precipitation")) { parsePrecipitationForecast(forecast, xml); } else if (elementName == QLatin1String("uv")) { data.UVRating = xml.attributes().value(QStringLiteral("category")).toString(); parseUVIndex(data, xml); // else if (elementName == QLatin1String("frost")) { FIXME: Wait until winter to see what this looks like. // parseFrost(xml, forecast); } else { if (elementName != QLatin1String("forecast")) { parseUnknownElement(xml); } } } } } void EnvCanadaIon::parseShortForecast(WeatherData::ForecastInfo *forecast, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("abbreviatedForecast")); QString shortText; while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("abbreviatedForecast")) { break; } if (xml.isStartElement()) { if (elementName == QLatin1String("pop")) { parseFloat(forecast->popPrecent, xml); } if (elementName == QLatin1String("textSummary")) { shortText = xml.readElementText(); QMap forecastList = forecastIcons(); if ((forecast->forecastPeriod == QLatin1String("tonight")) || (forecast->forecastPeriod.contains(QLatin1String("night")))) { forecastList.insert(QStringLiteral("a few clouds"), FewCloudsNight); forecastList.insert(QStringLiteral("cloudy periods"), PartlyCloudyNight); forecastList.insert(QStringLiteral("chance of drizzle mixed with rain"), ChanceShowersNight); forecastList.insert(QStringLiteral("chance of drizzle"), ChanceShowersNight); forecastList.insert(QStringLiteral("chance of drizzle or rain"), ChanceShowersNight); forecastList.insert(QStringLiteral("chance of flurries"), ChanceSnowNight); forecastList.insert(QStringLiteral("chance of light snow"), ChanceSnowNight); forecastList.insert(QStringLiteral("chance of flurries at times heavy"), ChanceSnowNight); forecastList.insert(QStringLiteral("chance of showers or drizzle"), ChanceShowersNight); forecastList.insert(QStringLiteral("chance of showers"), ChanceShowersNight); forecastList.insert(QStringLiteral("clearing"), ClearNight); } else { forecastList.insert(QStringLiteral("a few clouds"), FewCloudsDay); forecastList.insert(QStringLiteral("cloudy periods"), PartlyCloudyDay); forecastList.insert(QStringLiteral("chance of drizzle mixed with rain"), ChanceShowersDay); forecastList.insert(QStringLiteral("chance of drizzle"), ChanceShowersDay); forecastList.insert(QStringLiteral("chance of drizzle or rain"), ChanceShowersDay); forecastList.insert(QStringLiteral("chance of flurries"), ChanceSnowDay); forecastList.insert(QStringLiteral("chance of light snow"), ChanceSnowDay); forecastList.insert(QStringLiteral("chance of flurries at times heavy"), ChanceSnowDay); forecastList.insert(QStringLiteral("chance of showers or drizzle"), ChanceShowersDay); forecastList.insert(QStringLiteral("chance of showers"), ChanceShowersDay); forecastList.insert(QStringLiteral("clearing"), ClearDay); } forecast->shortForecast = shortText; forecast->iconName = getWeatherIcon(forecastList, shortText.toLower()); } } } } void EnvCanadaIon::parseUVIndex(WeatherData& data, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("uv")); while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("uv")) { break; } if (xml.isStartElement()) { if (elementName == QLatin1String("index")) { data.UVIndex = xml.readElementText(); } if (elementName == QLatin1String("textSummary")) { xml.readElementText(); } } } } void EnvCanadaIon::parseForecastTemperatures(WeatherData::ForecastInfo *forecast, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("temperatures")); while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("temperatures")) { break; } if (xml.isStartElement()) { if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("low")) { parseFloat(forecast->tempLow, xml); } else if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("high")) { parseFloat(forecast->tempHigh, xml); } else if (elementName == QLatin1String("textSummary")) { xml.readElementText(); } } } } void EnvCanadaIon::parsePrecipitationForecast(WeatherData::ForecastInfo *forecast, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("precipitation")); while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("precipitation")) { break; } if (xml.isStartElement()) { if (elementName == QLatin1String("textSummary")) { forecast->precipForecast = xml.readElementText(); } else if (elementName == QLatin1String("precipType")) { forecast->precipType = xml.readElementText(); } else if (elementName == QLatin1String("accumulation")) { parsePrecipTotals(forecast, xml); } } } } void EnvCanadaIon::parsePrecipTotals(WeatherData::ForecastInfo *forecast, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("accumulation")); while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("accumulation")) { break; } if (elementName == QLatin1String("name")) { xml.readElementText(); } else if (elementName == QLatin1String("amount")) { forecast->precipTotalExpected = xml.readElementText(); } } } void EnvCanadaIon::parseWindForecast(WeatherData::ForecastInfo *forecast, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("winds")); while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("winds")) { break; } if (xml.isStartElement()) { if (elementName == QLatin1String("textSummary")) { forecast->windForecast = xml.readElementText(); } else { if (xml.name() != QLatin1String("winds")) { parseUnknownElement(xml); } } } } } void EnvCanadaIon::parseYesterdayWeather(WeatherData& data, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("yesterdayConditions")); while (!xml.atEnd()) { xml.readNext(); if (xml.isEndElement()) { break; } const QStringRef elementName = xml.name(); if (xml.isStartElement()) { if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("high")) { parseFloat(data.prevHigh, xml); } else if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("low")) { parseFloat(data.prevLow, xml); } else if (elementName == QLatin1String("precip")) { data.prevPrecipType = xml.attributes().value(QStringLiteral("units")).toString(); if (data.prevPrecipType.isEmpty()) { data.prevPrecipType = QString::number(KUnitConversion::NoUnit); } data.prevPrecipTotal = xml.readElementText(); } } } } void EnvCanadaIon::parseWeatherRecords(WeatherData& data, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("almanac")); while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("almanac")) { break; } if (xml.isStartElement()) { if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("extremeMax")) { parseFloat(data.recordHigh, xml); } else if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("extremeMin")) { parseFloat(data.recordLow, xml); } else if (elementName == QLatin1String("precipitation") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("extremeRainfall")) { parseFloat(data.recordRain, xml); } else if (elementName == QLatin1String("precipitation") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("extremeSnowfall")) { parseFloat(data.recordSnow, xml); } } } } void EnvCanadaIon::parseAstronomicals(WeatherData& data, QXmlStreamReader& xml) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("riseSet")); while (!xml.atEnd()) { xml.readNext(); const QStringRef elementName = xml.name(); if (xml.isEndElement() && elementName == QLatin1String("riseSet")) { break; } if (xml.isStartElement()) { if (elementName == QLatin1String("disclaimer")) { xml.readElementText(); } else if (elementName == QLatin1String("dateTime")) { parseDateTime(data, xml); } } } } // handle when no XML tag is found void EnvCanadaIon::parseUnknownElement(QXmlStreamReader& xml) const { while (!xml.atEnd()) { xml.readNext(); if (xml.isEndElement()) { break; } if (xml.isStartElement()) { parseUnknownElement(xml); } } } void EnvCanadaIon::updateWeather(const QString& source) { //qCDebug(IONENGINE_ENVCAN) << "updateWeather()"; const WeatherData& weatherData = m_weatherData[source]; Plasma::DataEngine::Data data; data.insert(QStringLiteral("Country"), weatherData.countryName); data.insert(QStringLiteral("Place"), QVariant(weatherData.cityName + QStringLiteral(", ") + weatherData.shortTerritoryName)); data.insert(QStringLiteral("Region"), weatherData.regionName); data.insert(QStringLiteral("Station"), weatherData.stationID.isEmpty() ? i18n("N/A") : weatherData.stationID.toUpper()); const bool stationCoordValid = (!qIsNaN(weatherData.stationLatitude) && !qIsNaN(weatherData.stationLongitude)); if (stationCoordValid) { data.insert(QStringLiteral("Latitude"), weatherData.stationLatitude); data.insert(QStringLiteral("Longitude"), weatherData.stationLongitude); } // Real weather - Current conditions if (weatherData.observationDateTime.isValid()) { data.insert(QStringLiteral("Observation Timestamp"), weatherData.observationDateTime); } data.insert(QStringLiteral("Observation Period"), weatherData.obsTimestamp); if (!weatherData.condition.isEmpty()) { data.insert(QStringLiteral("Current Conditions"), i18nc("weather condition", weatherData.condition.toUtf8().data())); } //qCDebug(IONENGINE_ENVCAN) << "i18n condition string: " << qPrintable(condition(source)); QMap conditionList = conditionIcons(); if (weatherData.isNight) { conditionList.insert(QStringLiteral("decreasing cloud"), FewCloudsNight); conditionList.insert(QStringLiteral("mostly cloudy"), PartlyCloudyNight); conditionList.insert(QStringLiteral("partly cloudy"), PartlyCloudyNight); conditionList.insert(QStringLiteral("fair"), FewCloudsNight); } else { conditionList.insert(QStringLiteral("decreasing cloud"), FewCloudsDay); conditionList.insert(QStringLiteral("mostly cloudy"), PartlyCloudyDay); conditionList.insert(QStringLiteral("partly cloudy"), PartlyCloudyDay); conditionList.insert(QStringLiteral("fair"), FewCloudsDay); } data.insert(QStringLiteral("Condition Icon"), getWeatherIcon(conditionList, weatherData.condition)); if (!qIsNaN(weatherData.temperature)) { data.insert(QStringLiteral("Temperature"), weatherData.temperature); } if (!qIsNaN(weatherData.windchill)) { data.insert(QStringLiteral("Windchill"), weatherData.windchill); } if (!weatherData.humidex.isEmpty()) { data.insert(QStringLiteral("Humidex"), weatherData.humidex); } // Used for all temperatures data.insert(QStringLiteral("Temperature Unit"), KUnitConversion::Celsius); if (!qIsNaN(weatherData.dewpoint)) { data.insert(QStringLiteral("Dewpoint"), weatherData.dewpoint); } if (!qIsNaN(weatherData.pressure)) { data.insert(QStringLiteral("Pressure"), weatherData.pressure); data.insert(QStringLiteral("Pressure Unit"), KUnitConversion::Kilopascal); data.insert(QStringLiteral("Pressure Tendency"), weatherData.pressureTendency); } if (!qIsNaN(weatherData.visibility)) { data.insert(QStringLiteral("Visibility"), weatherData.visibility); data.insert(QStringLiteral("Visibility Unit"), KUnitConversion::Kilometer); } if (!qIsNaN(weatherData.humidity)) { data.insert(QStringLiteral("Humidity"), weatherData.humidity); data.insert(QStringLiteral("Humidity Unit"), KUnitConversion::Percent); } if (!qIsNaN(weatherData.windSpeed)) { data.insert(QStringLiteral("Wind Speed"), weatherData.windSpeed); } if (!qIsNaN(weatherData.windGust)) { data.insert(QStringLiteral("Wind Gust"), weatherData.windGust); } if (!qIsNaN(weatherData.windSpeed) || !qIsNaN(weatherData.windGust)) { data.insert(QStringLiteral("Wind Speed Unit"), KUnitConversion::KilometerPerHour); } if (!qIsNaN(weatherData.windSpeed) && static_cast(weatherData.windSpeed) == 0) { data.insert(QStringLiteral("Wind Direction"), QStringLiteral("VR")); // Variable/calm } else if (!weatherData.windDirection.isEmpty()) { data.insert(QStringLiteral("Wind Direction"), weatherData.windDirection); } if (!qIsNaN(weatherData.normalHigh)) { data.insert(QStringLiteral("Normal High"), weatherData.normalHigh); } if (!qIsNaN(weatherData.normalLow)) { data.insert(QStringLiteral("Normal Low"), weatherData.normalLow); } // Check if UV index is available for the location if (!weatherData.UVIndex.isEmpty()) { data.insert(QStringLiteral("UV Index"), weatherData.UVIndex); } if (!weatherData.UVRating.isEmpty()) { data.insert(QStringLiteral("UV Rating"), weatherData.UVRating); } const QVector& watches = weatherData.watches; // Set number of forecasts per day/night supported data.insert(QStringLiteral("Total Watches Issued"), watches.size()); // Check if we have warnings or watches for (int i = 0; i < watches.size(); ++i) { const WeatherData::WeatherEvent* watch = watches.at(i); const QString number = QString::number(i); data.insert(QStringLiteral("Watch Priority ") + number, watch->priority); data.insert(QStringLiteral("Watch Description ") + number, watch->description); data.insert(QStringLiteral("Watch Info ") + number, watch->url); data.insert(QStringLiteral("Watch Timestamp ") + number, watch->timestamp); } const QVector& warnings = weatherData.warnings; data.insert(QStringLiteral("Total Warnings Issued"), warnings.size()); for (int k = 0; k < warnings.size(); ++k) { const WeatherData::WeatherEvent* warning = warnings.at(k); const QString number = QString::number(k); data.insert(QStringLiteral("Warning Priority ") + number, warning->priority); data.insert(QStringLiteral("Warning Description ") + number, warning->description); data.insert(QStringLiteral("Warning Info ") + number, warning->url); data.insert(QStringLiteral("Warning Timestamp ") + number, warning->timestamp); } const QVector & forecasts = weatherData.forecasts; // Set number of forecasts per day/night supported data.insert(QStringLiteral("Total Weather Days"), forecasts.size()); int i = 0; for (const WeatherData::ForecastInfo* forecastInfo : forecasts) { QString forecastPeriod = forecastInfo->forecastPeriod; if (forecastPeriod.isEmpty()) { forecastPeriod = i18n("N/A"); } else { // We need to shortform the day/night strings. forecastPeriod.replace(QStringLiteral("Today"), i18n("day")); forecastPeriod.replace(QStringLiteral("Tonight"), i18nc("Short for tonight", "nite")); forecastPeriod.replace(QStringLiteral("night"), i18nc("Short for night, appended to the end of the weekday", "nt")); forecastPeriod.replace(QStringLiteral("Saturday"), i18nc("Short for Saturday", "Sat")); forecastPeriod.replace(QStringLiteral("Sunday"), i18nc("Short for Sunday", "Sun")); forecastPeriod.replace(QStringLiteral("Monday"), i18nc("Short for Monday", "Mon")); forecastPeriod.replace(QStringLiteral("Tuesday"), i18nc("Short for Tuesday", "Tue")); forecastPeriod.replace(QStringLiteral("Wednesday"), i18nc("Short for Wednesday", "Wed")); forecastPeriod.replace(QStringLiteral("Thursday"), i18nc("Short for Thursday", "Thu")); forecastPeriod.replace(QStringLiteral("Friday"), i18nc("Short for Friday", "Fri")); } const QString shortForecast = forecastInfo->shortForecast.isEmpty() ? i18n("N/A") : i18nc("weather forecast", forecastInfo->shortForecast.toUtf8().data()); const QString tempHigh = qIsNaN(forecastInfo->tempHigh) ? QString() : QString::number(forecastInfo->tempHigh); const QString tempLow = qIsNaN(forecastInfo->tempLow) ? QString() : QString::number(forecastInfo->tempLow); const QString popPrecent = qIsNaN(forecastInfo->popPrecent) ? QString() : QString::number(forecastInfo->popPrecent); data.insert(QStringLiteral("Short Forecast Day %1").arg(i), QStringLiteral("%1|%2|%3|%4|%5|%6").arg( forecastPeriod, forecastInfo->iconName, shortForecast, tempHigh, tempLow, popPrecent)); //qCDebug(IONENGINE_ENVCAN) << "i18n summary string: " << qPrintable(i18n(forecastInfo->shortForecast.toUtf8())); /* data.insert(QString("Long Forecast Day %1").arg(i), QString("%1|%2|%3|%4|%5|%6|%7|%8") \ .arg(fieldList[0]).arg(fieldList[2]).arg(fieldList[3]).arg(fieldList[4]).arg(fieldList[6]) \ .arg(fieldList[7]).arg(fieldList[8]).arg(fieldList[9])); */ ++i; } // yesterday if (!qIsNaN(weatherData.prevHigh)) { data.insert(QStringLiteral("Yesterday High"), weatherData.prevHigh); } if (!qIsNaN(weatherData.prevLow)) { data.insert(QStringLiteral("Yesterday Low"), weatherData.prevLow); } const QString& prevPrecipTotal = weatherData.prevPrecipTotal; if (prevPrecipTotal == QLatin1String("Trace")) { data.insert(QStringLiteral("Yesterday Precip Total"), i18nc("precipitation total, very little", "Trace")); } else if (!prevPrecipTotal.isEmpty()) { data.insert(QStringLiteral("Yesterday Precip Total"), prevPrecipTotal); const QString& prevPrecipType = weatherData.prevPrecipType; const KUnitConversion::UnitId unit = (prevPrecipType == QLatin1String("mm")) ? KUnitConversion::Millimeter : (prevPrecipType == QLatin1String("cm")) ? KUnitConversion::Centimeter : /*else*/ KUnitConversion::NoUnit; data.insert(QStringLiteral("Yesterday Precip Unit"), unit); } // records if (!qIsNaN(weatherData.recordHigh)) { data.insert(QStringLiteral("Record High Temperature"), weatherData.recordHigh); } if (!qIsNaN(weatherData.recordLow)) { data.insert(QStringLiteral("Record Low Temperature"), weatherData.recordLow); } if (!qIsNaN(weatherData.recordRain)) { data.insert(QStringLiteral("Record Rainfall"), weatherData.recordRain); data.insert(QStringLiteral("Record Rainfall Unit"), KUnitConversion::Millimeter); } if (!qIsNaN(weatherData.recordSnow)) { data.insert(QStringLiteral("Record Snowfall"), weatherData.recordSnow); data.insert(QStringLiteral("Record Snowfall Unit"), KUnitConversion::Centimeter); } data.insert(QStringLiteral("Credit"), i18nc("credit line, keep string short", "Data from Environment\302\240Canada")); setData(source, data); } void EnvCanadaIon::dataUpdated(const QString& sourceName, const Plasma::DataEngine::Data& data) { const bool isNight = (data.value(QStringLiteral("Corrected Elevation")).toDouble() < 0.0); for (auto end = m_weatherData.end(), it = m_weatherData.begin(); it != end; ++it) { auto& weatherData = it.value(); if (weatherData.solarDataTimeEngineSourceName == sourceName) { weatherData.isNight = isNight; updateWeather(it.key()); } } } K_EXPORT_PLASMA_DATAENGINE_WITH_JSON(envcan, EnvCanadaIon, "ion-envcan.json") #include "ion_envcan.moc"