diff options
author | Luiz Agostini <luiz.agostini@openbossa.org> | 2009-11-10 00:15:56 -0300 |
---|---|---|
committer | Luiz Agostini <luiz.agostini@openbossa.org> | 2009-11-10 01:02:37 -0300 |
commit | 7821f5e49c28b4b8e72caeb7ab4f11c6d5e3555e (patch) | |
tree | 236503c0eb05d368ed5159c3a881e21515287e5a /weather | |
parent | 658f5c70b84d5ef8dec79472ac461d0e272137c5 (diff) |
Weather: real content.
Signed-off-by: Luiz Agostini <luiz.agostini@openbossa.org>
Diffstat (limited to 'weather')
-rw-r--r-- | weather/addcitytool.cpp | 31 | ||||
-rw-r--r-- | weather/addcitytool.h | 5 | ||||
-rw-r--r-- | weather/bootmanager.cpp | 21 | ||||
-rw-r--r-- | weather/bootmanager.h | 4 | ||||
-rw-r--r-- | weather/demoforecastsource.cpp | 140 | ||||
-rw-r--r-- | weather/demoforecastsource.h | 44 | ||||
-rw-r--r-- | weather/fake_content.xml | 27 | ||||
-rw-r--r-- | weather/forecastdata.cpp | 78 | ||||
-rw-r--r-- | weather/forecastdata.h | 60 | ||||
-rw-r--r-- | weather/forecastprovider.cpp | 110 | ||||
-rw-r--r-- | weather/forecastprovider.h | 28 | ||||
-rw-r--r-- | weather/forecastsource.h | 18 | ||||
-rw-r--r-- | weather/main.cpp | 11 | ||||
-rw-r--r-- | weather/networkforecastsource.cpp | 237 | ||||
-rw-r--r-- | weather/networkforecastsource.h | 75 | ||||
-rw-r--r-- | weather/resources.qrc | 1 | ||||
-rw-r--r-- | weather/settings.cpp | 1 | ||||
-rw-r--r-- | weather/sym_iap_util.h | 131 | ||||
-rw-r--r-- | weather/weather.ini | 33 | ||||
-rw-r--r-- | weather/weather.pro | 18 | ||||
-rw-r--r-- | weather/xoapweatherresponse.cpp | 48 | ||||
-rw-r--r-- | weather/xoapweatherresponse.h | 43 | ||||
-rw-r--r-- | weather/yahooweatherresponse.cpp | 163 | ||||
-rw-r--r-- | weather/yahooweatherresponse.h | 181 |
24 files changed, 1303 insertions, 205 deletions
diff --git a/weather/addcitytool.cpp b/weather/addcitytool.cpp index 9cd0089..4d0e4ed 100644 --- a/weather/addcitytool.cpp +++ b/weather/addcitytool.cpp @@ -119,19 +119,22 @@ AddCitySearchScreen::AddCitySearchScreen(const QRectF &boundingRect, QGraphicsIt } -void AddCitySearchScreen::forecastResponse(const ForecastData &forecast) +void AddCitySearchScreen::forecastResponse(int reqId, const ForecastData &forecast) { - if (forecast.key() == m_city) { + if (reqId == m_reqId) { reset(); - emit forecastReceived(forecast); + if (!forecast.isNull() && !forecast.error()) + emit forecastReceived(forecast); + else + emit forecastRequestError(m_city); } } void AddCitySearchScreen::setCityName(const QString &name) { m_city = name; - ForecastProvider::connectToResponseSignal(this, SLOT(forecastResponse(ForecastData))); - ForecastProvider::getForecast(m_city, false); + ForecastProvider::connectToResponseSignal(this, SLOT(forecastResponse(int, ForecastData))); + m_reqId = ForecastProvider::getForecast(m_city, false); m_loading->start(); m_loading->show(); update(); @@ -297,6 +300,8 @@ AddCitySearchScreen *AddCityTool::createSearchScreen() AddCitySearchScreen *result = new AddCitySearchScreen(boundingRect(), this); connect(result, SIGNAL(forecastReceived(ForecastData)), this, SLOT(forecastReceived(ForecastData))); + connect(result, SIGNAL(forecastRequestError(QString)), + this, SLOT(forecastRequestError(QString))); result->setPos(0.0, 0.0); return result; } @@ -324,14 +329,14 @@ void AddCityTool::errorScreenClosed() void AddCityTool::forecastReceived(const ForecastData &forecast) { - if (forecast.error()) { - m_ErrorScreen->setCityName(forecast.key(), AddCityErrorScreen::NotFound); - setCurrentScreen(m_ErrorScreen); - } - else { - emit newForecast(forecast); - setCurrentScreen(m_firstScreen); - } + emit newForecast(forecast); + setCurrentScreen(m_firstScreen); +} + +void AddCityTool::forecastRequestError(const QString &name) +{ + m_ErrorScreen->setCityName(name, AddCityErrorScreen::NotFound); + setCurrentScreen(m_ErrorScreen); } void AddCityTool::citySelected(const QString &city) diff --git a/weather/addcitytool.h b/weather/addcitytool.h index f5f83e6..bc313b0 100644 --- a/weather/addcitytool.h +++ b/weather/addcitytool.h @@ -61,15 +61,17 @@ public: signals: void forecastReceived(const ForecastData &forecast); + void forecastRequestError(const QString &name); private: QString m_city; + int m_reqId; Loading *m_loading; void reset(); private slots: - void forecastResponse(const ForecastData &forecast); + void forecastResponse(int reqId, const ForecastData &forecast); }; class AddCityErrorScreen : public QObject, public QGraphicsPixmapItem @@ -128,6 +130,7 @@ signals: private slots: void errorScreenClosed(); void forecastReceived(const ForecastData &forecast); + void forecastRequestError(const QString &name); void citySelected(const QString &city); }; diff --git a/weather/bootmanager.cpp b/weather/bootmanager.cpp index de2b6ff..08a3738 100644 --- a/weather/bootmanager.cpp +++ b/weather/bootmanager.cpp @@ -17,7 +17,6 @@ BootManager::BootManager(QObject *parent) : QObject(parent) - , m_pendingResponses(0) , m_imagesLoaded(false) { } @@ -25,12 +24,11 @@ BootManager::BootManager(QObject *parent) void BootManager::run() { m_keys = Settings::getCurrentCities(); - ForecastProvider::connectToResponseSignal(this, SLOT(forecastResponse(ForecastData))); + ForecastProvider::connectToResponseSignal(this, SLOT(forecastResponse(int, ForecastData))); foreach (const QString & city, m_keys) { - ForecastProvider::getForecast(city); + m_requests.append(ForecastProvider::getForecast(city, true)); m_data.append(ForecastData()); } - m_pendingResponses = m_keys.count(); PixmapLoader::connectToOnIdleSignal(this, SLOT(pixmapLoaderIsIdle())); int count = ForecastHungItem::loadImages(); count += ForecastRain::loadImages(); @@ -48,15 +46,14 @@ void BootManager::run() PixmapLoader::disconnectReceiver(this); } -void BootManager::forecastResponse(const ForecastData &forecast) +void BootManager::forecastResponse(int reqId, const ForecastData &forecast) { - int idx = m_keys.indexOf(forecast.key()); - if (idx >= 0) { - if (m_data[idx].isNull()) - --m_pendingResponses; - m_data[idx] = forecast; + if (m_requests.removeAll(reqId)) { + int idx = m_keys.indexOf(forecast.key()); + if (idx >= 0) + m_data[idx] = forecast; } - if (!m_pendingResponses) { + if (m_requests.isEmpty()) { ForecastProvider::disconnectReceiver(this); if (m_imagesLoaded) emit ready(); @@ -67,6 +64,6 @@ void BootManager::pixmapLoaderIsIdle() { PixmapLoader::disconnectReceiver(this); m_imagesLoaded = true; - if (!m_pendingResponses) + if (m_requests.isEmpty()) emit ready(); } diff --git a/weather/bootmanager.h b/weather/bootmanager.h index a94e0f1..b061547 100644 --- a/weather/bootmanager.h +++ b/weather/bootmanager.h @@ -18,13 +18,13 @@ signals: void ready(); private slots: - void forecastResponse(const ForecastData &forecast); + void forecastResponse(int reqId, const ForecastData &forecast); void pixmapLoaderIsIdle(); private: QStringList m_keys; QList<ForecastData> m_data; - int m_pendingResponses; + QList<int> m_requests; bool m_imagesLoaded; }; diff --git a/weather/demoforecastsource.cpp b/weather/demoforecastsource.cpp new file mode 100644 index 0000000..db6f5cc --- /dev/null +++ b/weather/demoforecastsource.cpp @@ -0,0 +1,140 @@ +#include "demoforecastsource.h" +#include "forecast.h" + +#include <QTimer> +#include <QFile> +#include <QDebug> + +struct CityInfo +{ + const char * name; + const int lower; + const int temp; + const int upper; + const bool night; +}; + +static const CityInfo cityNames[Forecast::UnknownForecast] = { + {"Araxa", -7, -4, -1, true}, // MostlyCloudy + {"Belo Horizonte", 18, 22, 27, false}, // Cloudy + {"Recife", 25, 31, 34, true}, // MostlySunny + {"Ipojuca", 22, 29, 36, false}, // PartlyCloudy + {"Fortaleza", 31, 34, 36, true}, // Sunny + {"Sao Paulo", -1, 10, 12, false}, // Flurries + {"Uberlandia", -26, -23, -20, false}, // Fog + {"Governador Valadares", 3, 17, 18, true}, // Haze + {"Brasilia", 22, 25, 30, false}, // Sand + {"Florianopolis", 0, 8, 10, true}, // Dust + {"Gravata", -10, -2, 3, false}, // Icy + {"Maceio", 22, 35, 38, true}, // Sleet + {"Porto Alegre", -8, -6, -4, true}, // ChanceOfSleet + {"Batatais", -20, -12, -10, false}, // Snow + {"Ribeirao Preto", -20, -17, 0, true}, // ChanceOfSnow + {"Campinas", 1, 4, 10, false}, // Mist + {"Campo belo", 12, 19, 21, true}, // Rain + {"Sertaozinho", 10, 15, 17, true}, // ChanceOfRain + {"Pirassununga", -15, -12, 2, false}, // Storm + {"Goiania", 7, 13, 18, true}, // ChanceOfStorm + {"Manaus", 35, 41, 44, false}, // Thunderstorm + {"Belem", -14, -13, -5, true} // ChanceOfThunderstorm +}; + +DemoForecastSource::DemoForecastSource(int waitTime, QObject *parent) + : ForecastSource(parent) + , m_waitTime(waitTime) + , m_idSeq(0) +{ + createContentList(); +} + + +int DemoForecastSource::findByLocId(const QString &locId) +{ + if (!locId.isEmpty()) + for (int i = 0; i < m_requests.count(); ++i) + if (m_requests[i].m_locId == locId) + return i; + return -1; +} + +int DemoForecastSource::findByQuery(const QString &query) +{ + if (!query.isEmpty()) + for (int i = 0; i < m_requests.count(); ++i) + if (m_requests[i].m_cityQuery == query) + return i; + return -1; +} + +QString DemoForecastSource::findCity(const QString &name) +{ + QString city = name.toUpper(); + for (int i = 0; i < m_list.count(); ++i) { + if (city == m_list[i].cityName().toUpper()) + return m_list[i].key(); + } + return QString(); +} + +int DemoForecastSource::getForecast(const QString &key, bool locationId) +{ + + int idx = locationId ? findByLocId(key) : findByQuery(key); + if (idx >= 0) + return m_requests[idx].m_reqId; + + QString locId = locationId ? key : findCity(key); + + Request request; + request.m_locId = locId; + request.m_cityQuery = key; + request.m_reqId = getId(); + m_requests.append(request); + if (m_waitTime > 0) + QTimer::singleShot(m_waitTime, this, SLOT(sendResponse())); + else sendResponse(); + return request.m_reqId; +} + +void DemoForecastSource::createContentList() +{ + QFile file(":fake_content.xml"); + if (!file.open(QIODevice::ReadOnly)) { + qWarning() << "cannot open fake content file."; + return; + } + QDomDocument doc; + QString msg; + if (!doc.setContent(&file, &msg)) { + qWarning() << "fake content parse error: " << msg; + return; + } + if (doc.documentElement().childNodes().count() == 0) { + qWarning() << "fake content is empty."; + return; + } + QDomElement root = doc.documentElement(); + for (int i = 0; i < root.childNodes().count(); ++i) { + if (root.childNodes().at(i).isElement()) { + QDomElement element = root.childNodes().at(i).toElement(); + QString id = element.attribute("locId"); + if (!id.isEmpty() && element.childNodes().count() == 1 && + element.childNodes().at(0).isElement()) { + QDomElement child = element.childNodes().at(0).toElement(); + m_list.append(ForecastData(new YahooWeatherResponse(id, child))); + } + } + } +} + +void DemoForecastSource::sendResponse() +{ + if (!m_requests.isEmpty()) { + Request request = m_requests.takeFirst(); + for (int i = 0; i < m_list.count(); ++i) { + if (m_list[i].key() == request.m_locId) + emit forecastReceived(request.m_reqId, m_list[i]); + } + emit forecastReceived(request.m_reqId, ForecastData(0)); + } +} diff --git a/weather/demoforecastsource.h b/weather/demoforecastsource.h new file mode 100644 index 0000000..dbfd715 --- /dev/null +++ b/weather/demoforecastsource.h @@ -0,0 +1,44 @@ +#ifndef DEMOFORECASTSOURCE_H +#define DEMOFORECASTSOURCE_H + +#include "forecastsource.h" +#include "yahooweatherresponse.h" + +#include <QList> + +class DemoForecastSource : public ForecastSource +{ + Q_OBJECT +public: + DemoForecastSource(int waitTime, QObject *parent = 0); + int getForecast(const QString &key, bool locationId); + +private: + + class Request + { + public: + int m_reqId; + QString m_locId; + QString m_cityQuery; + }; + + const int m_waitTime; + QList<Request> m_requests; + QList<ForecastData> m_list; + int m_idSeq; + + void createContentList(); + QString findCity(const QString &name); + + int getId() { return ++m_idSeq; } + + int findByLocId(const QString &locId); + int findByQuery(const QString &query); + +private slots: + void sendResponse(); + +}; + +#endif // DEMOFORECASTSOURCE_H diff --git a/weather/fake_content.xml b/weather/fake_content.xml new file mode 100644 index 0000000..2bff5f0 --- /dev/null +++ b/weather/fake_content.xml @@ -0,0 +1,27 @@ +<fake-content> + <item locId="INDT0001"> + <rss version="2.0" xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"> + <channel> + <lastBuildDate>Mon, 09 Nov 2009 7:56 am PST</lastBuildDate> + <ttl>1440</ttl> + + <yweather:units temperature="C" distance="mi" pressure="in" speed="mph"/> + <yweather:wind chill="46" direction="0" speed="0" /> + <yweather:atmosphere humidity="76" visibility="10" pressure="30.11" rising="1" /> + <yweather:astronomy sunrise="5:41 am" sunset="6:02 pm"/> + <item> + <geo:lat>37.39</geo:lat> + <geo:long>-122.03</geo:long> + <pubDate>Mon, 09 Nov 2009 7:56 am PST</pubDate> + + <yweather:condition text="Mostly Cloudy" code="27" temp="22" date="Mon, 09 Nov 2009 7:56 am PST" /> + <yweather:forecast day="Mon" date="9 Nov 2009" low="18" high="31" text="Mostly Cloudy" code="30" /> + <yweather:forecast day="Tue" date="10 Nov 2009" low="49" high="68" text="Mostly Cloudy" code="28" /> + <guid isPermaLink="false">USCA1116_2009_11_09_7_56_PST</guid> + </item> + <yweather:location city="Mostly Cloudy" region="" country=""/> + + </channel> + </rss> + </item> +</fake-content> diff --git a/weather/forecastdata.cpp b/weather/forecastdata.cpp new file mode 100644 index 0000000..45e2567 --- /dev/null +++ b/weather/forecastdata.cpp @@ -0,0 +1,78 @@ +#include "forecastdata.h" + +struct ConditionTypeData +{ + const int id; + Forecast::ForecastType type; +}; + +static const int ContitionTypeCount = 47; +static const ConditionTypeData ContitionType[ContitionTypeCount] = { + {1, Forecast::Storm}, + {2, Forecast::Thunderstorm}, + {3, Forecast::Thunderstorm}, + {4, Forecast::Thunderstorm}, + {5, Forecast::Sleet}, + {6, Forecast::Rain}, + {7, Forecast::Sleet}, + {8, Forecast::Mist}, + {9, Forecast::Mist}, + {10, Forecast::Rain}, + {11, Forecast::Rain}, + {12, Forecast::Rain}, + {13, Forecast::Flurries}, + {14, Forecast::Flurries}, + {15, Forecast::Snow}, + {16, Forecast::Snow}, + {17, Forecast::Snow}, + {18, Forecast::Sleet}, + {19, Forecast::Haze}, + {20, Forecast::Fog}, + {21, Forecast::Haze}, + {22, Forecast::Fog}, + {23, Forecast::Cloudy}, + {24, Forecast::Cloudy}, + {25, Forecast::Cloudy}, + {26, Forecast::Cloudy}, + {27, Forecast::MostlyCloudy}, + {28, Forecast::MostlyCloudy}, + {29, Forecast::PartlyCloudy}, + {30, Forecast::PartlyCloudy}, + {31, Forecast::Sunny}, + {32, Forecast::Sunny}, + {33, Forecast::MostlySunny}, + {34, Forecast::MostlySunny}, + {35, Forecast::Sleet}, + {36, Forecast::Sunny}, + {37, Forecast::Thunderstorm}, + {38, Forecast::Thunderstorm}, + {39, Forecast::Thunderstorm}, + {40, Forecast::Storm}, + {41, Forecast::Snow}, + {42, Forecast::Sleet}, + {43, Forecast::Snow}, + {44, Forecast::PartlyCloudy}, + {45, Forecast::Thunderstorm}, + {46, Forecast::Sleet}, + {47, Forecast::Thunderstorm} +}; + + +Forecast::ForecastType ForecastData::type() const +{ + if (m_data) { + for (int i = 0; i < ContitionTypeCount; ++i) + if (ContitionType[i].id == m_data->condition().code()) + return ContitionType[i].type; + } + return Forecast::UnknownForecast; +} + +bool ForecastData::night() const +{ + if (!m_data) + return false; + return m_data->condition().date().time() < m_data->astronomy().sunrise() + || m_data->condition().date().time() > m_data->astronomy().sunset(); +} + diff --git a/weather/forecastdata.h b/weather/forecastdata.h index eaf711f..bf5b9cd 100644 --- a/weather/forecastdata.h +++ b/weather/forecastdata.h @@ -3,60 +3,42 @@ #include <QString> #include "forecast.h" +#include "yahooweatherresponse.h" + +#include <QSharedPointer> + +class YahooWeatherResponse; class ForecastData { public: - ForecastData() - : m_error(false) - , m_lower(0) - , m_current(0) - , m_upper(0) - , m_isNull(true) - {} - ForecastData(bool error, Forecast::ForecastType type, bool night, const QString &key, - const QString &cityName, int lower, int current, int upper) - : m_type(type) - , m_night(night) - , m_error(error) - , m_key(key) - , m_cityName(cityName) - , m_lower(lower) - , m_current(current) - , m_upper(upper) - , m_isNull(false) - {} - - Forecast::ForecastType type() const { return m_type; } - bool night() const { return m_night; } + ForecastData() : m_error(false), m_data(0) {} + ForecastData(YahooWeatherResponse *data) : m_error(!data), m_data(data) {} + + Forecast::ForecastType type() const; + bool night() const; bool error() const { return m_error; } - QString key() const { return m_key; } - QString cityName() const { return m_cityName; } - int lower() const { return m_lower; } - int current() const { return m_current; } - int upper() const { return m_upper; } - bool isNull() const { return m_isNull; } + QString key() const { return m_data ? m_data->locationCode() : QString(); } + QString cityName() const { return m_data ? m_data->location().city() : QString(); } + int lower() const { return m_data ? m_data->forecast(0).low() : 0; } + int current() const { return m_data ? m_data->condition().temperature() : 0; } + int upper() const { return m_data ? m_data->forecast(0).high() : 0; } + bool isNull() const { return !m_error && !m_data; } inline bool operator==(const ForecastData &other); bool operator!=(const ForecastData &other) { return !operator==(other); } private: - Forecast::ForecastType m_type; - bool m_night; bool m_error; - QString m_key; - QString m_cityName; - int m_lower; - int m_current; - int m_upper; - bool m_isNull; + + QSharedPointer<YahooWeatherResponse> m_data; }; bool ForecastData::operator==(const ForecastData &other) { - return m_type == other.m_type && m_night == other.m_night && m_error == other.m_error - && m_key == other.m_key && m_cityName == other.m_cityName && m_lower == other.m_lower - && m_current == other.m_current && m_upper == other.m_upper; + return type() == other.type() && night() == other.night() && error() == other.error() + && key() == other.key() && cityName() == other.cityName() && lower() == other.lower() + && current() == other.current() && upper() == other.upper(); } #endif // FORECASTDATA_H diff --git a/weather/forecastprovider.cpp b/weather/forecastprovider.cpp index 94c7341..5bda3fe 100644 --- a/weather/forecastprovider.cpp +++ b/weather/forecastprovider.cpp @@ -4,52 +4,21 @@ #include "forecast.h" -struct CityInfo -{ - const char * name; - const int lower; - const int temp; - const int upper; - const bool night; -}; - -static const CityInfo cityNames[Forecast::UnknownForecast] = { - {"Araxa", -7, -4, -1, true}, // MostlyCloudy - {"Belo Horizonte", 18, 22, 27, false}, // Cloudy - {"Recife", 25, 31, 34, true}, // MostlySunny - {"Ipojuca", 22, 29, 36, false}, // PartlyCloudy - {"Fortaleza", 31, 34, 36, true}, // Sunny - {"Sao Paulo", -1, 10, 12, false}, // Flurries - {"Uberlandia", -26, -23, -20, false}, // Fog - {"Governador Valadares", 3, 17, 18, true}, // Haze - {"Brasilia", 22, 25, 30, false}, // Sand - {"Florianopolis", 0, 8, 10, true}, // Dust - {"Gravata", -10, -2, 3, false}, // Icy - {"Maceio", 22, 35, 38, true}, // Sleet - {"Porto Alegre", -8, -6, -4, true}, // ChanceOfSleet - {"Batatais", -20, -12, -10, false}, // Snow - {"Ribeirao Preto", -20, -17, 0, true}, // ChanceOfSnow - {"Campinas", 1, 4, 10, false}, // Mist - {"Campo belo", 12, 19, 21, true}, // Rain - {"Sertaozinho", 10, 15, 17, true}, // ChanceOfRain - {"Pirassununga", -15, -12, 2, false}, // Storm - {"Goiania", 7, 13, 18, true}, // ChanceOfStorm - {"Manaus", 35, 41, 44, false}, // Thunderstorm - {"Belem", -14, -13, -5, true} // ChanceOfThunderstorm -}; - ForecastProvider::ForecastProvider() + : m_source(0) { } ForecastProvider::~ForecastProvider() { - qDeleteAll(m_requests); } -void ForecastProvider::getForecast(const QString &key, bool refresh) +int ForecastProvider::getForecast(const QString &key, bool locationId) { - instance()->doGetForecast(key, refresh); + ForecastProvider *obj = instance(); + if (obj->m_source) + return obj->m_source->getForecast(key, locationId); + return -1; } ForecastProvider *ForecastProvider::instance() @@ -58,65 +27,28 @@ ForecastProvider *ForecastProvider::instance() return result; } -void ForecastProvider::connectToResponseSignal(QObject *receiver, const char *method) -{ - QObject::connect(instance(), SIGNAL(forecastResponse(ForecastData)), - receiver, method, Qt::QueuedConnection); -} -void ForecastProvider::disconnectReceiver(QObject *receiver) +void ForecastProvider::setForecastSource(ForecastSource *source) { - instance()->disconnect(receiver); -} - -void ForecastProvider::doSendRequest(const QString &key) -{ - qsrand(QTime(0, 0).secsTo(QTime::currentTime()) * qrand()); - m_dumbRequestList.append(key); - QTimer::singleShot((qrand() % 400) + 100, this, SLOT(receiveDumbResponse())); -} - -void ForecastProvider::receiveDumbResponse() -{ - QString key = m_dumbRequestList.takeFirst(); - for (int i = 0; i < Forecast::UnknownForecast; ++i) { - if (key.compare(cityNames[i].name, Qt::CaseInsensitive) == 0) { - ForecastData response(false, Forecast::ForecastType(i), cityNames[i].night, key, - cityNames[i].name, cityNames[i].lower, cityNames[i].temp, - cityNames[i].upper); - forecastReceived(response); - return; - } + ForecastProvider *obj = instance(); + if (obj->m_source) { + disconnect(obj->m_source, SIGNAL(forecastReceived(int, ForecastData)), + obj, SIGNAL(forecastResponse(int, ForecastData))); + obj->m_source->deleteLater(); } - forecastReceived(ForecastData(true, Forecast::UnknownForecast, false, key, "", 0, 0, 0)); + obj->m_source = source; + if (obj->m_source) + connect(obj->m_source, SIGNAL(forecastReceived(int, ForecastData)), + obj, SIGNAL(forecastResponse(int, ForecastData)), Qt::QueuedConnection); } -void ForecastProvider::doGetForecast(const QString &key, bool refresh) +void ForecastProvider::connectToResponseSignal(QObject *receiver, const char *method) { - ForecastRequestInfo *request = 0; - if (!m_requests.contains(key)) { - request = new ForecastRequestInfo(); - m_requests[key] = request; - doSendRequest(key); - return; - } - request = m_requests[key]; - if (!request->m_ready) - return; - if (refresh) { - request->m_ready = false; - doSendRequest(key); - return; - } - emit forecastResponse(request->m_response); + QObject::connect(instance(), SIGNAL(forecastResponse(int, ForecastData)), + receiver, method, Qt::QueuedConnection); } -void ForecastProvider::forecastReceived(const ForecastData &forecast) +void ForecastProvider::disconnectReceiver(QObject *receiver) { - if (!m_requests.contains(forecast.key())) - return; - ForecastRequestInfo *request = m_requests[forecast.key()]; - request->m_ready = true; - request->m_response = forecast; - emit forecastResponse(request->m_response); + instance()->disconnect(receiver); } diff --git a/weather/forecastprovider.h b/weather/forecastprovider.h index 602b773..fcb75dd 100644 --- a/weather/forecastprovider.h +++ b/weather/forecastprovider.h @@ -8,44 +8,30 @@ #include <QMap> #include "forecastdata.h" +#include "forecastsource.h" class ForecastProvider : public QObject { Q_OBJECT public: - static void getForecast(const QString &key, bool refresh = false); + static int getForecast(const QString &key, bool locationId); + static void connectToResponseSignal(QObject *receiver, const char *method); static void disconnectReceiver(QObject *receiver); + static void setForecastSource(ForecastSource *source); + signals: - void forecastResponse(const ForecastData &forecast); + void forecastResponse(int reqId, const ForecastData &forecast); private: - struct ForecastRequestInfo - { - ForecastRequestInfo() : m_ready(false) {} - bool m_ready; - ForecastData m_response; - }; - - QMap<QString, ForecastRequestInfo*> m_requests; + ForecastSource *m_source; ForecastProvider(); ~ForecastProvider(); static ForecastProvider *instance(); - void doGetForecast(const QString &key, bool refresh); - void forecastReceived(const ForecastData &forecast); - - void doSendRequest(const QString &key); - -private: - QStringList m_dumbRequestList; - -private slots: - void receiveDumbResponse(); - }; #endif // FORECASTPROVIDER_H diff --git a/weather/forecastsource.h b/weather/forecastsource.h new file mode 100644 index 0000000..8c7cfbe --- /dev/null +++ b/weather/forecastsource.h @@ -0,0 +1,18 @@ +#ifndef FORECASTSOURCE_H +#define FORECASTSOURCE_H + +#include <QObject> +#include "forecastdata.h" + +class ForecastSource : public QObject +{ + Q_OBJECT +public: + ForecastSource(QObject *parent = 0) : QObject(parent) {} + virtual int getForecast(const QString &key, bool locationId) = 0; + +signals: + void forecastReceived(int id, const ForecastData &data); +}; + +#endif // FORECASTSOURCE_H diff --git a/weather/main.cpp b/weather/main.cpp index e35a7fc..e90a959 100644 --- a/weather/main.cpp +++ b/weather/main.cpp @@ -40,6 +40,14 @@ #include "forecastdata.h" #include "settings.h" +#include "forecastprovider.h" +#include "demoforecastsource.h" +#include "networkforecastsource.h" + +#if defined (Q_OS_SYMBIAN) +#include "sym_iap_util.h" +#endif + int main(int argc, char **argv) { qRegisterMetaType<ForecastData>("ForecastData"); @@ -52,9 +60,12 @@ int main(int argc, char **argv) MainView mainView; // mainView.setFixedSize(windowSize); + ForecastProvider::setForecastSource(new NetworkForecastSource()); + #ifdef Q_OS_SYMBIAN mainView.showFullScreen(); Settings::fixedPortraitOrientation(); + qt_SetDefaultIap(); #else mainView.show(); #endif diff --git a/weather/networkforecastsource.cpp b/weather/networkforecastsource.cpp new file mode 100644 index 0000000..c2a7dbe --- /dev/null +++ b/weather/networkforecastsource.cpp @@ -0,0 +1,237 @@ +#include "networkforecastsource.h" +#include "yahooweatherresponse.h" +#include "xoapweatherresponse.h" + +#include <QUrl> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QDebug> +#include <QBuffer> +#include <QDomDocument> +#include <QDateTime> +#include <QTime> + +#define CITY_NAME_HOST "xoap.weather.com" +#define CITY_NAME_TO_LOCATION_ID_URL "http://" CITY_NAME_HOST "/search/search" +#define CITY_NAME_TO_LOCATION_ID_PARAM "where" + +#define FORECAST_HOST "weather.yahooapis.com" +#define FORECAST_URL "http://" FORECAST_HOST "/forecastrss" +#define FORECAST_LOCATION_PARAM "p" +#define FORECAST_UNIT_PARAM "u" +#define FORECAST_CELSIUS_UNIT "c" + +#define YWEATHER_NS_URI "http://xml.weather.yahoo.com/ns/rss/1.0" +#define YWEATHER_NS_NAME "yweather" + +static int getId() +{ + static int id(0); + return ++id; +} + +// LocationRequestManager + +LocationRequestManager::LocationRequestManager(QObject *parent) + : QObject(parent) +{ + connect(&m_network, SIGNAL(finished(QNetworkReply*)), + this, SLOT(receiveResponse(QNetworkReply*))); +} + +int LocationRequestManager::addRequest(const QString &query) +{ + if (m_requests.contains(query)) + return m_requests[query]; + QUrl url(CITY_NAME_TO_LOCATION_ID_URL); + url.addQueryItem(CITY_NAME_TO_LOCATION_ID_PARAM, query); + m_network.get(QNetworkRequest(url)); + + int result = getId(); + m_requests[query] = result; + return result; +} + +QString LocationRequestManager::readResponse(const QString &query, QNetworkReply *reply) +{ + + if (reply->error() != QNetworkReply::NoError) { + qWarning() << "location id query error: " << query; + return QString(); + } + + QDomDocument doc; + + if (!doc.setContent(reply)) { + qWarning() << "location id parse error: " << query; + return QString(); + } + XoapWeatherResponse reader; + reader.read(doc.documentElement()); + + if (reader.items().count() == 0) { + qWarning() << "empty location id response: " << query; + return QString(); + } + + return reader.items()[0].id(); + +} + +void LocationRequestManager::receiveResponse(QNetworkReply *reply) +{ + const QString query = reply->request().url().queryItemValue(CITY_NAME_TO_LOCATION_ID_PARAM); + + if (query.isNull() || !m_requests.contains(query)) { + qWarning() << "Unexpected location id query response. (query = " << query << ")"; + return; + } + const int reqId = m_requests[query]; + m_requests.remove(query); + + QString locId = readResponse(query, reply); + + if (locId.isEmpty()) + emit locationIdQueryError(reqId, query); + else + emit newLocationId(reqId, locId); +} + +// ForecastRequestmanager + +ForecastRequestmanager::ForecastRequestmanager(QObject *parent) + : QObject(parent) +{ + connect(&m_network, SIGNAL(finished(QNetworkReply*)), + this, SLOT(receiveResponse(QNetworkReply*))); +} + +int ForecastRequestmanager::addRequest(const QString &locId) +{ + if (m_requests.contains(locId)) + return m_requests[locId][0]; + int result = getId(); + doAddRequest(result, locId); + return result; +} + +void ForecastRequestmanager::addRequest(int reqId, const QString &locId) +{ + if (m_requests.contains(locId)) { + m_requests[locId].append(reqId); + return; + } + doAddRequest(reqId, locId); +} + +void ForecastRequestmanager::doAddRequest(int reqId, const QString &locId) +{ + QUrl url(FORECAST_URL); + url.addQueryItem(FORECAST_LOCATION_PARAM, locId); + url.addQueryItem(FORECAST_UNIT_PARAM, FORECAST_CELSIUS_UNIT); + m_network.get(QNetworkRequest (url)); + QList<int> list; + list.append(reqId); + m_requests[locId] = list; +} + +YahooWeatherResponse *ForecastRequestmanager::readResponse(const QString &locId, + QNetworkReply *reply) +{ + if (reply->error() != QNetworkReply::NoError) { + qWarning() << "forecast query error: " << locId; + return 0; + } + + QDomDocument doc; + + if (!doc.setContent(reply)) { + qWarning() << "forecast parse error: " << locId; + return 0; + } + + bool ok = doc.documentElement().childNodes().count() > 0 + && doc.documentElement().childNodes().at(0).isElement(); + if (ok) { + QDomElement element = doc.documentElement().childNodes().at(0).toElement(); + ok = element.elementsByTagName("lastBuildDate").count() == 1; + } + + if (!ok) { + qWarning() << "invalid forecast response: " << locId; + return 0; + } + + return new YahooWeatherResponse(locId, doc.documentElement()); +} + +void ForecastRequestmanager::receiveResponse(QNetworkReply *reply) +{ + const QString locId = reply->request().url().queryItemValue(FORECAST_LOCATION_PARAM); + + if (locId.isNull() || !m_requests.contains(locId)) { + qWarning() << "Unexpected forecast query response. (locId = " << locId << ")"; + return; + } + + const QList<int> list = m_requests[locId]; + m_requests.remove(locId); + + YahooWeatherResponse *result = readResponse(locId, reply); + if (result) + foreach (int reqId, list) { + emit newForecastResponse(reqId, result); + } + else + foreach (int reqId, list) + emit forecastResponseError(reqId); +} + +// NetworkForecastSource + +NetworkForecastSource::NetworkForecastSource(QObject *parent) + : ForecastSource(parent) +{ + connect(&m_locationManager, SIGNAL(newLocationId(int, QString)), + &m_forecastManager, SLOT(addRequest(int, QString))); + + connect(&m_locationManager, SIGNAL(locationIdQueryError(int, QString)), + &m_forecastManager, SLOT(addRequest(int, QString))); + + connect(&m_forecastManager, SIGNAL(forecastResponseError(int)), + this, SLOT(forecastResponseError(int))); + + connect(&m_forecastManager, SIGNAL(newForecastResponse(int, YahooWeatherResponse*)), + this, SLOT(newForecastResponse(int, YahooWeatherResponse*))); +} + +NetworkForecastSource::~NetworkForecastSource() +{ +} + +int NetworkForecastSource::getForecast(const QString &key, bool locationId) +{ + if (locationId) + return m_forecastManager.addRequest(key); + else + return m_locationManager.addRequest(key); +} + +void NetworkForecastSource::newForecastResponse(int reqId, YahooWeatherResponse *forecast) +{ + qDebug() << "----------------------------------------------"; + qDebug() << "forecast response received: " << reqId; + forecast->print(); + qDebug() << "----------------------------------------------"; + emit forecastReceived(reqId, ForecastData(forecast)); +} + +void NetworkForecastSource::forecastResponseError(int reqId) +{ + emit forecastReceived(reqId, ForecastData(0)); +} + + + + + diff --git a/weather/networkforecastsource.h b/weather/networkforecastsource.h new file mode 100644 index 0000000..49f26c5 --- /dev/null +++ b/weather/networkforecastsource.h @@ -0,0 +1,75 @@ +#ifndef NETWORKFORECASTSOURCE_H +#define NETWORKFORECASTSOURCE_H + +#include "forecastdata.h" +#include "forecastsource.h" +#include <QObject> +#include <QMap> +#include <QStringList> +#include <QNetworkAccessManager> + +class LocationRequestManager : public QObject +{ + Q_OBJECT +public: + LocationRequestManager(QObject *parent = 0); + int addRequest(const QString &query); + +signals: + void newLocationId(int reqId, const QString &locId); + void locationIdQueryError(int reqId, const QString &query); + +private slots: + void receiveResponse(QNetworkReply *reply); + +private: + QMap<QString, int> m_requests; + QNetworkAccessManager m_network; + + QString readResponse(const QString &query, QNetworkReply *reply); +}; + +class ForecastRequestmanager : public QObject +{ + Q_OBJECT +public: + ForecastRequestmanager(QObject *parent = 0); + + int addRequest(const QString &locId); + +public slots: + void addRequest(int reqId, const QString &locId); + +signals: + void newForecastResponse(int reqId, YahooWeatherResponse *forecast); + void forecastResponseError(int reqId); + +private slots: + void receiveResponse(QNetworkReply *reply); + +private: + QMap<QString, QList<int> > m_requests; + QNetworkAccessManager m_network; + + YahooWeatherResponse *readResponse(const QString &query, QNetworkReply *locId); + void doAddRequest(int reqId, const QString &locId); +}; + +class NetworkForecastSource : public ForecastSource +{ + Q_OBJECT +public: + NetworkForecastSource(QObject *parent = 0); + ~NetworkForecastSource(); + int getForecast(const QString &key, bool locationId); + +private: + LocationRequestManager m_locationManager; + ForecastRequestmanager m_forecastManager; + +private slots: + void newForecastResponse(int reqId, YahooWeatherResponse *forecast); + void forecastResponseError(int reqId); +}; + +#endif // NETWORKFORECASTSOURCE_H diff --git a/weather/resources.qrc b/weather/resources.qrc index b4a8863..94386d5 100644 --- a/weather/resources.qrc +++ b/weather/resources.qrc @@ -96,5 +96,6 @@ <file>images/weather_elements/scroll.png</file> <file>images/weather_elements/scroll_knob.png</file> <file>images/weather_elements/city_name_background_bigger.png</file> + <file>fake_content.xml</file> </qresource> </RCC> diff --git a/weather/settings.cpp b/weather/settings.cpp index 896c90d..3c926dc 100644 --- a/weather/settings.cpp +++ b/weather/settings.cpp @@ -78,7 +78,6 @@ public: CitySortHelper(const QMap<QString, int> &map) : m_map(map) {} bool operator()(const QString &city1, const QString &city2) { - qDebug() << city1 << m_map[city1] << city2 << m_map[city2] << (m_map[city1] < m_map[city2]); return m_map[city1] < m_map[city2]; } diff --git a/weather/sym_iap_util.h b/weather/sym_iap_util.h new file mode 100644 index 0000000..14df5af --- /dev/null +++ b/weather/sym_iap_util.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QSYM_IAP_UTIL_H +#define QSYM_IAP_UTIL_H + +#include <es_sock.h> +#include <sys/socket.h> +#include <net/if.h> +#include <rconnmon.h> + +QString qt_TDesC2QStringL(const TDesC& aDescriptor) +{ +#ifdef QT_NO_UNICODE + return QString::fromLocal8Bit(aDescriptor.Ptr(), aDescriptor.Length()); +#else + return QString::fromUtf16(aDescriptor.Ptr(), aDescriptor.Length()); +#endif +} + +static void qt_SetDefaultIapL() +{ + TUint count; + TRequestStatus status; + TUint ids[15]; + + RSocketServ serv; + CleanupClosePushL(serv); + + RConnection conn; + CleanupClosePushL(conn); + + RConnectionMonitor monitor; + CleanupClosePushL(monitor); + + monitor.ConnectL(); + monitor.GetConnectionCount(count, status); + User::WaitForRequest(status); + if(status.Int() != KErrNone) { + User::Leave(status.Int()); + } + + TUint numSubConnections; + + if(count > 0) { + for (TInt i = 1; i <= count; i++) { + User::LeaveIfError(monitor.GetConnectionInfo(i, ids[i-1], numSubConnections)); + } + /* + * get IAP value for first active connection + */ + TBuf< 50 > iapName; + monitor.GetStringAttribute(ids[0], 0, KIAPName, iapName, status); + User::WaitForRequest(status); + if (status.Int() != KErrNone) { + User::Leave(status.Int()); + } else { + QString strIapName = qt_TDesC2QStringL(iapName); + struct ifreq ifReq; + strcpy(ifReq.ifr_name, strIapName.toLatin1().data()); + User::LeaveIfError(setdefaultif(&ifReq)); + } + } else { + /* + * no active connections yet + * use IAP dialog to select one + */ + User::LeaveIfError(serv.Connect()); + User::LeaveIfError(conn.Open(serv)); + User::LeaveIfError(conn.Start()); + + _LIT(KIapNameSetting, "IAP\\Name"); + TBuf8<50> iap8Name; + User::LeaveIfError(conn.GetDesSetting(TPtrC(KIapNameSetting), iap8Name)); + iap8Name.ZeroTerminate(); + + conn.Stop(); + + struct ifreq ifReq; + strcpy(ifReq.ifr_name, (char*)iap8Name.Ptr()); + User::LeaveIfError(setdefaultif(&ifReq)); + } + CleanupStack::PopAndDestroy(&monitor); + CleanupStack::PopAndDestroy(&conn); + CleanupStack::PopAndDestroy(&serv); +} + +static int qt_SetDefaultIap() +{ + TRAPD(err, qt_SetDefaultIapL()); + return err; +} + +#endif // QSYM_IAP_UTIL_H diff --git a/weather/weather.ini b/weather/weather.ini index 59f46a6..bfce791 100644 --- a/weather/weather.ini +++ b/weather/weather.ini @@ -1,27 +1,12 @@ [General] -windowSize = @Size(480 864) +windowSize=@Size(360 640) [Cities] -Araxa = 1 -Belo Horizonte = 2 -Recife = 3 -Ipojuca = 4 -Fortaleza = 5 -Sao Paulo = 6 -Uberlandia = 7 -Governador Valadares = 8 -Brasilia = 9 -Florianopolis = 10 -Gravata = 11 -Maceio = 12 -Porto Alegre = 13 -Batatais = 14 -Ribeirao Preto = 15 -Campinas = 16 -Campo belo = 17 -Sertaozinho = 18 -Pirassununga = 19 -Goiania = 20 -Manaus = 21 -Belem = 22 -[Cities] +NOXX0029=0 +ASXX0112=1 +USND0343=2 +UKXX0085=3 +BRXX0309=4 +IDXX0022=5 +CHXX0049=6 +INXX0012=7 diff --git a/weather/weather.pro b/weather/weather.pro index bbbdddf..50d2c0b 100644 --- a/weather/weather.pro +++ b/weather/weather.pro @@ -1,6 +1,7 @@ TEMPLATE = app TARGET = weather -QT += network +QT += network \ + xml target.path = $$PREFIX/bin INSTALLS += target RESOURCES += resources.qrc @@ -50,7 +51,13 @@ HEADERS += mainview.h \ citylist.h \ citymanager.h \ addcitytool.h \ - painttextitem.h + painttextitem.h \ + forecastsource.h \ + demoforecastsource.h \ + networkforecastsource.h \ + yahooweatherresponse.h \ + xoapweatherresponse.h \ + sym_iap_util.h SOURCES += mainview.cpp \ main.cpp \ settings.cpp \ @@ -73,4 +80,9 @@ SOURCES += mainview.cpp \ citylist.cpp \ citymanager.cpp \ addcitytool.cpp \ - painttextitem.cpp + painttextitem.cpp \ + demoforecastsource.cpp \ + networkforecastsource.cpp \ + yahooweatherresponse.cpp \ + xoapweatherresponse.cpp \ + forecastdata.cpp diff --git a/weather/xoapweatherresponse.cpp b/weather/xoapweatherresponse.cpp new file mode 100644 index 0000000..3c520e2 --- /dev/null +++ b/weather/xoapweatherresponse.cpp @@ -0,0 +1,48 @@ +#include "xoapweatherresponse.h" + +#include <QDebug> + +static inline int strToInt(const QString &str, int defaultValue) +{ + bool ok; + int result = str.toInt(&ok); + return ok ? result : defaultValue; +} + +static inline QString getNodeContentText(QDomNode node) +{ + if (node.childNodes().count() == 0 || !node.childNodes().at(0).isText()) + return QString(); + return node.childNodes().at(0).nodeValue(); +} + +void XoapWeatherResponse::Item::read(QDomElement element) +{ + m_id = element.attribute("id"); + m_type = strToInt(element.attribute("type"), 0); + m_texts.clear(); + m_texts << getNodeContentText(element).split(",", QString::SkipEmptyParts); +} + +XoapWeatherResponse::XoapWeatherResponse() +{ +} + +void XoapWeatherResponse::read(QDomElement element) +{ + m_version = element.attribute("ver"); + for (int i = 0; i < element.childNodes().count(); ++i) { + if (element.childNodes().at(i).isElement()) { + Item item; + item.read(element.childNodes().at(i).toElement()); + m_list.append(item); + } + } +} + +void XoapWeatherResponse::print() +{ + qDebug() << m_version << m_list.count() << " items"; + for (int i = 0; i < m_list.count(); ++i) + qDebug() << "item " << i << " : " << m_list[i].id() << m_list[i].type() << m_list[i].texts(); +} diff --git a/weather/xoapweatherresponse.h b/weather/xoapweatherresponse.h new file mode 100644 index 0000000..1fd13cd --- /dev/null +++ b/weather/xoapweatherresponse.h @@ -0,0 +1,43 @@ +#ifndef XOAPWEATHERRESPONSE_H +#define XOAPWEATHERRESPONSE_H + +#include <QString> +#include <QStringList> +#include <QDomElement> +#include <QTime> +#include <QDate> +#include <QDateTime> + +class XoapWeatherResponse +{ +public: + class Item + { + public: + Item() : m_type(0) {} + void read(QDomElement element); + + QStringList texts() const { return m_texts; } + int type() const { return m_type; } + QString id() const { return m_id; } + + private: + QStringList m_texts; + int m_type; + QString m_id; + + }; + XoapWeatherResponse(); + void read(QDomElement element); + void print(); + + QString versionid() const { return m_version; } + const QList<Item> &items() const { return m_list; } + + +private: + QString m_version; + QList<Item> m_list; +}; + +#endif // XOAPWEATHERRESPONSE_H diff --git a/weather/yahooweatherresponse.cpp b/weather/yahooweatherresponse.cpp new file mode 100644 index 0000000..325b632 --- /dev/null +++ b/weather/yahooweatherresponse.cpp @@ -0,0 +1,163 @@ +#include "yahooweatherresponse.h" +#include <QDebug> + +static inline int strToInt(const QString &str, int defaultValue) +{ + bool ok; + int result = str.toInt(&ok); + return ok ? result : defaultValue; +} + +static inline float strToFloat(const QString &str, qreal defaultValue) +{ + bool ok; + float result = str.toFloat(&ok); + return ok ? result : defaultValue; +} + +static inline YahooWeatherResponse::PressureState readPressure(const QString &str) +{ + switch (strToInt(str, 0)) { + case 1: return YahooWeatherResponse::Rising; + case 2: return YahooWeatherResponse::Falling; + default: return YahooWeatherResponse::Steady; + } +} + +static inline QString getNodeContentText(QDomNode node) +{ + if (node.childNodes().count() == 0 || !node.childNodes().at(0).isText()) + return QString(); + return node.childNodes().at(0).nodeValue(); +} + +static const QDateTime readDateTime(const QString &str) +{ + QString text = str.right(str.length() - str.indexOf(',') - 1); + int len = text.indexOf("am", 15); + len = len < 0 ? text.indexOf("pm", 15) : len; + text = text.left(len + 2).trimmed(); + return QDateTime::fromString(text, "dd MMM yyyy h:mm ap"); +} + +YahooWeatherResponse::Location::Location(QDomElement element) + : m_city(element.attribute("city")) + , m_region(element.attribute("region")) + , m_country(element.attribute("country")) +{} + +YahooWeatherResponse::Units::Units(QDomElement element) + : m_temperature(element.attribute("temperature") == "f" ? Fahrenheit : Celsius) + , m_distance(element.attribute("distance") == "mi" ? Miles : Kilometers) + , m_pressure(element.attribute("pressure") == "in" ? PoundsPerSquare : Millibars) + , m_speed(element.attribute("speed") == "mph" ? MilesPerHour : KilometersPerHour) +{} + +YahooWeatherResponse::Wind::Wind(QDomElement element) + : m_chill(strToInt(element.attribute("chill"), 0)) + , m_direction(strToInt(element.attribute("direction"), 0)) + , m_speed(strToInt(element.attribute("speed"), 0)) +{} + +YahooWeatherResponse::Atmosphere::Atmosphere(QDomElement element) + : m_humidity(strToInt(element.attribute("humidity"), 0)) + , m_visibility(strToFloat(element.attribute("visibility"), 0.0) * 100) + , m_pressure(strToFloat(element.attribute("pressure"), 0.0)) + , m_pressureState(readPressure(element.attribute("rising"))) +{} + +YahooWeatherResponse::Astronomy::Astronomy(QDomElement element) + : m_sunrise(QTime::fromString(element.attribute("sunrise"), "h:mm ap")) + , m_sunset(QTime::fromString(element.attribute("sunset"), "h:mm ap")) +{} + +YahooWeatherResponse::Condition::Condition(QDomElement element) + : m_text(element.attribute("text")) + , m_code(strToInt(element.attribute("code"), 3200)) + , m_temperature(strToInt(element.attribute("temp"), 0)) + , m_date(readDateTime(element.attribute("date"))) +{} + +YahooWeatherResponse::Forecast::Forecast(QDomElement element) + : m_date(QDate::fromString(element.attribute("date"), "dd MMM yyyy")) + , m_low(strToInt(element.attribute("low"), 0)) + , m_high(strToInt(element.attribute("high"), 0)) + , m_text(element.attribute("text")) + , m_code(strToInt(element.attribute("code"), 3200)) +{} + +void YahooWeatherResponse::readItem(QDomElement item) +{ + int idx = 0; + for (int i = 0; i < item.childNodes().count(); ++i) { + QDomNode node = item.childNodes().at(i); + if (node.nodeName() == "geo:lat") + m_latitude = strToFloat(getNodeContentText(node), 0.0); + if (node.nodeName() == "geo:long") + m_longitude = strToFloat(getNodeContentText(node), 0.0); + if (node.nodeName() == "pubDate") + m_pubDate = readDateTime(getNodeContentText(node)); + if (node.nodeName() == "yweather:condition") + m_condition = Condition(node.toElement()); + if (node.nodeName() == "yweather:forecast") + m_forecast[idx++] = Forecast(node.toElement()); + idx = idx > 1 ? 0 : idx; + } +} + +YahooWeatherResponse::YahooWeatherResponse(const QString &locationCode, QDomElement element) + : m_locationCode(locationCode) +{ + QDomNodeList channels = element.elementsByTagName("channel"); + if (channels.count() == 0) + return; + QDomElement channel = channels.item(0).toElement(); + for (int i = 0; i < channel.childNodes().count(); ++i) { + QDomNode node = channel.childNodes().at(i); + + if (node.nodeName() == "lastBuildDate") + m_buildDate = readDateTime(getNodeContentText(node)); + + if (node.nodeName() == "ttl") + m_timeToLive = strToInt(getNodeContentText(node), 30); + + if (node.nodeName() == "yweather:location") + m_location = Location(node.toElement()); + + if (node.nodeName() == "yweather:units") + m_units = Units(node.toElement()); + + if (node.nodeName() == "yweather:wind") + m_wind = Wind(node.toElement()); + + if (node.nodeName() == "yweather:atmosphere") + m_atmosphere = Atmosphere(node.toElement()); + + if (node.nodeName() == "yweather:astronomy") + m_astronomy = Astronomy(node.toElement()); + + if (node.nodeName() == "item") + readItem(node.toElement()); + + } +} + +void YahooWeatherResponse::print() +{ + qDebug() << "location code" << m_locationCode; + qDebug() << "buildDate" << m_buildDate; + qDebug() << "timeToLive" << m_timeToLive; + + qDebug() << "latitude" << m_latitude; + qDebug() << "longitude" << m_longitude; + qDebug() << "pubDate" << m_pubDate; + + qDebug() << "astronomy" << m_astronomy.sunrise() << m_astronomy.sunset(); + qDebug() << "atmosphere" << m_atmosphere.humidity() << m_atmosphere.visibility() << m_atmosphere.pressure() << m_atmosphere.pressureState(); + qDebug() << "condition" << m_condition.text() << m_condition.code() << m_condition.temperature() << m_condition.date(); + qDebug() << "forecast" << m_forecast[0].text() << m_forecast[0].code() << m_forecast[0].low() << m_forecast[0].high() << m_forecast[0].date(); + qDebug() << "forecast" << m_forecast[1].text() << m_forecast[1].code() << m_forecast[1].low() << m_forecast[1].high() << m_forecast[1].date(); + qDebug() << "location" << m_location.city() << m_location.region() << m_location.country(); + qDebug() << "units" << m_units.distance() << m_units.pressure() << m_units.speed() << m_units.temperature(); + qDebug() << "wind" << m_wind.chill() << m_wind.direction() << m_wind.speed(); +} diff --git a/weather/yahooweatherresponse.h b/weather/yahooweatherresponse.h new file mode 100644 index 0000000..ac9d20e --- /dev/null +++ b/weather/yahooweatherresponse.h @@ -0,0 +1,181 @@ +#ifndef YAHOOWEATHERRESPONSE_H +#define YAHOOWEATHERRESPONSE_H + +#include <QString> +#include <QDomElement> +#include <QTime> +#include <QDate> +#include <QDateTime> + +class YahooWeatherResponse +{ +public: + enum TemperatureUnit { Fahrenheit, Celsius }; + enum DistanceUnit { Miles, Kilometers }; + enum PressureUnit { PoundsPerSquare, Millibars }; + enum SpeedUnit { MilesPerHour, KilometersPerHour }; + enum PressureState { Steady, Rising, Falling }; + + class Location + { + public: + Location() {}; + Location(QDomElement element); + QString city() const { return m_city; } + QString region() const { return m_region; } + QString country() const { return m_country; } + + private: + QString m_city; + QString m_region; + QString m_country; + }; + + class Units + { + public: + Units() {}; + Units(QDomElement element); + TemperatureUnit temperature() const { return m_temperature; } + DistanceUnit distance() const { return m_distance; } + PressureUnit pressure() const { return m_pressure; } + SpeedUnit speed() const { return m_speed; } + + private: + TemperatureUnit m_temperature; + DistanceUnit m_distance; + PressureUnit m_pressure; + SpeedUnit m_speed; + }; + + class Wind + { + public: + Wind() {}; + Wind(QDomElement element); + int chill() const { return m_chill; } + int direction() const { return m_direction; } + int speed() const { return m_speed; } + + private: + int m_chill; + int m_direction; + int m_speed; + }; + + class Atmosphere + { + public: + Atmosphere() {}; + Atmosphere(QDomElement element); + int humidity() const { return m_humidity; } + float visibility() const { return m_visibility; } + float pressure() const { return m_pressure; } + PressureState pressureState() const { return m_pressureState; } + + private: + int m_humidity; + float m_visibility; + float m_pressure; + PressureState m_pressureState; + }; + + class Astronomy + { + public: + Astronomy() {}; + Astronomy(QDomElement element); + QTime sunrise() const { return m_sunrise; } + QTime sunset() const { return m_sunset; } + + private: + QTime m_sunrise; + QTime m_sunset; + }; + + class Condition + { + public: + Condition() {}; + Condition(QDomElement element); + QString text() const { return m_text; } + int code() const { return m_code; } + int temperature() const { return m_temperature; } + QDateTime date() const { return m_date; } + + private: + QString m_text; + int m_code; + int m_temperature; + QDateTime m_date; + }; + + class Forecast + { + public: + Forecast() {}; + Forecast(QDomElement element); + + QDate date() const { return m_date; } + int low() const { return m_low; } + int high() const { return m_high; } + QString text() const { return m_text; } + int code() const { return m_code; } + + private: + QDate m_date; + int m_low; + int m_high; + QString m_text; + int m_code; + }; + +public: + YahooWeatherResponse(const QString &locationCode, QDomElement element); + + QString locationCode() const { return m_locationCode; } + + Location location() const { return m_location; } + Units units() const { return m_units; } + Wind wind() const { return m_wind; } + Atmosphere atmosphere() const { return m_atmosphere; } + Astronomy astronomy() const { return m_astronomy; } + Condition condition() const { return m_condition; } + Forecast forecast(int idx) const { return m_forecast[idx]; } + + QDateTime buildDate() const { return m_buildDate; } + int timeToLive() const { return m_timeToLive; } + float latitude() const { return m_latitude; } + float longitude() const { return m_longitude; } + QDateTime pubDate() const { return m_pubDate; } + + void print(); + + +private: + QString m_locationCode; + QDateTime m_buildDate; + int m_timeToLive; + float m_latitude; + float m_longitude; + QDateTime m_pubDate; + + Location m_location; + Units m_units; + Wind m_wind; + Atmosphere m_atmosphere; + Astronomy m_astronomy; + Condition m_condition; + Forecast m_forecast[2]; + + void readItem(QDomElement item); + +// <guid isPermaLink="false">USCA1116_2009_11_08_18_56_PST</guid> + + + + + +}; + +#endif // YAHOOWEATHERRESPONSE_H |