7#include "TileCreator.h"
11#include <QApplication>
19#include "MarbleDebug.h"
20#include "MarbleDirs.h"
21#include "MarbleGlobal.h"
22#include "TileLoaderHelper.h"
27class TileCreatorPrivate
30 TileCreatorPrivate(TileCreatorSource *source,
const QString &dem,
const QString &targetDir =
QString())
32 , m_targetDir(targetDir)
34 , m_tileFormat(QStringLiteral(
"jpg"))
60 TileCreatorSource *m_source;
63class TileCreatorSourceImage :
public TileCreatorSource
66 explicit TileCreatorSourceImage(
const QString &sourcePath)
67 : m_sourceImage(
QImage(sourcePath))
72 QSize fullImageSize()
const override
74 if (m_sourceImage.
size().
width() > 21600 || m_sourceImage.
height() > 10800) {
75 qDebug(
"Install map too large!");
78 return m_sourceImage.
size();
81 QImage tile(
int n,
int m,
int maxTileLevel)
override
83 int mmax = TileLoaderHelper::levelToColumn(defaultLevelZeroColumns, maxTileLevel);
84 int nmax = TileLoaderHelper::levelToRow(defaultLevelZeroRows, maxTileLevel);
86 int imageHeight = m_sourceImage.
height();
87 int imageWidth = m_sourceImage.
width();
92 bool needsScaling = (imageWidth != 2 * nmax * (int)(c_defaultTileSize) || imageHeight != nmax * (int)(c_defaultTileSize));
95 mDebug() <<
"Image Size doesn't match 2*n*TILEWIDTH x n*TILEHEIGHT geometry. Scaling ...";
97 int stdImageWidth = 2 * nmax * c_defaultTileSize;
98 if (stdImageWidth == 0)
99 stdImageWidth = 2 * c_defaultTileSize;
101 int stdImageHeight = nmax * c_defaultTileSize;
102 if (stdImageWidth != imageWidth) {
104 << QStringLiteral(
"TileCreator::createTiles() The size of the final image will measure %1 x %2 pixels").arg(stdImageWidth).arg(stdImageHeight);
109 if (m_cachedRowNum == n) {
113 QRect sourceRowRect(0, (
int)((qreal)(n * imageHeight) / (qreal)(nmax)), imageWidth, (
int)((qreal)(imageHeight) / (qreal)(nmax)));
115 row = m_sourceImage.
copy(sourceRowRect);
120 QSize destSize(stdImageWidth, c_defaultTileSize);
129 mDebug() <<
"Read-Error! Null QImage!";
133 QImage tile = row.
copy(m * stdImageWidth / mmax, 0, c_defaultTileSize, c_defaultTileSize);
147 , d(new TileCreatorPrivate(nullptr, dem, targetDir))
150 mDebug() <<
"Prefix: " << sourceDir <<
"installmap:" << installMap;
157 sourcePath = sourceDir +
QLatin1Char(
'/') + installMap;
158 mDebug() <<
"Trying absolute path*:" << sourcePath;
164 mDebug() <<
"Creating tiles from*: " << sourcePath;
166 d->m_source =
new TileCreatorSourceImage(sourcePath);
168 if (d->m_targetDir.isNull())
171 setTerminationEnabled(
true);
174TileCreator::TileCreator(TileCreatorSource *source,
const QString &dem,
const QString &targetDir)
176 , d(new TileCreatorPrivate(source, dem, targetDir))
178 setTerminationEnabled(
true);
181TileCreator::~TileCreator()
186void TileCreator::cancelTileCreation()
188 d->m_cancelled =
true;
191void TileCreator::run()
193 if (d->m_resume && d->m_tileFormat ==
QLatin1StringView(
"jpg") && d->m_tileQuality != 100) {
194 qWarning() <<
"Resuming jpegs is only supported with tileQuality 100";
201 mDebug() <<
"Installing tiles to: " << d->m_targetDir;
204 for (
int cnt = 0; cnt <= 255; ++cnt) {
205 grayScalePalette.
insert(cnt, qRgb(cnt, cnt, cnt));
208 QSize fullImageSize = d->m_source->fullImageSize();
209 int imageWidth = fullImageSize.
width();
210 int imageHeight = fullImageSize.
height();
212 mDebug() << QStringLiteral(
"TileCreator::createTiles() image dimensions %1 x %2").arg(imageWidth).arg(imageHeight);
214 if (imageWidth < 1 || imageHeight < 1) {
215 qDebug(
"Invalid imagemap!");
220 float approxMaxTileLevel = std::log(imageWidth / (2.0 * c_defaultTileSize)) / std::log(2.0);
222 int maxTileLevel = 0;
223 if (approxMaxTileLevel ==
int(approxMaxTileLevel))
224 maxTileLevel =
static_cast<int>(approxMaxTileLevel);
226 maxTileLevel =
static_cast<int>(approxMaxTileLevel + 1);
228 if (maxTileLevel < 0) {
229 mDebug() << QStringLiteral(
"TileCreator::createTiles(): Invalid Maximum Tile Level: %1").arg(maxTileLevel);
231 mDebug() <<
"Maximum Tile Level: " << maxTileLevel;
240 int totalTileCount = 0;
242 while (tileLevel <= maxTileLevel) {
243 totalTileCount += (TileLoaderHelper::levelToRow(defaultLevelZeroRows, tileLevel) * TileLoaderHelper::levelToColumn(defaultLevelZeroColumns, tileLevel));
247 mDebug() << totalTileCount <<
" tiles to be created in total.";
249 int mmax = TileLoaderHelper::levelToColumn(defaultLevelZeroColumns, maxTileLevel);
250 int nmax = TileLoaderHelper::levelToRow(defaultLevelZeroRows, maxTileLevel);
253 int percentCompleted = 0;
254 int createdTilesCount = 0;
258 QString dirName(d->m_targetDir + QStringLiteral(
"%1").arg(maxTileLevel));
259 if (!
QDir(dirName).exists())
262 for (
int n = 0; n < nmax; ++n) {
263 QString dirName(d->m_targetDir + QStringLiteral(
"%1/%2").arg(maxTileLevel).arg(n, tileDigits, 10,
QLatin1Char(
'0')));
264 if (!
QDir(dirName).exists())
268 for (
int n = 0; n < nmax; ++n) {
269 for (
int m = 0; m < mmax; ++m) {
270 mDebug() <<
"** tile" << m <<
"x" << n;
275 tileName = d->m_targetDir
276 + QStringLiteral(
"%1/%2/%2_%3.%4")
280 .
arg(d->m_tileFormat);
286 QImage tile = d->m_source->tile(n, m, maxTileLevel);
289 mDebug() <<
"Read-Error! Null QImage!";
297 bool ok = tile.
save(tileName, d->m_tileFormat.toLatin1().data(), d->m_tileFormat ==
QLatin1StringView(
"jpg") ? 100 : d->m_tileQuality);
299 mDebug() <<
"Error while writing Tile: " << tileName;
301 mDebug() << tileName <<
"size" <<
QFile(tileName).
size();
304 QImage writtenTile(tileName);
305 Q_ASSERT(writtenTile.size() == tile.
size());
306 for (
int i = 0; i < writtenTile.size().width(); ++i) {
307 for (
int j = 0; j < writtenTile.size().height(); ++j) {
308 if (writtenTile.pixel(i, j) != tile.
pixel(i, j)) {
309 unsigned int pixel = tile.
pixel(i, j);
310 unsigned int writtenPixel = writtenTile.pixel(i, j);
311 qWarning() <<
"***** pixel" << i << j <<
"is off by" << (pixel - writtenPixel) <<
"pixel" << pixel <<
"writtenPixel"
313 QByteArray baPixel((
char *)&pixel,
sizeof(
unsigned int));
314 qWarning() <<
"pixel" << baPixel.size() <<
"0x" << baPixel.toHex();
315 QByteArray baWrittenPixel((
char *)&writtenPixel,
sizeof(
unsigned int));
316 qWarning() <<
"writtenPixel" << baWrittenPixel.size() <<
"0x" << baWrittenPixel.toHex();
324 percentCompleted = (int)(90 * (qreal)(createdTilesCount) / (qreal)(totalTileCount));
327 mDebug() <<
"percentCompleted" << percentCompleted;
328 Q_EMIT progress(percentCompleted);
332 mDebug() <<
"tileLevel: " << maxTileLevel <<
" successfully created.";
334 tileLevel = maxTileLevel;
339 while (tileLevel > 0) {
342 int nmaxit = TileLoaderHelper::levelToRow(defaultLevelZeroRows, tileLevel);
344 for (
int n = 0; n < nmaxit; ++n) {
345 QString dirName(d->m_targetDir + QStringLiteral(
"%1/%2").arg(tileLevel).arg(n, tileDigits, 10,
QLatin1Char(
'0')));
348 if (!
QDir(dirName).exists())
351 int mmaxit = TileLoaderHelper::levelToColumn(defaultLevelZeroColumns, tileLevel);
352 for (
int m = 0; m < mmaxit; ++m) {
356 QString newTileName = d->m_targetDir
357 + QStringLiteral(
"%1/%2/%2_%3.%4")
361 .
arg(d->m_tileFormat);
366 tileName = d->m_targetDir
367 + QStringLiteral(
"%1/%2/%2_%3.%4")
371 .
arg(d->m_tileFormat);
372 QImage img_topleft(tileName);
374 tileName = d->m_targetDir
375 + QStringLiteral(
"%1/%2/%2_%3.%4")
379 .
arg(d->m_tileFormat);
380 QImage img_topright(tileName);
382 tileName = d->m_targetDir
383 + QStringLiteral(
"%1/%2/%2_%3.%4")
387 .
arg(d->m_tileFormat);
388 QImage img_bottomleft(tileName);
390 tileName = d->m_targetDir
391 + QStringLiteral(
"%1/%2/%2_%3.%4")
395 .
arg(d->m_tileFormat);
396 QImage img_bottomright(tileName);
398 QSize const expectedSize(c_defaultTileSize, c_defaultTileSize);
399 if (img_topleft.size() != expectedSize || img_topright.size() != expectedSize || img_bottomleft.size() != expectedSize
400 || img_bottomright.size() != expectedSize) {
401 mDebug() <<
"Tile write failure. Missing write permissions?";
402 Q_EMIT progress(100);
405 QImage tile = img_topleft;
411 for (uint y = 0; y < c_defaultTileSize / 2; ++y) {
413 const uchar *srcLine = img_topleft.scanLine(2 * y);
414 for (uint x = 0; x < c_defaultTileSize / 2; ++x)
415 destLine[x] = srcLine[2 * x];
417 for (uint y = 0; y < c_defaultTileSize / 2; ++y) {
419 const uchar *srcLine = img_topright.scanLine(2 * y);
420 for (uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x)
421 destLine[x] = srcLine[2 * (x - c_defaultTileSize / 2)];
423 for (uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y) {
425 const uchar *srcLine = img_bottomleft.scanLine(2 * (y - c_defaultTileSize / 2));
426 for (uint x = 0; x < c_defaultTileSize / 2; ++x)
427 destLine[x] = srcLine[2 * x];
429 for (uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y) {
431 const uchar *srcLine = img_bottomright.scanLine(2 * (y - c_defaultTileSize / 2));
432 for (uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x)
433 destLine[x] = srcLine[2 * (x - c_defaultTileSize / 2)];
446 for (uint y = 0; y < c_defaultTileSize / 2; ++y) {
447 destLine = (QRgb *)tile.
scanLine(y);
448 const QRgb *srcLine = (QRgb *)img_topleft.scanLine(2 * y);
449 for (uint x = 0; x < c_defaultTileSize / 2; ++x)
450 destLine[x] = srcLine[2 * x];
452 for (uint y = 0; y < c_defaultTileSize / 2; ++y) {
453 destLine = (QRgb *)tile.
scanLine(y);
454 const QRgb *srcLine = (QRgb *)img_topright.scanLine(2 * y);
455 for (uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x)
456 destLine[x] = srcLine[2 * (x - c_defaultTileSize / 2)];
458 for (uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y) {
459 destLine = (QRgb *)tile.
scanLine(y);
460 const QRgb *srcLine = (QRgb *)img_bottomleft.scanLine(2 * (y - c_defaultTileSize / 2));
461 for (uint x = 0; x < c_defaultTileSize / 2; ++x)
462 destLine[x] = srcLine[2 * x];
464 for (uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y) {
465 destLine = (QRgb *)tile.
scanLine(y);
466 const QRgb *srcLine = (QRgb *)img_bottomright.scanLine(2 * (y - c_defaultTileSize / 2));
467 for (uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x)
468 destLine[x] = srcLine[2 * (x - c_defaultTileSize / 2)];
472 mDebug() << newTileName;
476 bool ok = tile.
save(newTileName, d->m_tileFormat.toLatin1().data(), d->m_tileFormat ==
QLatin1StringView(
"jpg") ? 100 : d->m_tileQuality);
478 mDebug() <<
"Error while writing Tile: " << newTileName;
481 percentCompleted = (int)(90 * (qreal)(createdTilesCount) / (qreal)(totalTileCount));
484 Q_EMIT progress(percentCompleted);
485 mDebug() <<
"percentCompleted" << percentCompleted;
488 mDebug() <<
"tileLevel: " << tileLevel <<
" successfully created.";
490 mDebug() <<
"Tile creation completed.";
494 int savedTilesCount = 0;
497 while (tileLevel <= maxTileLevel) {
498 int nmaxit = TileLoaderHelper::levelToRow(defaultLevelZeroRows, tileLevel);
499 for (
int n = 0; n < nmaxit; ++n) {
500 int mmaxit = TileLoaderHelper::levelToColumn(defaultLevelZeroColumns, tileLevel);
501 for (
int m = 0; m < mmaxit; ++m) {
507 tileName = d->m_targetDir
508 + QStringLiteral(
"%1/%2/%2_%3.%4")
512 .
arg(d->m_tileFormat);
517 ok = tile.
save(tileName, d->m_tileFormat.toLatin1().data(), d->m_tileQuality);
520 mDebug() <<
"Error while writing Tile: " << tileName;
522 percentCompleted = 90 + (int)(9 * (qreal)(savedTilesCount) / (qreal)(totalTileCount));
523 Q_EMIT progress(percentCompleted);
524 mDebug() <<
"percentCompleted" << percentCompleted;
534 percentCompleted = 100;
535 Q_EMIT progress(percentCompleted);
537 mDebug() <<
"percentCompleted: " << percentCompleted;
540void TileCreator::setTileFormat(
const QString &format)
542 d->m_tileFormat = format;
545QString TileCreator::tileFormat()
const
547 return d->m_tileFormat;
550void TileCreator::setTileQuality(
int quality)
552 d->m_tileQuality = quality;
555int TileCreator::tileQuality()
const
557 return d->m_tileQuality;
560void TileCreator::setResume(
bool resume)
562 d->m_resume = resume;
565bool TileCreator::resume()
const
570void TileCreator::setVerifyExactResult(
bool verify)
575bool TileCreator::verifyExactResult()
const
582#include "moc_TileCreator.cpp"
KIOCORE_EXPORT MkpathJob * mkpath(const QUrl &url, const QUrl &baseUrl=QUrl(), JobFlags flags=DefaultFlags)
Binds a QML item to a specific geodetic location in screen coordinates.
bool exists() const const
bool isAbsolutePath(const QString &path)
bool exists() const const
virtual qint64 size() const const override
QImage copy(const QRect &rectangle) const const
bool isNull() const const
QRgb pixel(const QPoint &position) const const
bool save(QIODevice *device, const char *format, int quality) const const
QImage scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
void setColorTable(const QList< QRgb > &colors)
iterator insert(const_iterator before, parameter_type value)
QString arg(Args &&... args) const const
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const