File indexing completed on 2024-03-24 15:23:34

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2016 Dennis Nienhüser <nienhueser@kde.org>
0004 //
0005 
0006 #include "MbTileWriter.h"
0007 
0008 #include <QDebug>
0009 #include <QSqlDatabase>
0010 #include <QSqlError>
0011 
0012 #include <iostream>
0013 #include <iomanip>
0014 
0015 namespace Marble
0016 {
0017 
0018 MbTileWriter::MbTileWriter(const QString &filename, const QString &extension) :
0019     m_overwriteTiles(true),
0020     m_reportProgress(true),
0021     m_tileCounter(0),
0022     m_commitInterval(10000)
0023 {
0024     bool const exists = QFileInfo(filename).exists();
0025 
0026     QSqlDatabase database = QSqlDatabase::addDatabase( "QSQLITE" );
0027     database.setDatabaseName( filename );
0028     if ( !database.open() ) {
0029         qCritical() << "Failed to connect to database";
0030         return;
0031     }
0032 
0033     if (!exists) {
0034         execQuery("PRAGMA application_id = 0x4d504258"); // MBTiles tileset, see https://www.sqlite.org/src/artifact?ci=trunk&filename=magic.txt
0035         execQuery("CREATE TABLE tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob);");
0036         execQuery("CREATE UNIQUE INDEX tile_index ON tiles(zoom_level, tile_column, tile_row);");
0037 
0038         execQuery("CREATE TABLE metadata (name text, value text);");
0039         setMetaData("name", "Marble Vector OSM");
0040         setMetaData("type", "baselayer");
0041         setMetaData("version", "1.0");
0042         setMetaData("description", "A global roadmap created by the OpenStreetMap (OSM) project");
0043         setMetaData("format", extension);
0044         setMetaData("attribution", "Data from <a href=\"https://openstreetmap.org/\">OpenStreetMap</a> and <a href=\"https://www.naturalearthdata.com/\">Natural Earth</a> contributors");
0045     }
0046     execQuery("BEGIN TRANSACTION");
0047 }
0048 
0049 MbTileWriter::~MbTileWriter()
0050 {
0051     execQuery("END TRANSACTION");
0052     if (m_reportProgress) {
0053         std::cout << std::endl;
0054     }
0055 }
0056 
0057 void MbTileWriter::setOverwriteTiles(bool overwrite)
0058 {
0059     m_overwriteTiles = overwrite;
0060 }
0061 
0062 void MbTileWriter::setReportProgress(bool report)
0063 {
0064     m_reportProgress = report;
0065 }
0066 
0067 void MbTileWriter::setCommitInterval(int interval)
0068 {
0069     m_commitInterval = interval;
0070 }
0071 
0072 void MbTileWriter::addTile(const QFileInfo &file, qint32 x, qint32 y, qint32 z)
0073 {
0074     if (!m_overwriteTiles && hasTile(x, y, z)) {
0075         if (m_reportProgress) {
0076             std::cout << " Skipping existing " << z << '/' << x << '/' << y << '\r';
0077             std::cout.flush();
0078         }
0079         return;
0080     }
0081 
0082     if (m_reportProgress && m_tileCounter % 500 == 0) {
0083         std::cout << "Tile " << std::right << std::setw(10) << m_tileCounter << ": ";
0084         std::cout << "Adding " << z << '/' << x << '/' << y << '\r';
0085         std::cout.flush();
0086     }
0087 
0088     QFile tileContent(file.absoluteFilePath());
0089     tileContent.open(QFile::ReadOnly);
0090     addTile(&tileContent, x, y, z);
0091 }
0092 
0093 void MbTileWriter::addTile(QIODevice *device, qint32 x, qint32 y, qint32 z)
0094 {
0095     ++m_tileCounter;
0096     if (m_commitInterval > 0 && m_tileCounter % m_commitInterval == 0) {
0097         execQuery("END TRANSACTION");
0098         execQuery("BEGIN TRANSACTION");
0099     }
0100 
0101     QSqlQuery query;
0102     query.prepare( "INSERT OR REPLACE INTO tiles"
0103                    " (zoom_level, tile_column, tile_row, tile_data)"
0104                    " VALUES (?, ?, ?, ?)" );
0105     query.addBindValue(z);
0106     query.addBindValue(x);
0107     query.addBindValue(y);
0108     query.addBindValue(device->readAll());
0109     execQuery(query);
0110 }
0111 
0112 bool MbTileWriter::hasTile(qint32 x, qint32 y, qint32 z) const
0113 {
0114     QSqlQuery query;
0115     query.prepare( "SELECT EXISTS(SELECT 1 FROM tiles"
0116                    " WHERE zoom_level=? AND tile_column=? AND tile_row=?);");
0117     query.addBindValue(z);
0118     query.addBindValue(x);
0119     query.addBindValue(y);
0120     query.exec();
0121     if (query.lastError().isValid()) {
0122         qCritical() << "Problems occurred when executing the query" << query.executedQuery();
0123         qCritical() << "SQL error: " << query.lastError();
0124     } else {
0125         if (query.next()) {
0126             return query.value(0).toBool();
0127         }
0128     }
0129     return false;
0130 }
0131 
0132 void MbTileWriter::execQuery( const QString &query ) const
0133 {
0134     QSqlQuery sqlQuery( query );
0135     if ( sqlQuery.lastError().isValid() ) {
0136         qCritical() << "Problems occurred when executing the query" << query;
0137         qCritical() << "SQL error: " << sqlQuery.lastError();
0138     }
0139 }
0140 
0141 void MbTileWriter::execQuery( QSqlQuery &query ) const
0142 {
0143     query.exec();
0144     if ( query.lastError().isValid() ) {
0145         qCritical() << "Problems occurred when executing the query" << query.executedQuery();
0146         qCritical() << "SQL error: " << query.lastError();
0147     }
0148 }
0149 
0150 void MbTileWriter::setMetaData(const QString &name, const QString &value)
0151 {
0152     QSqlQuery query;
0153     query.prepare("INSERT INTO metadata (name, value) VALUES (?, ?)");
0154     query.addBindValue(name);
0155     query.addBindValue(value);
0156     execQuery(query);
0157 }
0158 
0159 }