summaryrefslogtreecommitdiff
path: root/examples/positioning/weatherinfo/appmodel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/positioning/weatherinfo/appmodel.cpp')
-rw-r--r--examples/positioning/weatherinfo/appmodel.cpp146
1 files changed, 123 insertions, 23 deletions
diff --git a/examples/positioning/weatherinfo/appmodel.cpp b/examples/positioning/weatherinfo/appmodel.cpp
index 63c00cb3..0d603b43 100644
--- a/examples/positioning/weatherinfo/appmodel.cpp
+++ b/examples/positioning/weatherinfo/appmodel.cpp
@@ -53,6 +53,7 @@
#include <QGeoPositionInfoSource>
#include <QGeoPositionInfo>
+#include <QGeoCircle>
#include <QLoggingCategory>
Q_LOGGING_CATEGORY(requestsLog, "wapp.requests")
@@ -133,6 +134,92 @@ void WeatherData::setTemperature(const QString &value)
emit dataChanged();
}
+/*
+ The class is used as a cache for the weather information.
+ It contains a map to cache weather for cities.
+ The gps location is cached separately.
+
+ For the coordiante search we do not compare the coordinate directly, but
+ check if it's within a circle of 3 km radius (we assume that the weather
+ does not really change within that radius).
+
+ The cache returns a pair with empty location and weather data if no data
+ is found, or if the data is outdated.
+*/
+class WeatherDataCache
+{
+public:
+ WeatherDataCache() = default;
+
+ using WeatherDataPair = QPair<QString, QList<WeatherInfo>>;
+
+ WeatherDataPair getWeatherData(const QString &name) const;
+ WeatherDataPair getWeatherData(const QGeoCoordinate &coordinate) const;
+
+ void addCacheElement(const LocationInfo &location, const QList<WeatherInfo> &info);
+
+ static bool isCacheResultValid(const WeatherDataPair &result);
+
+private:
+ struct CacheItem
+ {
+ qint64 m_cacheTime;
+ QList<WeatherInfo> m_weatherData;
+ };
+
+ QMap<QString, CacheItem> m_cityCache;
+
+ QGeoCoordinate m_gpsLocation;
+ QString m_gpsName;
+ CacheItem m_gpsData;
+
+ static const qint64 kCacheTimeoutInterval = 3600; // 1 hour
+ static const int kCircleRadius = 3000; // 3 km
+};
+
+WeatherDataCache::WeatherDataPair WeatherDataCache::getWeatherData(const QString &name) const
+{
+ if (m_cityCache.contains(name)) {
+ const qint64 currentTime = QDateTime::currentSecsSinceEpoch();
+ const auto &item = m_cityCache.value(name);
+ if (currentTime - item.m_cacheTime < kCacheTimeoutInterval)
+ return qMakePair(name, item.m_weatherData);
+ }
+ return qMakePair(QString(), QList<WeatherInfo>());
+}
+
+WeatherDataCache::WeatherDataPair WeatherDataCache::getWeatherData(const QGeoCoordinate &coordinate) const
+{
+ if (m_gpsLocation.isValid() && !m_gpsName.isEmpty()) {
+ const QGeoCircle area(m_gpsLocation, kCircleRadius);
+ if (area.contains(coordinate)) {
+ const qint64 currentTime = QDateTime::currentSecsSinceEpoch();
+ if (currentTime - m_gpsData.m_cacheTime < kCacheTimeoutInterval)
+ return qMakePair(m_gpsName, m_gpsData.m_weatherData);
+ }
+ }
+ return qMakePair(QString(), QList<WeatherInfo>());
+}
+
+void WeatherDataCache::addCacheElement(const LocationInfo &location, const QList<WeatherInfo> &info)
+{
+ // It it expected that we have valid QGeoCoordinate only when the weather
+ // is received based on coordinates.
+ const qint64 currentTime = QDateTime::currentSecsSinceEpoch();
+ if (location.m_coordinate.isValid()) {
+ m_gpsLocation = location.m_coordinate;
+ m_gpsName = location.m_name;
+ m_gpsData = { currentTime, info };
+ } else {
+ m_cityCache[location.m_name] = { currentTime, info };
+ }
+}
+
+bool WeatherDataCache::isCacheResultValid(const WeatherDataCache::WeatherDataPair &result)
+{
+ return !result.first.isEmpty() && !result.second.isEmpty();
+}
+
class AppModelPrivate
{
public:
@@ -144,24 +231,10 @@ public:
QQmlListProperty<WeatherData> *fcProp = nullptr;
bool ready = false;
bool useGps = true;
+ WeatherDataCache m_dataCache;
OpenWeatherMapBackend m_openWeatherBackend;
-
- void requestWeatherByCoordinates();
- void requestWeatherByCity();
};
-void AppModelPrivate::requestWeatherByCoordinates()
-{
- // TODO - add support for weather data cache
- m_openWeatherBackend.requestWeatherInfo(coord);
-}
-
-void AppModelPrivate::requestWeatherByCity()
-{
- // TODO - add support for weather data cache
- m_openWeatherBackend.requestWeatherInfo(city);
-}
-
static void forecastAppend(QQmlListProperty<WeatherData> *prop, WeatherData *val)
{
Q_UNUSED(val);
@@ -213,7 +286,7 @@ AppModel::AppModel(QObject *parent) :
d->useGps = false;
d->city = "Brisbane";
emit cityChanged();
- d->requestWeatherByCity();
+ requestWeatherByCity();
}
}
//! [1]
@@ -238,7 +311,7 @@ void AppModel::positionUpdated(QGeoPositionInfo gpsPos)
if (!d->useGps)
return;
- d->requestWeatherByCoordinates();
+ requestWeatherByCoordinates();
}
//! [2]
@@ -255,7 +328,7 @@ void AppModel::positionError(QGeoPositionInfoSource::Error e)
d->useGps = false;
d->city = "Brisbane";
emit cityChanged();
- d->requestWeatherByCity();
+ requestWeatherByCity();
}
void AppModel::refreshWeather()
@@ -265,15 +338,22 @@ void AppModel::refreshWeather()
return;
}
qCDebug(requestsLog) << "refreshing weather";
- d->requestWeatherByCity();
+ requestWeatherByCity();
+}
+
+void AppModel::handleWeatherData(const LocationInfo &location,
+ const QList<WeatherInfo> &weatherDetails)
+{
+ if (applyWeatherData(location.m_name, weatherDetails))
+ d->m_dataCache.addCacheElement(location, weatherDetails);
}
-void AppModel::handleWeatherData(const QString &city, const QList<WeatherInfo> &weatherDetails)
+bool AppModel::applyWeatherData(const QString &city, const QList<WeatherInfo> &weatherDetails)
{
// Check that we didn't get outdated weather data. The city should match,
// if only we do not use GPS.
if (city != d->city && !d->useGps)
- return;
+ return false;
if (city != d->city && d->useGps) {
d->city = city;
@@ -305,6 +385,26 @@ void AppModel::handleWeatherData(const QString &city, const QList<WeatherInfo> &
}
emit weatherChanged();
+
+ return true;
+}
+
+void AppModel::requestWeatherByCoordinates()
+{
+ const auto cacheResult = d->m_dataCache.getWeatherData(d->coord);
+ if (WeatherDataCache::isCacheResultValid(cacheResult))
+ applyWeatherData(cacheResult.first, cacheResult.second);
+ else
+ d->m_openWeatherBackend.requestWeatherInfo(d->coord);
+}
+
+void AppModel::requestWeatherByCity()
+{
+ const auto cacheResult = d->m_dataCache.getWeatherData(d->city);
+ if (WeatherDataCache::isCacheResultValid(cacheResult))
+ applyWeatherData(cacheResult.first, cacheResult.second);
+ else
+ d->m_openWeatherBackend.requestWeatherInfo(d->city);
}
bool AppModel::hasValidCity() const
@@ -354,7 +454,7 @@ void AppModel::setUseGps(bool value)
// if we already have a valid GPS position, do not wait until it
// updates, but query the city immediately
if (d->coord.isValid())
- d->requestWeatherByCoordinates();
+ requestWeatherByCoordinates();
}
emit useGpsChanged();
}
@@ -368,5 +468,5 @@ void AppModel::setCity(const QString &value)
{
d->city = value;
emit cityChanged();
- d->requestWeatherByCity();
+ requestWeatherByCity();
}