28using namespace Qt::StringLiterals;
31 : mFilename( filename )
44 const int result = mDatabase.exec( u
"CREATE UNIQUE INDEX IF NOT EXISTS uk_tiles ON tiles (zoom_level, tile_column, tile_row);"_s, mLastError );
45 if ( result != SQLITE_OK )
47 QgsDebugError( u
"Failed to create GeoPackage tile index: %1"_s.arg( mLastError ) );
56 if ( mFilename.isEmpty() )
59 if ( QFile::exists( mFilename ) )
61 mLastError = u
"File already exists"_s;
65 int result = mDatabase.open_v2( mFilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
nullptr );
66 if ( result != SQLITE_OK )
68 mLastError = mDatabase.errorMessage();
69 QgsDebugError( u
"Can't create GeoPackage database: %1"_s.arg( mLastError ) );
76 mDatabase.exec( u
"PRAGMA journal_mode = WAL;"_s, errorMessage );
77 mDatabase.exec( u
"PRAGMA synchronous = OFF;"_s, errorMessage );
78 mDatabase.exec( u
"PRAGMA temp_store = MEMORY;"_s, errorMessage );
79 mDatabase.exec( u
"PRAGMA cache_size = -64000;"_s, errorMessage );
81 mDatabase.exec( u
"PRAGMA application_id = 1196444487;"_s, mLastError );
82 mDatabase.exec( u
"PRAGMA user_version = 10200;"_s, mLastError );
83 mDatabase.exec( u
"BEGIN TRANSACTION;"_s, errorMessage );
85 const QString sql =
"CREATE TABLE gpkg_spatial_ref_sys ("
86 " srs_name TEXT NOT NULL, srs_id INTEGER NOT NULL PRIMARY KEY,"
87 " organization TEXT NOT NULL, organization_coordsys_id INTEGER NOT NULL,"
88 " definition TEXT NOT NULL, description TEXT"
90 "CREATE TABLE gpkg_contents ("
91 " table_name TEXT NOT NULL PRIMARY KEY, data_type TEXT NOT NULL,"
92 " identifier TEXT UNIQUE, description TEXT DEFAULT '',"
93 " last_change DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),"
94 " min_x DOUBLE, min_y DOUBLE, max_x DOUBLE, max_y DOUBLE, srs_id INTEGER"
96 "CREATE TABLE gpkg_tile_matrix_set ("
97 " table_name TEXT NOT NULL PRIMARY KEY, srs_id INTEGER NOT NULL,"
98 " min_x DOUBLE NOT NULL, min_y DOUBLE NOT NULL,"
99 " max_x DOUBLE NOT NULL, max_y DOUBLE NOT NULL"
101 "CREATE TABLE gpkg_tile_matrix ("
102 " table_name TEXT NOT NULL, zoom_level INTEGER NOT NULL,"
103 " matrix_width INTEGER NOT NULL, matrix_height INTEGER NOT NULL,"
104 " tile_width INTEGER NOT NULL, tile_height INTEGER NOT NULL,"
105 " pixel_x_size DOUBLE NOT NULL, pixel_y_size DOUBLE NOT NULL,"
106 " CONSTRAINT pk_ttm PRIMARY KEY (table_name, zoom_level)"
108 "CREATE TABLE tiles ("
109 " id INTEGER PRIMARY KEY AUTOINCREMENT, zoom_level INTEGER NOT NULL,"
110 " tile_column INTEGER NOT NULL, tile_row INTEGER NOT NULL,"
111 " tile_data BLOB NOT NULL"
114 result = mDatabase.exec( sql, mLastError );
115 if ( result != SQLITE_OK )
117 QgsDebugError( u
"Failed to initialize GeoPackage database tables: "_s + mLastError );
122 QString srsSql =
"INSERT INTO gpkg_spatial_ref_sys VALUES "
123 "('Undefined Cartesian SRS', -1, 'NONE', -1, 'undefined', 'undefined Cartesian coordinate reference system'),"
124 "('Undefined geographic SRS', 0, 'NONE', 0, 'undefined', 'undefined geographic coordinate reference system'),";
125 srsSql +=
"('WGS 84 geodetic', 4326, 'EPSG', 4326, 'GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]', 'longitude/latitude coordinates in decimal degrees on the WGS 84 spheroid'),"_L1;
126 srsSql +=
"('WGS 84 / Pseudo-Mercator', 3857, 'EPSG', 3857, 'GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]', '')"_L1;
128 result = mDatabase.exec( srsSql, mLastError );
129 if ( result != SQLITE_OK )
131 QgsDebugError( u
"Failed to populate GeoPackage gpkg_spatial_ref_sys table: "_s + mLastError );
142 const QStringList authidParts = crs.
authid().split(
':' );
143 const QString org = authidParts.size() == 2 && !authidParts.at( 0 ).isEmpty() ? authidParts.at( 0 ) : u
"NONE"_s;
144 const int orgId = authidParts.size() == 2 ? authidParts.at( 1 ).toInt() : 0;
147 const QString customSrsSql = QString(
148 "INSERT OR IGNORE INTO gpkg_spatial_ref_sys (srs_name, srs_id, organization, organization_coordsys_id, definition, description) "
149 "VALUES (%1, %2, %3, %4, %5, %6);"
158 result = mDatabase.exec( customSrsSql, mLastError );
159 if ( result != SQLITE_OK )
161 QgsDebugError( u
"Failed to insert target SRS into GeoPackage gpkg_spatial_ref_sys table: "_s + mLastError );
167 const QString contentsSql = QString(
168 "INSERT INTO gpkg_contents (table_name, data_type, identifier, min_x, min_y, max_x, max_y, srs_id) "
169 "VALUES ('tiles', 'tiles', 'tiles', %1, %2, %3, %4, %5);"
171 .arg( contentsExtent.
xMinimum(), 0,
'g', 17 )
172 .arg( contentsExtent.
yMinimum(), 0,
'g', 17 )
173 .arg( contentsExtent.
xMaximum(), 0,
'g', 17 )
174 .arg( contentsExtent.
yMaximum(), 0,
'g', 17 )
176 result = mDatabase.exec( contentsSql, mLastError );
177 if ( result != SQLITE_OK )
179 QgsDebugError( u
"Failed to populate GeoPackage gpkg_contents table: "_s + mLastError );
184 const QString tmsSql = QString(
"INSERT INTO gpkg_tile_matrix_set VALUES ('tiles', %1, %2, %3, %4, %5);" )
186 .arg( tileMatrixSetExtent.
xMinimum(), 0,
'g', 17 )
187 .arg( tileMatrixSetExtent.
yMinimum(), 0,
'g', 17 )
188 .arg( tileMatrixSetExtent.
xMaximum(), 0,
'g', 17 )
189 .arg( tileMatrixSetExtent.
yMaximum(), 0,
'g', 17 );
191 result = mDatabase.exec( tmsSql, mLastError );
192 if ( result != SQLITE_OK )
194 QgsDebugError( u
"Failed to populate GeoPackage gpkg_tile_matrix_set: "_s + mLastError );
199 const double tmsWidth = tileMatrixSetExtent.
width();
200 const double tmsHeight = tileMatrixSetExtent.
height();
202 for (
int z = minZoom; z <= maxZoom; ++z )
208 const double pixelXSize = tmsWidth / ( matrixWidth * tileWidth );
209 const double pixelYSize = tmsHeight / ( matrixHeight * tileHeight );
211 const QString matrixSql = QString(
"INSERT INTO gpkg_tile_matrix VALUES ('tiles', %1, %2, %3, %4, %5, %6, %7);" )
217 .arg( pixelXSize, 0,
'g', 17 )
218 .arg( pixelYSize, 0,
'g', 17 );
220 result = mDatabase.exec( matrixSql, mLastError );
221 if ( result != SQLITE_OK )
223 QgsDebugError( u
"Failed to populate GeoPackage gpkg_tile_matrix for zoom %1: %2"_s.arg( z ).arg( mLastError ) );
228 result = mDatabase.exec( u
"COMMIT TRANSACTION;"_s, mLastError );
229 if ( result != SQLITE_OK )
231 QgsDebugError( u
"Failed to commit transaction: %1"_s.arg( mLastError ) );
241 if ( tiles.isEmpty() )
248 QgsDebugError( u
"GeoPackage database not open: "_s + mFilename );
252 int result = mDatabase.exec( u
"BEGIN TRANSACTION;"_s, mLastError );
253 if ( result != SQLITE_OK )
255 QgsDebugError( u
"Failed to begin transaction: %1"_s.arg( mLastError ) );
259 const QString sql = u
"INSERT OR REPLACE INTO tiles (zoom_level, tile_column, tile_row, tile_data) VALUES (?, ?, ?, ?)"_s;
261 if ( result != SQLITE_OK )
263 QgsDebugError( u
"GeoPackage failed to prepare statement: %1"_s.arg( sql ) );
264 mLastError = mDatabase.errorMessage();
265 mDatabase.exec( u
"ROLLBACK TRANSACTION;"_s, mLastError );
269 for (
const TileData &tile : tiles )
271 sqlite3_reset( preparedStatement.get() );
272 sqlite3_clear_bindings( preparedStatement.get() );
274 sqlite3_bind_int( preparedStatement.get(), 1, tile.z );
275 sqlite3_bind_int( preparedStatement.get(), 2, tile.x );
276 sqlite3_bind_int( preparedStatement.get(), 3, tile.y );
277 sqlite3_bind_blob( preparedStatement.get(), 4, tile.data.constData(), tile.data.size(), SQLITE_TRANSIENT );
279 if ( preparedStatement.
step() != SQLITE_DONE )
281 mLastError = mDatabase.errorMessage();
282 QgsDebugError( u
"GeoPackage tile failed to be set: %1,%2,%3"_s.arg( tile.z ).arg( tile.x ).arg( tile.y ) );
286 result = mDatabase.exec( u
"COMMIT TRANSACTION;"_s, mLastError );
287 if ( result != SQLITE_OK )
289 QgsDebugError( u
"Failed to commit transaction: %1"_s.arg( mLastError ) );
@ Wkt1Gdal
WKT1 as traditionally output by GDAL, deriving from OGC 01-009. A notable departure from WKT1_GDAL wi...
Represents a coordinate reference system (CRS).
long postgisSrid() const
Returns PostGIS SRID for the CRS.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
long srsid() const
Returns the internal CRS ID, if available.
QgsGeoPackageTiles(const QString &filename)
Constructs the GeoPackage reader (but it does not open the file yet).
bool close()
Explicitly closes the database.
void setTileData(const QList< TileData > &tiles) const
Adds a batch of tile data within a single SQLite transaction.
bool finalize()
Finalizes the database after writing all tiles.
bool create(const QgsTileMatrix &z0matrix, const QgsRectangle &contentsExtent, int minZoom, int maxZoom, int tileWidth=256, int tileHeight=256)
Creates a new GeoPackage database and initializes it with metadata and tiles tables.
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.
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
QgsRectangle extent() const
Returns extent of the tile matrix.
QgsCoordinateReferenceSystem crs() const
Returns the crs of the tile matrix.
long long derivedMatrixHeightAtZoomLevel(int zoomLevel) const
Returns the calculated matrix height at the specified zoom level.
long long derivedMatrixWidthAtZoomLevel(int zoomLevel) const
Returns the calculated matrix width at the specified zoom level.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
#define QgsDebugError(str)
Struct representing a raw image tile.