16#include "GeoDataDocument.h"
17#include "GeoSceneTextureTileDataset.h"
18#include "GeoSceneTileDataset.h"
19#include "GeoSceneTypes.h"
20#include "GeoSceneVectorTileDataset.h"
21#include "HttpDownloadManager.h"
22#include "MarbleDebug.h"
23#include "MarbleDirs.h"
24#include "ParseRunnerPlugin.h"
25#include "ParsingRunner.h"
33TileLoader::TileLoader(HttpDownloadManager *
const downloadManager,
const PluginManager *pluginManager)
34 : m_pluginManager(pluginManager)
36 qRegisterMetaType<DownloadUsage>(
"DownloadUsage");
37 connect(
this, &TileLoader::tileRequested, downloadManager, &HttpDownloadManager::addJob);
42TileLoader::~TileLoader()
50QImage TileLoader::loadTileImage(GeoSceneTextureTileDataset
const *textureLayer, TileId
const &tileId, DownloadUsage
const usage)
52 QString const fileName = tileFileName(textureLayer, tileId);
54 TileStatus
status = tileStatus(textureLayer, tileId);
59 mDebug() << tileId <<
"StateUptodate";
61 Q_ASSERT(
status == Expired);
62 mDebug() << tileId <<
"StateExpired";
63 triggerDownload(textureLayer, tileId, usage);
66 QImage const image(fileName);
67 if (!image.isNull()) {
76 QImage replacementTile = scaledLowerLevelTile(textureLayer, tileId);
77 Q_ASSERT(!replacementTile.
isNull());
79 triggerDownload(textureLayer, tileId, usage);
81 return replacementTile;
84GeoDataDocument *TileLoader::loadTileVectorData(GeoSceneVectorTileDataset
const *textureLayer, TileId
const &tileId, DownloadUsage
const usage)
88 QString const fileName = tileFileName(textureLayer, tileId);
90 TileStatus
status = tileStatus(textureLayer, tileId);
95 mDebug() << tileId <<
"StateUptodate";
97 Q_ASSERT(
status == Expired);
98 mDebug() << tileId <<
"StateExpired";
99 triggerDownload(textureLayer, tileId, usage);
102 QFile file(fileName);
105 GeoDataDocument *document = openVectorFile(fileName);
112 triggerDownload(textureLayer, tileId, usage);
124void TileLoader::downloadTile(GeoSceneTileDataset
const *tileData, TileId
const &tileId, DownloadUsage
const usage)
126 triggerDownload(tileData, tileId, usage);
129int TileLoader::maximumTileLevel(GeoSceneTileDataset
const &tileData)
133 if (tileData.maximumTileLevel() >= 0) {
134 return tileData.maximumTileLevel();
137 int maximumTileLevel = -1;
138 const QFileInfo themeStr(tileData.themeStr());
139 const QString tilepath = themeStr.isAbsolute() ? themeStr.absoluteFilePath() : MarbleDirs::path(tileData.themeStr());
145 for (; it !=
end; ++it) {
147 const int value = (*it).toInt(&ok, 10);
149 if (ok && value > maximumTileLevel)
150 maximumTileLevel = value;
155 return maximumTileLevel + 1;
158bool TileLoader::baseTilesAvailable(GeoSceneTileDataset
const &tileData)
160 const int levelZeroColumns = tileData.levelZeroColumns();
161 const int levelZeroRows = tileData.levelZeroRows();
167 for (
int column = 0; result && column < levelZeroColumns; ++column) {
168 for (
int row = 0; result && row < levelZeroRows; ++row) {
169 const TileId id(0, 0, column, row);
170 const QString tilepath = tileFileName(&tileData,
id);
173 mDebug() <<
"Base tile " << tileData.relativeTileFileName(
id) <<
" is missing for source dir " << tileData.sourceDir();
181TileLoader::TileStatus TileLoader::tileStatus(GeoSceneTileDataset
const *tileData,
const TileId &tileId)
183 QString const fileName = tileFileName(tileData, tileId);
185 if (!fileInfo.exists()) {
189 const QDateTime lastModified = fileInfo.lastModified();
190 const int expireSecs = tileData->expire();
192 return isExpired ? Expired : Available;
198 Q_ASSERT(components.
size() == 5);
200 QString const origin = components[0];
201 QString const sourceDir = components[1];
202 int const zoomLevel = components[2].toInt();
203 int const tileX = components[3].toInt();
204 int const tileY = components[4].toInt();
206 TileId
const id = TileId(sourceDir, zoomLevel, tileX, tileY);
213 Q_EMIT tileCompleted(
id, tileImage);
217void TileLoader::updateTile(
const QString &fileName,
const QString &idStr)
220 Q_ASSERT(components.
size() == 5);
222 QString const origin = components[0];
223 QString const sourceDir = components[1];
224 int const zoomLevel = components[2].toInt();
225 int const tileX = components[3].toInt();
226 int const tileY = components[4].toInt();
228 TileId
const id = TileId(sourceDir, zoomLevel, tileX, tileY);
230 GeoDataDocument *document = openVectorFile(MarbleDirs::path(fileName));
232 Q_EMIT tileCompleted(
id, document);
237QString TileLoader::tileFileName(GeoSceneTileDataset
const *tileData, TileId
const &tileId)
239 QString const fileName = tileData->relativeTileFileName(tileId);
241 return dirInfo.isAbsolute() ? fileName : MarbleDirs::path(fileName);
244void TileLoader::triggerDownload(GeoSceneTileDataset
const *tileData, TileId
const &
id, DownloadUsage
const usage)
246 if (
id.zoomLevel() > 0) {
247 int minValue = tileData->maximumTileLevel() == -1 ?
id.zoomLevel() : qMin(
id.zoomLevel(), tileData->maximumTileLevel());
248 if (
id.zoomLevel() != qMax(tileData->minimumTileLevel(), minValue)) {
254 QUrl const sourceUrl = tileData->downloadUrl(
id);
255 QString const destFileName = tileData->relativeTileFileName(
id);
258 Q_EMIT tileRequested(sourceUrl, destFileName, idStr, usage);
261QImage TileLoader::scaledLowerLevelTile(
const GeoSceneTextureTileDataset *textureData, TileId
const &
id)
265 int const minimumLevel = textureData->minimumTileLevel();
266 for (
int level = qMax<int>(0,
id.zoomLevel() - 1);
level >= 0; --
level) {
267 if (level > 0 && level < minimumLevel) {
270 int const deltaLevel =
id.zoomLevel() -
level;
272 TileId
const replacementTileId(
id.mapThemeIdHash(), level,
id.x() >> deltaLevel,
id.y() >> deltaLevel);
273 QString const fileName = tileFileName(textureData, replacementTileId);
274 mDebug() <<
"TileLoader::scaledLowerLevelTile"
275 <<
"trying" << fileName;
278 if (level == 0 && toScale.
isNull()) {
279 mDebug() <<
"No level zero tile installed in map theme dir. Falling back to a transparent image for now.";
280 QSize tileSize = textureData->tileSize();
281 Q_ASSERT(!tileSize.isEmpty());
282 toScale = QImage(tileSize, QImage::Format_ARGB32_Premultiplied);
283 toScale.fill(qRgba(0, 0, 0, 0));
288 int const restTileX = id.x() % (1 << deltaLevel);
289 int const restTileY = id.y() % (1 << deltaLevel);
290 int const partWidth = qMax(1, toScale.width() >> deltaLevel);
291 int const partHeight = qMax(1, toScale.height() >> deltaLevel);
292 int const startX = restTileX * partWidth;
293 int const startY = restTileY * partHeight;
294 mDebug() <<
"QImage::copy:" << startX << startY << partWidth << partHeight;
295 QImage const part = toScale.copy(startX, startY, partWidth, partHeight);
296 mDebug() <<
"QImage::scaled:" << toScale.size();
297 return part.scaled(toScale.size());
301 Q_ASSERT_X(
false,
"scaled image",
"level zero image missing");
305GeoDataDocument *TileLoader::openVectorFile(
const QString &fileName)
const
310 const QString completeSuffix = fileInfo.completeSuffix().
toLower();
312 for (
const ParseRunnerPlugin *plugin : std::as_const(plugins)) {
313 QStringList const extensions = plugin->fileExtensions();
315 ParsingRunner *runner = plugin->newRunner();
317 GeoDataDocument *document = runner->parseFile(fileName, UserDocument, error);
318 if (!document && !
error.isEmpty()) {
319 mDebug() << QStringLiteral(
"Failed to open vector tile %1: %2").arg(fileName, error);
326 mDebug() <<
"Unable to open vector tile " << fileName <<
": No suitable plugin registered to parse this file format";
332#include "moc_TileLoader.cpp"
Q_SCRIPTABLE CaptureState status()
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QStringView level(QStringView ifopt)
const QList< QKeySequence > & end()
Binds a QML item to a specific geodetic location in screen coordinates.
DownloadUsage
This enum is used to describe the type of download.
QDateTime currentDateTime()
qint64 secsTo(const QDateTime &other) const const
QStringList entryList(Filters filters, SortFlags sort) const const
bool exists() const const
QImage fromData(QByteArrayView data, const char *format)
bool isNull() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype size() const const
QString arg(Args &&... args) const const
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString toLower() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)