QGIS API Documentation 3.99.0-Master (09f76ad7019)
Loading...
Searching...
No Matches
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#include <QString>
24
25using namespace Qt::StringLiterals;
26
27QgsMbTiles::QgsMbTiles( const QString &filename )
28 : mFilename( filename )
29{
30}
31
33{
34 if ( mDatabase )
35 return true; // already opened
36
37 if ( mFilename.isEmpty() )
38 return false;
39
40 const sqlite3_database_unique_ptr database;
41 const int result = mDatabase.open_v2( mFilename, SQLITE_OPEN_READONLY, nullptr );
42 if ( result != SQLITE_OK )
43 {
44 QgsDebugError( u"Can't open MBTiles database: %1"_s.arg( database.errorMessage() ) );
45 return false;
46 }
47 return true;
48}
49
51{
52 return bool( mDatabase );
53}
54
56{
57 if ( mDatabase )
58 return false;
59
60 if ( QFile::exists( mFilename ) )
61 return false;
62
63 const sqlite3_database_unique_ptr database;
64 int result = mDatabase.open_v2( mFilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
65 if ( result != SQLITE_OK )
66 {
67 QgsDebugError( u"Can't create MBTiles database: %1"_s.arg( database.errorMessage() ) );
68 return false;
69 }
70
71 const QString sql = \
72 "CREATE TABLE metadata (name text, value text);" \
73 "CREATE TABLE tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob);" \
74 "CREATE UNIQUE INDEX tile_index on tiles (zoom_level, tile_column, tile_row);";
75 QString errorMessage;
76 result = mDatabase.exec( sql, errorMessage );
77 if ( result != SQLITE_OK )
78 {
79 QgsDebugError( u"Failed to initialize MBTiles database: "_s + errorMessage );
80 return false;
81 }
82
83 return true;
84}
85
86QString QgsMbTiles::metadataValue( const QString &key ) const
87{
88 if ( !mDatabase )
89 {
90 QgsDebugError( u"MBTiles database not open: "_s + mFilename );
91 return QString();
92 }
93
94 int result;
95 const QString sql = u"select value from metadata where name='%1'"_s.arg( key );
96 sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
97 if ( result != SQLITE_OK )
98 {
99 QgsDebugError( u"MBTile failed to prepare statement: "_s + sql );
100 return QString();
101 }
102
103 if ( preparedStatement.step() != SQLITE_ROW )
104 {
105 QgsDebugError( u"MBTile metadata value not found: "_s + key );
106 return QString();
107 }
108
109 return preparedStatement.columnAsText( 0 );
110}
111
112void QgsMbTiles::setMetadataValue( const QString &key, const QString &value ) const
113{
114 if ( !mDatabase )
115 {
116 QgsDebugError( u"MBTiles database not open: "_s + mFilename );
117 return;
118 }
119
120 int result;
121 const QString sql = u"insert into metadata values (%1, %2)"_s.arg( QgsSqliteUtils::quotedValue( key ), QgsSqliteUtils::quotedValue( value ) );
122 sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
123 if ( result != SQLITE_OK )
124 {
125 QgsDebugError( u"MBTile failed to prepare statement: "_s + sql );
126 return;
127 }
128
129 if ( preparedStatement.step() != SQLITE_DONE )
130 {
131 QgsDebugError( u"MBTile metadata value failed to be set: "_s + key );
132 return;
133 }
134}
135
137{
138 const QString boundsStr = metadataValue( "bounds" );
139 if ( boundsStr.isEmpty() )
140 return QgsRectangle();
141 QStringList boundsArray = boundsStr.split( ',' );
142 if ( boundsArray.count() != 4 )
143 return QgsRectangle();
144
145 return QgsRectangle( boundsArray[0].toDouble(), boundsArray[1].toDouble(),
146 boundsArray[2].toDouble(), boundsArray[3].toDouble() );
147}
148
149QByteArray QgsMbTiles::tileData( int z, int x, int y ) const
150{
151 if ( !mDatabase )
152 {
153 QgsDebugError( u"MBTiles database not open: "_s + mFilename );
154 return QByteArray();
155 }
156
157 int result;
158 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 );
159 sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
160 if ( result != SQLITE_OK )
161 {
162 QgsDebugError( u"MBTile failed to prepare statement: "_s + sql );
163 return QByteArray();
164 }
165
166 if ( preparedStatement.step() != SQLITE_ROW )
167 {
168 // this is not entirely unexpected -- user may have just requested a tile outside of the extent of the mbtiles package
169 QgsDebugMsgLevel( u"MBTile not found: z=%1 x=%2 y=%3"_s.arg( z ).arg( x ).arg( y ), 2 );
170 return QByteArray();
171 }
172
173 return preparedStatement.columnAsBlob( 0 );
174}
175
176QImage QgsMbTiles::tileDataAsImage( int z, int x, int y ) const
177{
178 QImage tileImage;
179 const QByteArray tileBlob = tileData( z, x, y );
180 if ( !tileImage.loadFromData( tileBlob ) )
181 {
182 QgsDebugError( u"MBTile data failed to load: z=%1 x=%2 y=%3"_s.arg( z ).arg( x ).arg( y ) );
183 return QImage();
184 }
185 return tileImage;
186}
187
188void QgsMbTiles::setTileData( int z, int x, int y, const QByteArray &data ) const
189{
190 if ( !mDatabase )
191 {
192 QgsDebugError( u"MBTiles database not open: "_s + mFilename );
193 return;
194 }
195
196 int result;
197 const QString sql = u"insert into tiles values (%1, %2, %3, ?)"_s.arg( z ).arg( x ).arg( y );
198 sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
199 if ( result != SQLITE_OK )
200 {
201 QgsDebugError( u"MBTile failed to prepare statement: "_s + sql );
202 return;
203 }
204
205 sqlite3_bind_blob( preparedStatement.get(), 1, data.constData(), data.size(), SQLITE_TRANSIENT );
206
207 if ( preparedStatement.step() != SQLITE_DONE )
208 {
209 QgsDebugError( u"MBTile tile failed to be set: %1,%2,%3"_s.arg( z ).arg( x ).arg( y ) );
210 return;
211 }
212}
bool create()
Creates a new MBTiles file and initializes it with metadata and tiles tables.
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).
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.
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 databases, which automatically closes the database when the pointer goes o...
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
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)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59