presentation/MapTileSource.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QDeadlineTimer> | ||
| 4 | #include <QDir> | ||
| 5 | #include <QHash> | ||
| 6 | #include <QList> | ||
| 7 | #include <QObject> | ||
| 8 | #include <QPointer> | ||
| 9 | #include <QSet> | ||
| 10 | #include <QString> | ||
| 11 | |||
| 12 | class QNetworkAccessManager; | ||
| 13 | class QNetworkReply; | ||
| 14 | |||
| 15 | // Online map tiles for the contacts' world map (Sprint 103 S103.4.10, D-11; | ||
| 16 | // opt-in in Settings › Privacy). Follows the OpenStreetMap tile usage | ||
| 17 | // policy: a valid User-Agent, a disk cache (tiles are kept ≥ 7 days and | ||
| 18 | // re-fetched after 30; the cache is trimmed to `maxCacheBytes`), at most two | ||
| 19 | // requests at a time (setMaxParallel), only the tiles of the current viewport (want() | ||
| 20 | // replaces the wish list and aborts transfers that left it), no prefetching. | ||
| 21 | // | ||
| 22 | // The template is an https URL with {z}, {x} and {y} ("{s}" is not | ||
| 23 | // supported); plain http is accepted only for loopback hosts (tests). | ||
| 24 | // Tiles are fetched by C++ only, never by QML. Nothing is fetched while | ||
| 25 | // `enabled` is false. | ||
| 26 | class MapTileSource final : public QObject { | ||
| 27 | 4 | Q_OBJECT | |
| 28 | |||
| 29 | public: | ||
| 30 | static constexpr int MaxParallel = 2; | ||
| 31 | static constexpr qint64 DefaultCacheBytes = 200LL * 1024 * 1024; | ||
| 32 | static constexpr int RefreshDays = 30; | ||
| 33 | static constexpr qint64 MaxTileBytes = 1024 * 1024; | ||
| 34 | static constexpr int RetryAfterMs = 60000; // a failed tile waits this long | ||
| 35 | |||
| 36 | explicit MapTileSource(QDir cacheRoot, QObject *parent = nullptr); | ||
| 37 | ~MapTileSource() override; | ||
| 38 | |||
| 39 | // https://tile.openstreetmap.org/{z}/{x}/{y}.png | ||
| 40 | static QString defaultTemplate(); | ||
| 41 | // "" when `templateUrl` is usable, else why not (for the settings field). | ||
| 42 | static QString validate(const QString &templateUrl); | ||
| 43 | |||
| 44 | void setEnabled(bool enabled); | ||
| 45 | 5 | bool enabled() const { return m_enabled; } | |
| 46 | void setTemplate(const QString &templateUrl); | ||
| 47 | 5 | QString templateUrl() const { return m_template; } | |
| 48 | void setMaxCacheBytes(qint64 bytes) { m_maxCacheBytes = bytes; } | ||
| 49 | // F103-24: requests at a time for the chosen style (1…4; 2 for OpenStreetMap). | ||
| 50 | 842 | void setMaxParallel(int parallel) { m_maxParallel = qBound(1, parallel, 4); } | |
| 51 | 1 | void setRetryAfterMs(int ms) { m_retryAfterMs = qMax(0, ms); } // test seam | |
| 52 | |||
| 53 | // The cached file of a tile ("" when there is none). | ||
| 54 | QString cachedFile(int zoom, int x, int y) const; | ||
| 55 | // Replaces the wanted tiles ("z/x/y"); cached ones are not fetched again | ||
| 56 | // unless older than RefreshDays. | ||
| 57 | void want(const QStringList &tiles); | ||
| 58 | void clearCache(); | ||
| 59 | qint64 cacheBytes() const; | ||
| 60 | |||
| 61 | // Tests and the network spy (G20). | ||
| 62 | 34 | int requestsStarted() const { return m_requestsStarted; } | |
| 63 | 66 | int activeRequests() const { return int(m_active.size()); } | |
| 64 | QNetworkAccessManager *network() const { return m_network; } | ||
| 65 | |||
| 66 | signals: | ||
| 67 | // A tile was stored ("z/x/y"). | ||
| 68 | void tileReady(const QString &tile); | ||
| 69 | // The last request failed (offline, server error); cleared by the next success. | ||
| 70 | void unavailableChanged(bool unavailable); | ||
| 71 | |||
| 72 | private: | ||
| 73 | QString fileOf(const QString &tile) const; | ||
| 74 | QDir serverDir() const; | ||
| 75 | bool fresh(const QString &tile) const; | ||
| 76 | void pump(); | ||
| 77 | void finished(QNetworkReply *reply, const QString &tile); | ||
| 78 | void trimCache(); | ||
| 79 | void abortAll(const QStringList &tiles); | ||
| 80 | |||
| 81 | QDir m_cacheRoot; | ||
| 82 | QNetworkAccessManager *m_network = nullptr; | ||
| 83 | bool m_enabled = false; | ||
| 84 | QString m_template; | ||
| 85 | qint64 m_maxCacheBytes = DefaultCacheBytes; | ||
| 86 | int m_maxParallel = MaxParallel; | ||
| 87 | QStringList m_wanted; // in viewport order | ||
| 88 | QHash<QString, QPointer<QNetworkReply>> m_active; // tile → reply | ||
| 89 | // S106-246: a failed tile is asked for again once its delay passed (it | ||
| 90 | // used to stay failed for the whole session). | ||
| 91 | QHash<QString, QDeadlineTimer> m_failed; | ||
| 92 | int m_retryAfterMs = RetryAfterMs; | ||
| 93 | int m_requestsStarted = 0; | ||
| 94 | int m_storedSinceTrim = 0; | ||
| 95 | bool m_unavailable = false; | ||
| 96 | }; | ||
| 97 |