summaryrefslogtreecommitdiffstats
path: root/examples/positioning/weatherinfo/appmodel.cpp
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@digia.com>2013-11-18 10:24:19 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-11-18 11:53:15 +0100
commit02e88e8349b4c516c855f2f373838a29e9d3e1bb (patch)
treed439538d57af42be14e22377d347eebdc3973dae /examples/positioning/weatherinfo/appmodel.cpp
parent898f292736ced9ae83e7fad2da3006196dfaa8c7 (diff)
Reorder example dir structure according to Qt convention
Causes undesirable example structure in Qt release package Task-number: QTBUG-34907 Change-Id: I47e2205aa6faef388792146b36cf6ae6cae863da Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Diffstat (limited to 'examples/positioning/weatherinfo/appmodel.cpp')
-rw-r--r--examples/positioning/weatherinfo/appmodel.cpp497
1 files changed, 497 insertions, 0 deletions
diff --git a/examples/positioning/weatherinfo/appmodel.cpp b/examples/positioning/weatherinfo/appmodel.cpp
new file mode 100644
index 00000000..d0a37265
--- /dev/null
+++ b/examples/positioning/weatherinfo/appmodel.cpp
@@ -0,0 +1,497 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "appmodel.h"
+
+#include <qgeopositioninfosource.h>
+#include <qgeosatelliteinfosource.h>
+#include <qnmeapositioninfosource.h>
+#include <qgeopositioninfo.h>
+#include <qnetworkconfigmanager.h>
+#include <qnetworksession.h>
+
+#include <QSignalMapper>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QStringList>
+#include <QUrlQuery>
+#include <QElapsedTimer>
+
+/*
+ *This application uses http://openweathermap.org/api
+ **/
+
+#define ZERO_KELVIN 273.15
+
+WeatherData::WeatherData(QObject *parent) :
+ QObject(parent)
+{
+}
+
+WeatherData::WeatherData(const WeatherData &other) :
+ QObject(0),
+ m_dayOfWeek(other.m_dayOfWeek),
+ m_weather(other.m_weather),
+ m_weatherDescription(other.m_weatherDescription),
+ m_temperature(other.m_temperature)
+{
+}
+
+QString WeatherData::dayOfWeek() const
+{
+ return m_dayOfWeek;
+}
+
+/*!
+ * The icon value is based on OpenWeatherMap.org icon set. For details
+ * see http://bugs.openweathermap.org/projects/api/wiki/Weather_Condition_Codes
+ *
+ * e.g. 01d ->sunny day
+ *
+ * The icon string will be translated to
+ * http://openweathermap.org/img/w/01d.png
+ */
+QString WeatherData::weatherIcon() const
+{
+ return m_weather;
+}
+
+QString WeatherData::weatherDescription() const
+{
+ return m_weatherDescription;
+}
+
+QString WeatherData::temperature() const
+{
+ return m_temperature;
+}
+
+void WeatherData::setDayOfWeek(const QString &value)
+{
+ m_dayOfWeek = value;
+ emit dataChanged();
+}
+
+void WeatherData::setWeatherIcon(const QString &value)
+{
+ m_weather = value;
+ emit dataChanged();
+}
+
+void WeatherData::setWeatherDescription(const QString &value)
+{
+ m_weatherDescription = value;
+ emit dataChanged();
+}
+
+void WeatherData::setTemperature(const QString &value)
+{
+ m_temperature = value;
+ emit dataChanged();
+}
+
+class AppModelPrivate
+{
+public:
+ QGeoPositionInfoSource *src;
+ QGeoCoordinate coord;
+ QString city;
+ QNetworkAccessManager *nam;
+ QNetworkSession *ns;
+ WeatherData now;
+ QList<WeatherData*> forecast;
+ QQmlListProperty<WeatherData> *fcProp;
+ QSignalMapper *geoReplyMapper;
+ QSignalMapper *weatherReplyMapper, *forecastReplyMapper;
+ bool ready;
+ bool useGps;
+ QElapsedTimer throttle;
+
+ AppModelPrivate() :
+ src(NULL),
+ nam(NULL),
+ ns(NULL),
+ fcProp(NULL),
+ ready(false),
+ useGps(true)
+ {}
+};
+
+static void forecastAppend(QQmlListProperty<WeatherData> *prop, WeatherData *val)
+{
+ Q_UNUSED(val);
+ Q_UNUSED(prop);
+}
+
+static WeatherData *forecastAt(QQmlListProperty<WeatherData> *prop, int index)
+{
+ AppModelPrivate *d = static_cast<AppModelPrivate*>(prop->data);
+ return d->forecast.at(index);
+}
+
+static int forecastCount(QQmlListProperty<WeatherData> *prop)
+{
+ AppModelPrivate *d = static_cast<AppModelPrivate*>(prop->data);
+ return d->forecast.size();
+}
+
+static void forecastClear(QQmlListProperty<WeatherData> *prop)
+{
+ static_cast<AppModelPrivate*>(prop->data)->forecast.clear();
+}
+
+//! [0]
+AppModel::AppModel(QObject *parent) :
+ QObject(parent),
+ d(new AppModelPrivate)
+{
+//! [0]
+ d->fcProp = new QQmlListProperty<WeatherData>(this, d,
+ forecastAppend,
+ forecastCount,
+ forecastAt,
+ forecastClear);
+
+ d->geoReplyMapper = new QSignalMapper(this);
+ d->weatherReplyMapper = new QSignalMapper(this);
+ d->forecastReplyMapper = new QSignalMapper(this);
+
+ connect(d->geoReplyMapper, SIGNAL(mapped(QObject*)),
+ this, SLOT(handleGeoNetworkData(QObject*)));
+ connect(d->weatherReplyMapper, SIGNAL(mapped(QObject*)),
+ this, SLOT(handleWeatherNetworkData(QObject*)));
+ connect(d->forecastReplyMapper, SIGNAL(mapped(QObject*)),
+ this, SLOT(handleForecastNetworkData(QObject*)));
+
+//! [1]
+ // make sure we have an active network session
+ d->nam = new QNetworkAccessManager(this);
+
+ QNetworkConfigurationManager ncm;
+ d->ns = new QNetworkSession(ncm.defaultConfiguration(), this);
+ connect(d->ns, SIGNAL(opened()), this, SLOT(networkSessionOpened()));
+ // the session may be already open. if it is, run the slot directly
+ if (d->ns->isOpen())
+ this->networkSessionOpened();
+ // tell the system we want network
+ d->ns->open();
+}
+//! [1]
+
+AppModel::~AppModel()
+{
+ d->ns->close();
+ if (d->src)
+ d->src->stopUpdates();
+ delete d;
+}
+
+//! [2]
+void AppModel::networkSessionOpened()
+{
+ d->src = QGeoPositionInfoSource::createDefaultSource(this);
+
+ if (d->src) {
+ d->useGps = true;
+ connect(d->src, SIGNAL(positionUpdated(QGeoPositionInfo)),
+ this, SLOT(positionUpdated(QGeoPositionInfo)));
+ d->src->startUpdates();
+ } else {
+ d->useGps = false;
+ d->city = "Brisbane";
+ emit cityChanged();
+ this->refreshWeather();
+ }
+}
+//! [2]
+
+//! [3]
+void AppModel::positionUpdated(QGeoPositionInfo gpsPos)
+{
+ d->coord = gpsPos.coordinate();
+
+ if (!(d->useGps))
+ return;
+
+ QString latitude, longitude;
+ longitude.setNum(d->coord.longitude());
+ latitude.setNum(d->coord.latitude());
+//! [3]
+
+ //don't update more often then once a minute
+ //to keep load on server low
+ if (d->throttle.isValid() && d->throttle.elapsed() < 1000*10 ) {
+ return;
+ }
+ d->throttle.restart();
+
+ QUrl url("http://api.openweathermap.org/data/2.5/weather");
+ QUrlQuery query;
+ query.addQueryItem("lat", latitude);
+ query.addQueryItem("lon", longitude);
+ query.addQueryItem("mode", "json");
+ url.setQuery(query);
+
+ QNetworkReply *rep = d->nam->get(QNetworkRequest(url));
+ // connect up the signal right away
+ d->geoReplyMapper->setMapping(rep, rep);
+ connect(rep, SIGNAL(finished()),
+ d->geoReplyMapper, SLOT(map()));
+}
+
+void AppModel::handleGeoNetworkData(QObject *replyObj)
+{
+ QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(replyObj);
+ if (!networkReply)
+ return;
+
+ if (!networkReply->error()) {
+ //convert coordinates to city name
+ QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll());
+
+ QJsonObject jo = document.object();
+ QJsonValue jv = jo.value(QStringLiteral("name"));
+
+ const QString city = jv.toString();
+ if (city != d->city) {
+ if (!d->throttle.isValid())
+ d->throttle.start();
+ d->city = city;
+ emit cityChanged();
+ refreshWeather();
+ }
+ }
+ networkReply->deleteLater();
+}
+
+void AppModel::refreshWeather()
+{
+ QUrl url("http://api.openweathermap.org/data/2.5/weather");
+ QUrlQuery query;
+
+ query.addQueryItem("q", d->city);
+ query.addQueryItem("mode", "json");
+ url.setQuery(query);
+
+ QNetworkReply *rep = d->nam->get(QNetworkRequest(url));
+ // connect up the signal right away
+ d->weatherReplyMapper->setMapping(rep, rep);
+ connect(rep, SIGNAL(finished()),
+ d->weatherReplyMapper, SLOT(map()));
+}
+
+static QString niceTemperatureString(double t)
+{
+ return QString::number(qRound(t-ZERO_KELVIN)) + QChar(0xB0);
+}
+
+void AppModel::handleWeatherNetworkData(QObject *replyObj)
+{
+ QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(replyObj);
+ if (!networkReply)
+ return;
+
+ if (!networkReply->error()) {
+ foreach (WeatherData *inf, d->forecast)
+ delete inf;
+ d->forecast.clear();
+
+ QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll());
+
+ if (document.isObject()) {
+ QJsonObject obj = document.object();
+ QJsonObject tempObject;
+ QJsonValue val;
+
+ if (obj.contains(QStringLiteral("weather"))) {
+ val = obj.value(QStringLiteral("weather"));
+ QJsonArray weatherArray = val.toArray();
+ val = weatherArray.at(0);
+ tempObject = val.toObject();
+ d->now.setWeatherDescription(tempObject.value(QStringLiteral("description")).toString());
+ d->now.setWeatherIcon(tempObject.value("icon").toString());
+ }
+ if (obj.contains(QStringLiteral("main"))) {
+ val = obj.value(QStringLiteral("main"));
+ tempObject = val.toObject();
+ val = tempObject.value(QStringLiteral("temp"));
+ d->now.setTemperature(niceTemperatureString(val.toDouble()));
+ }
+ }
+ }
+ networkReply->deleteLater();
+
+ //retrieve the forecast
+ QUrl url("http://api.openweathermap.org/data/2.5/forecast/daily");
+ QUrlQuery query;
+
+ query.addQueryItem("q", d->city);
+ query.addQueryItem("mode", "json");
+ query.addQueryItem("cnt", "5");
+ url.setQuery(query);
+
+ QNetworkReply *rep = d->nam->get(QNetworkRequest(url));
+ // connect up the signal right away
+ d->forecastReplyMapper->setMapping(rep, rep);
+ connect(rep, SIGNAL(finished()), d->forecastReplyMapper, SLOT(map()));
+}
+
+void AppModel::handleForecastNetworkData(QObject *replyObj)
+{
+ QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(replyObj);
+ if (!networkReply)
+ return;
+
+ if (!networkReply->error()) {
+ QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll());
+
+ QJsonObject jo;
+ QJsonValue jv;
+ QJsonObject root = document.object();
+ jv = root.value(QStringLiteral("list"));
+ if (!jv.isArray())
+ qWarning() << "Invalid forecast object";
+ QJsonArray ja = jv.toArray();
+ //we need 4 days of forecast -> first entry is today
+ if (ja.count() != 5)
+ qWarning() << "Invalid forecast object";
+
+ QString data;
+ for (int i = 1; i<ja.count(); i++) {
+ WeatherData *forecastEntry = new WeatherData();
+
+ //min/max temperature
+ QJsonObject subtree = ja.at(i).toObject();
+ jo = subtree.value(QStringLiteral("temp")).toObject();
+ jv = jo.value(QStringLiteral("min"));
+ data.clear();
+ data += niceTemperatureString(jv.toDouble());
+ data += QChar('/');
+ jv = jo.value(QStringLiteral("max"));
+ data += niceTemperatureString(jv.toDouble());
+ forecastEntry->setTemperature(data);
+
+ //get date
+ jv = subtree.value(QStringLiteral("dt"));
+ QDateTime dt = QDateTime::fromMSecsSinceEpoch((qint64)jv.toDouble()*1000);
+ forecastEntry->setDayOfWeek(dt.date().toString(QStringLiteral("ddd")));
+
+ //get icon
+ QJsonArray weatherArray = subtree.value(QStringLiteral("weather")).toArray();
+ jo = weatherArray.at(0).toObject();
+ forecastEntry->setWeatherIcon(jo.value(QStringLiteral("icon")).toString());
+
+ //get description
+ forecastEntry->setWeatherDescription(jo.value(QStringLiteral("description")).toString());
+
+ d->forecast.append(forecastEntry);
+ }
+
+ if (!(d->ready)) {
+ d->ready = true;
+ emit readyChanged();
+ }
+
+ emit weatherChanged();
+ }
+ networkReply->deleteLater();
+}
+
+bool AppModel::hasValidCity() const
+{
+ return (!(d->city.isEmpty()) && d->city.size() > 1 && d->city != "");
+}
+
+bool AppModel::hasValidWeather() const
+{
+ return hasValidCity() && (!(d->now.weatherIcon().isEmpty()) &&
+ (d->now.weatherIcon().size() > 1) &&
+ d->now.weatherIcon() != "");
+}
+
+WeatherData *AppModel::weather() const
+{
+ return &(d->now);
+}
+
+QQmlListProperty<WeatherData> AppModel::forecast() const
+{
+ return *(d->fcProp);
+}
+
+bool AppModel::ready() const
+{
+ return d->ready;
+}
+
+bool AppModel::hasSource() const
+{
+ return (d->src != NULL);
+}
+
+bool AppModel::useGps() const
+{
+ return d->useGps;
+}
+
+void AppModel::setUseGps(bool value)
+{
+ d->useGps = value;
+ if (value) {
+ d->city = "";
+ d->throttle.invalidate();
+ emit cityChanged();
+ emit weatherChanged();
+ }
+ emit useGpsChanged();
+}
+
+QString AppModel::city() const
+{
+ return d->city;
+}
+
+void AppModel::setCity(const QString &value)
+{
+ d->city = value;
+ emit cityChanged();
+ refreshWeather();
+}