QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsmbtiles.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmbtiles.cpp
3  --------------------------------------
4  Date : January 2020
5  Copyright : (C) 2020 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsmbtiles.h"
17 
18 #include "qgslogger.h"
19 #include "qgsrectangle.h"
20 
21 #include <QFile>
22 #include <QImage>
23 
24 QgsMbTiles::QgsMbTiles( const QString &filename )
25  : mFilename( filename )
26 {
27 }
28 
30 {
31  if ( mDatabase )
32  return true; // already opened
33 
34  const sqlite3_database_unique_ptr database;
35  const int result = mDatabase.open_v2( mFilename, SQLITE_OPEN_READONLY, nullptr );
36  if ( result != SQLITE_OK )
37  {
38  QgsDebugMsg( QStringLiteral( "Can't open MBTiles database: %1" ).arg( database.errorMessage() ) );
39  return false;
40  }
41  return true;
42 }
43 
44 bool QgsMbTiles::isOpen() const
45 {
46  return bool( mDatabase );
47 }
48 
50 {
51  if ( mDatabase )
52  return false;
53 
54  if ( QFile::exists( mFilename ) )
55  return false;
56 
57  const sqlite3_database_unique_ptr database;
58  int result = mDatabase.open_v2( mFilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
59  if ( result != SQLITE_OK )
60  {
61  QgsDebugMsg( QStringLiteral( "Can't create MBTiles database: %1" ).arg( database.errorMessage() ) );
62  return false;
63  }
64 
65  const QString sql = \
66  "CREATE TABLE metadata (name text, value text);" \
67  "CREATE TABLE tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob);" \
68  "CREATE UNIQUE INDEX tile_index on tiles (zoom_level, tile_column, tile_row);";
69  QString errorMessage;
70  result = mDatabase.exec( sql, errorMessage );
71  if ( result != SQLITE_OK )
72  {
73  QgsDebugMsg( QStringLiteral( "Failed to initialize MBTiles database: " ) + errorMessage );
74  return false;
75  }
76 
77  return true;
78 }
79 
80 QString QgsMbTiles::metadataValue( const QString &key ) const
81 {
82  if ( !mDatabase )
83  {
84  QgsDebugMsg( QStringLiteral( "MBTiles database not open: " ) + mFilename );
85  return QString();
86  }
87 
88  int result;
89  const QString sql = QStringLiteral( "select value from metadata where name='%1'" ).arg( key );
90  sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
91  if ( result != SQLITE_OK )
92  {
93  QgsDebugMsg( QStringLiteral( "MBTile failed to prepare statement: " ) + sql );
94  return QString();
95  }
96 
97  if ( preparedStatement.step() != SQLITE_ROW )
98  {
99  QgsDebugMsg( QStringLiteral( "MBTile metadata value not found: " ) + key );
100  return QString();
101  }
102 
103  return preparedStatement.columnAsText( 0 );
104 }
105 
106 void QgsMbTiles::setMetadataValue( const QString &key, const QString &value ) const
107 {
108  if ( !mDatabase )
109  {
110  QgsDebugMsg( QStringLiteral( "MBTiles database not open: " ) + mFilename );
111  return;
112  }
113 
114  int result;
115  const QString sql = QStringLiteral( "insert into metadata values (%1, %2)" ).arg( QgsSqliteUtils::quotedValue( key ), QgsSqliteUtils::quotedValue( value ) );
116  sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
117  if ( result != SQLITE_OK )
118  {
119  QgsDebugMsg( QStringLiteral( "MBTile failed to prepare statement: " ) + sql );
120  return;
121  }
122 
123  if ( preparedStatement.step() != SQLITE_DONE )
124  {
125  QgsDebugMsg( QStringLiteral( "MBTile metadata value failed to be set: " ) + key );
126  return;
127  }
128 }
129 
131 {
132  const QString boundsStr = metadataValue( "bounds" );
133  if ( boundsStr.isEmpty() )
134  return QgsRectangle();
135  QStringList boundsArray = boundsStr.split( ',' );
136  if ( boundsArray.count() != 4 )
137  return QgsRectangle();
138 
139  return QgsRectangle( boundsArray[0].toDouble(), boundsArray[1].toDouble(),
140  boundsArray[2].toDouble(), boundsArray[3].toDouble() );
141 }
142 
143 QByteArray QgsMbTiles::tileData( int z, int x, int y ) const
144 {
145  if ( !mDatabase )
146  {
147  QgsDebugMsg( QStringLiteral( "MBTiles database not open: " ) + mFilename );
148  return QByteArray();
149  }
150 
151  int result;
152  const QString sql = QStringLiteral( "select tile_data from tiles where zoom_level=%1 and tile_column=%2 and tile_row=%3" ).arg( z ).arg( x ).arg( y );
153  sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
154  if ( result != SQLITE_OK )
155  {
156  QgsDebugMsg( QStringLiteral( "MBTile failed to prepare statement: " ) + sql );
157  return QByteArray();
158  }
159 
160  if ( preparedStatement.step() != SQLITE_ROW )
161  {
162  // this is not entirely unexpected -- user may have just requested a tile outside of the extent of the mbtiles package
163  QgsDebugMsgLevel( QStringLiteral( "MBTile not found: z=%1 x=%2 y=%3" ).arg( z ).arg( x ).arg( y ), 2 );
164  return QByteArray();
165  }
166 
167  return preparedStatement.columnAsBlob( 0 );
168 }
169 
170 QImage QgsMbTiles::tileDataAsImage( int z, int x, int y ) const
171 {
172  QImage tileImage;
173  const QByteArray tileBlob = tileData( z, x, y );
174  if ( !tileImage.loadFromData( tileBlob ) )
175  {
176  QgsDebugMsg( QStringLiteral( "MBTile data failed to load: z=%1 x=%2 y=%3" ).arg( z ).arg( x ).arg( y ) );
177  return QImage();
178  }
179  return tileImage;
180 }
181 
182 void QgsMbTiles::setTileData( int z, int x, int y, const QByteArray &data ) const
183 {
184  if ( !mDatabase )
185  {
186  QgsDebugMsg( QStringLiteral( "MBTiles database not open: " ) + mFilename );
187  return;
188  }
189 
190  int result;
191  const QString sql = QStringLiteral( "insert into tiles values (%1, %2, %3, ?)" ).arg( z ).arg( x ).arg( y );
192  sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
193  if ( result != SQLITE_OK )
194  {
195  QgsDebugMsg( QStringLiteral( "MBTile failed to prepare statement: " ) + sql );
196  return;
197  }
198 
199  sqlite3_bind_blob( preparedStatement.get(), 1, data.constData(), data.size(), SQLITE_TRANSIENT );
200 
201  if ( preparedStatement.step() != SQLITE_DONE )
202  {
203  QgsDebugMsg( QStringLiteral( "MBTile tile failed to be set: %1,%2,%3" ).arg( z ).arg( x ).arg( y ) );
204  return;
205  }
206 }
QgsMbTiles::QgsMbTiles
QgsMbTiles(const QString &filename)
Constructs MBTiles reader (but it does not open the file yet)
Definition: qgsmbtiles.cpp:24
QgsMbTiles::tileDataAsImage
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)
Definition: qgsmbtiles.cpp:170
qgsrectangle.h
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
sqlite3_database_unique_ptr::prepare
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
Definition: qgssqliteutils.cpp:99
QgsMbTiles::isOpen
bool isOpen() const
Returns whether the MBTiles file is currently opened.
Definition: qgsmbtiles.cpp:44
QgsMbTiles::open
bool open()
Tries to open the file, returns true on success.
Definition: qgsmbtiles.cpp:29
sqlite3_statement_unique_ptr::columnAsBlob
QByteArray columnAsBlob(int column) const
Returns the column value from the current statement row as raw byte array.
Definition: qgssqliteutils.cpp:66
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
sqlite3_database_unique_ptr::errorMessage
QString errorMessage() const
Returns the most recent error message encountered by the database.
Definition: qgssqliteutils.cpp:94
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
sqlite3_statement_unique_ptr::step
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
Definition: qgssqliteutils.cpp:41
qgsmbtiles.h
QgsMbTiles::setMetadataValue
void setMetadataValue(const QString &key, const QString &value) const
Sets metadata value for the given key.
Definition: qgsmbtiles.cpp:106
QgsSqliteUtils::quotedValue
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
Definition: qgssqliteutils.cpp:266
QgsMbTiles::tileData
QByteArray tileData(int z, int x, int y) const
Returns raw tile data for given tile.
Definition: qgsmbtiles.cpp:143
sqlite3_database_unique_ptr::open_v2
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
Definition: qgssqliteutils.cpp:86
QgsMbTiles::create
bool create()
Creates a new MBTiles file and initializes it with metadata and tiles tables.
Definition: qgsmbtiles.cpp:49
QgsMbTiles::metadataValue
QString metadataValue(const QString &key) const
Requests metadata value for the given key.
Definition: qgsmbtiles.cpp:80
QgsMbTiles::extent
QgsRectangle extent() const
Returns bounding box from metadata, given in WGS 84 (if available)
Definition: qgsmbtiles.cpp:130
sqlite3_statement_unique_ptr::columnAsText
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
Definition: qgssqliteutils.cpp:61
sqlite3_database_unique_ptr::exec
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Definition: qgssqliteutils.cpp:109
qgslogger.h
sqlite3_database_unique_ptr
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
Definition: qgssqliteutils.h:118
sqlite3_statement_unique_ptr
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
Definition: qgssqliteutils.h:69
QgsMbTiles::setTileData
void setTileData(int z, int x, int y, const QByteArray &data) const
Adds tile data for the given tile coordinates.
Definition: qgsmbtiles.cpp:182