25using namespace Qt::StringLiterals;
28 : mFilename( filename )
36 if ( mFilename.isEmpty() )
39 const int result = mDatabase.open_v2( mFilename, SQLITE_OPEN_READONLY,
nullptr );
40 if ( result != SQLITE_OK )
42 mLastError = mDatabase.errorMessage();
43 QgsDebugError( u
"Can't open MBTiles database: %1"_s.arg( mLastError ) );
51 return bool( mDatabase );
59 if ( !mDeferredIndexCreation )
62 const int result = mDatabase.exec( u
"CREATE UNIQUE INDEX IF NOT EXISTS tile_index ON tiles (zoom_level, tile_column, tile_row);"_s, mLastError );
64 return result == SQLITE_OK;
72 if ( QFile::exists( mFilename ) )
75 int result = mDatabase.open_v2( mFilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
nullptr );
76 if ( result != SQLITE_OK )
78 mLastError = mDatabase.errorMessage();
79 QgsDebugError( u
"Can't create MBTiles database: %1"_s.arg( mLastError ) );
86 mDatabase.exec( u
"PRAGMA journal_mode = WAL;"_s, errorMessage );
87 mDatabase.exec( u
"PRAGMA synchronous = OFF;"_s, errorMessage );
88 mDatabase.exec( u
"PRAGMA temp_store = MEMORY;"_s, errorMessage );
89 mDatabase.exec( u
"PRAGMA cache_size = -64000;"_s, errorMessage );
91 QString sql =
"CREATE TABLE metadata (name text, value text);"
92 "CREATE TABLE tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob);";
93 if ( !deferIndexCreation )
95 sql +=
"CREATE UNIQUE INDEX tile_index on tiles (zoom_level, tile_column, tile_row);";
99 mDeferredIndexCreation =
true;
102 result = mDatabase.exec( sql, mLastError );
103 if ( result != SQLITE_OK )
105 QgsDebugError( u
"Failed to initialize MBTiles database: "_s + mLastError );
116 QgsDebugError( u
"MBTiles database not open: "_s + mFilename );
121 const QString sql = u
"select value from metadata where name='%1'"_s.arg( key );
123 if ( result != SQLITE_OK )
125 mLastError = mDatabase.errorMessage();
126 QgsDebugError( u
"MBTile failed to prepare statement: "_s + sql );
130 if ( preparedStatement.
step() != SQLITE_ROW )
132 mLastError = mDatabase.errorMessage();
133 QgsDebugError( u
"MBTile metadata value not found: "_s + key );
144 QgsDebugError( u
"MBTiles database not open: "_s + mFilename );
151 if ( result != SQLITE_OK )
153 mLastError = mDatabase.errorMessage();
154 QgsDebugError( u
"MBTile failed to prepare statement: "_s + sql );
158 if ( preparedStatement.
step() != SQLITE_DONE )
160 mLastError = mDatabase.errorMessage();
161 QgsDebugError( u
"MBTile metadata value failed to be set: "_s + key );
169 if ( boundsStr.isEmpty() )
171 QStringList boundsArray = boundsStr.split(
',' );
172 if ( boundsArray.count() != 4 )
175 return QgsRectangle( boundsArray[0].toDouble(), boundsArray[1].toDouble(), boundsArray[2].toDouble(), boundsArray[3].toDouble() );
182 QgsDebugError( u
"MBTiles database not open: "_s + mFilename );
187 const QString sql = u
"select tile_data from tiles where zoom_level=%1 and tile_column=%2 and tile_row=%3"_s.arg( z ).arg( x ).arg( y );
189 if ( result != SQLITE_OK )
191 mLastError = mDatabase.errorMessage();
192 QgsDebugError( u
"MBTile failed to prepare statement: "_s + sql );
196 if ( preparedStatement.
step() != SQLITE_ROW )
199 QgsDebugMsgLevel( u
"MBTile not found: z=%1 x=%2 y=%3"_s.arg( z ).arg( x ).arg( y ), 2 );
209 const QByteArray tileBlob =
tileData( z, x, y );
210 if ( !tileImage.loadFromData( tileBlob ) )
212 QgsDebugError( u
"MBTile data failed to load: z=%1 x=%2 y=%3"_s.arg( z ).arg( x ).arg( y ) );
222 QgsDebugError( u
"MBTiles database not open: "_s + mFilename );
227 const QString sql = u
"insert into tiles values (%1, %2, %3, ?)"_s.arg( z ).arg( x ).arg( y );
229 if ( result != SQLITE_OK )
231 mLastError = mDatabase.errorMessage();
232 QgsDebugError( u
"MBTile failed to prepare statement: "_s + sql );
236 sqlite3_bind_blob( preparedStatement.get(), 1, data.constData(), data.size(), SQLITE_TRANSIENT );
238 if ( preparedStatement.
step() != SQLITE_DONE )
240 mLastError = mDatabase.errorMessage();
241 QgsDebugError( u
"MBTile tile failed to be set: %1,%2,%3"_s.arg( z ).arg( x ).arg( y ) );
248 if ( tiles.isEmpty() )
255 QgsDebugError( u
"MBTiles database not open: "_s + mFilename );
259 int result = mDatabase.exec( u
"BEGIN TRANSACTION;"_s, mLastError );
260 if ( result != SQLITE_OK )
262 QgsDebugError( u
"Failed to begin transaction: %1"_s.arg( mLastError ) );
266 const QString sql = u
"INSERT OR REPLACE INTO tiles (zoom_level, tile_column, tile_row, tile_data) VALUES (?, ?, ?, ?)"_s;
268 if ( result != SQLITE_OK )
270 mLastError = mDatabase.errorMessage();
271 QgsDebugError( u
"MBTile failed to prepare statement: %1"_s.arg( sql ) );
272 QString errorMessage;
273 mDatabase.exec( u
"ROLLBACK TRANSACTION;"_s, errorMessage );
277 for (
const TileData &tile : tiles )
279 sqlite3_reset( preparedStatement.get() );
280 sqlite3_clear_bindings( preparedStatement.get() );
282 sqlite3_bind_int( preparedStatement.get(), 1, tile.z );
283 sqlite3_bind_int( preparedStatement.get(), 2, tile.x );
284 sqlite3_bind_int( preparedStatement.get(), 3, tile.y );
285 sqlite3_bind_blob( preparedStatement.get(), 4, tile.data.constData(), tile.data.size(), SQLITE_TRANSIENT );
287 if ( preparedStatement.
step() != SQLITE_DONE )
289 mLastError = mDatabase.errorMessage();
290 QgsDebugError( u
"MBTile tile failed to be set: %1,%2,%3"_s.arg( tile.z ).arg( tile.x ).arg( tile.y ) );
294 result = mDatabase.exec( u
"COMMIT TRANSACTION;"_s, mLastError );
295 if ( result != SQLITE_OK )
297 QgsDebugError( u
"Failed to commit transaction: %1"_s.arg( mLastError ) );
QgsMbTiles(const QString &filename)
Constructs MBTiles reader (but it does not open the file yet).
QString metadataValue(const QString &key) const
Requests metadata value for the given key.
bool open()
Tries to open the file, returns true on success.
QImage tileDataAsImage(int z, int x, int y) const
Returns tile decoded as a raster image (if stored in a known format like JPG or PNG).
bool finalize()
Finalizes the database after writing all tiles.
QByteArray tileData(int z, int x, int y) const
Returns raw tile data for given tile.
void setMetadataValue(const QString &key, const QString &value) const
Sets metadata value for the given key.
bool isOpen() const
Returns whether the MBTiles file is currently opened.
void setTileData(int z, int x, int y, const QByteArray &data) const
Adds tile data for the given tile coordinates.
bool create(bool deferIndexCreation=false)
Creates a new MBTiles file and initializes it with metadata and tiles tables.
QgsRectangle extent() const
Returns bounding box from metadata, given in WGS 84 (if available).
A rectangle specified with double values.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
QByteArray columnAsBlob(int column) const
Returns the column value from the current statement row as raw byte array.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
Struct representing a raw image tile.