LCOV - code coverage report
Current view: top level - core - qgsmbtiles.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 165 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       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                 :            : #include <zlib.h>
      25                 :            : 
      26                 :            : 
      27                 :          0 : QgsMbTiles::QgsMbTiles( const QString &filename )
      28                 :          0 :   : mFilename( filename )
      29                 :            : {
      30                 :          0 : }
      31                 :            : 
      32                 :          0 : bool QgsMbTiles::open()
      33                 :            : {
      34                 :          0 :   if ( mDatabase )
      35                 :          0 :     return true;  // already opened
      36                 :            : 
      37                 :          0 :   sqlite3_database_unique_ptr database;
      38                 :          0 :   int result = mDatabase.open_v2( mFilename, SQLITE_OPEN_READONLY, nullptr );
      39                 :          0 :   if ( result != SQLITE_OK )
      40                 :            :   {
      41                 :          0 :     QgsDebugMsg( QStringLiteral( "Can't open MBTiles database: %1" ).arg( database.errorMessage() ) );
      42                 :          0 :     return false;
      43                 :            :   }
      44                 :          0 :   return true;
      45                 :          0 : }
      46                 :            : 
      47                 :          0 : bool QgsMbTiles::isOpen() const
      48                 :            : {
      49                 :          0 :   return bool( mDatabase );
      50                 :            : }
      51                 :            : 
      52                 :          0 : bool QgsMbTiles::create()
      53                 :            : {
      54                 :          0 :   if ( mDatabase )
      55                 :          0 :     return false;
      56                 :            : 
      57                 :          0 :   if ( QFile::exists( mFilename ) )
      58                 :          0 :     return false;
      59                 :            : 
      60                 :          0 :   sqlite3_database_unique_ptr database;
      61                 :          0 :   int result = mDatabase.open_v2( mFilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
      62                 :          0 :   if ( result != SQLITE_OK )
      63                 :            :   {
      64                 :          0 :     QgsDebugMsg( QStringLiteral( "Can't create MBTiles database: %1" ).arg( database.errorMessage() ) );
      65                 :          0 :     return false;
      66                 :            :   }
      67                 :            : 
      68                 :            :   QString sql = \
      69                 :          0 :                 "CREATE TABLE metadata (name text, value text);" \
      70                 :            :                 "CREATE TABLE tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob);" \
      71                 :            :                 "CREATE UNIQUE INDEX tile_index on tiles (zoom_level, tile_column, tile_row);";
      72                 :          0 :   QString errorMessage;
      73                 :          0 :   result = mDatabase.exec( sql, errorMessage );
      74                 :          0 :   if ( result != SQLITE_OK )
      75                 :            :   {
      76                 :          0 :     QgsDebugMsg( QStringLiteral( "Failed to initialize MBTiles database: " ) + errorMessage );
      77                 :          0 :     return false;
      78                 :            :   }
      79                 :            : 
      80                 :          0 :   return true;
      81                 :          0 : }
      82                 :            : 
      83                 :          0 : QString QgsMbTiles::metadataValue( const QString &key )
      84                 :            : {
      85                 :          0 :   if ( !mDatabase )
      86                 :            :   {
      87                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTiles database not open: " ) + mFilename );
      88                 :          0 :     return QString();
      89                 :            :   }
      90                 :            : 
      91                 :            :   int result;
      92                 :          0 :   QString sql = QStringLiteral( "select value from metadata where name='%1'" ).arg( key );
      93                 :          0 :   sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
      94                 :          0 :   if ( result != SQLITE_OK )
      95                 :            :   {
      96                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTile failed to prepare statement: " ) + sql );
      97                 :          0 :     return QString();
      98                 :            :   }
      99                 :            : 
     100                 :          0 :   if ( preparedStatement.step() != SQLITE_ROW )
     101                 :            :   {
     102                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTile metadata value not found: " ) + key );
     103                 :          0 :     return QString();
     104                 :            :   }
     105                 :            : 
     106                 :          0 :   return preparedStatement.columnAsText( 0 );
     107                 :          0 : }
     108                 :            : 
     109                 :          0 : void QgsMbTiles::setMetadataValue( const QString &key, const QString &value )
     110                 :            : {
     111                 :          0 :   if ( !mDatabase )
     112                 :            :   {
     113                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTiles database not open: " ) + mFilename );
     114                 :          0 :     return;
     115                 :            :   }
     116                 :            : 
     117                 :            :   int result;
     118                 :          0 :   QString sql = QStringLiteral( "insert into metadata values (%1, %2)" ).arg( QgsSqliteUtils::quotedValue( key ), QgsSqliteUtils::quotedValue( value ) );
     119                 :          0 :   sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
     120                 :          0 :   if ( result != SQLITE_OK )
     121                 :            :   {
     122                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTile failed to prepare statement: " ) + sql );
     123                 :          0 :     return;
     124                 :            :   }
     125                 :            : 
     126                 :          0 :   if ( preparedStatement.step() != SQLITE_DONE )
     127                 :            :   {
     128                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTile metadata value failed to be set: " ) + key );
     129                 :          0 :     return;
     130                 :            :   }
     131                 :          0 : }
     132                 :            : 
     133                 :          0 : QgsRectangle QgsMbTiles::extent()
     134                 :            : {
     135                 :          0 :   QString boundsStr = metadataValue( "bounds" );
     136                 :          0 :   if ( boundsStr.isEmpty() )
     137                 :          0 :     return QgsRectangle();
     138                 :          0 :   QStringList boundsArray = boundsStr.split( ',' );
     139                 :          0 :   if ( boundsArray.count() != 4 )
     140                 :          0 :     return QgsRectangle();
     141                 :            : 
     142                 :          0 :   return QgsRectangle( boundsArray[0].toDouble(), boundsArray[1].toDouble(),
     143                 :          0 :                        boundsArray[2].toDouble(), boundsArray[3].toDouble() );
     144                 :          0 : }
     145                 :            : 
     146                 :          0 : QByteArray QgsMbTiles::tileData( int z, int x, int y )
     147                 :            : {
     148                 :          0 :   if ( !mDatabase )
     149                 :            :   {
     150                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTiles database not open: " ) + mFilename );
     151                 :          0 :     return QByteArray();
     152                 :            :   }
     153                 :            : 
     154                 :            :   int result;
     155                 :          0 :   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 );
     156                 :          0 :   sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
     157                 :          0 :   if ( result != SQLITE_OK )
     158                 :            :   {
     159                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTile failed to prepare statement: " ) + sql );
     160                 :          0 :     return QByteArray();
     161                 :            :   }
     162                 :            : 
     163                 :          0 :   if ( preparedStatement.step() != SQLITE_ROW )
     164                 :            :   {
     165                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTile not found: z=%1 x=%2 y=%3" ).arg( z ).arg( x ).arg( y ) );
     166                 :          0 :     return QByteArray();
     167                 :            :   }
     168                 :            : 
     169                 :          0 :   return preparedStatement.columnAsBlob( 0 );
     170                 :          0 : }
     171                 :            : 
     172                 :          0 : QImage QgsMbTiles::tileDataAsImage( int z, int x, int y )
     173                 :            : {
     174                 :          0 :   QImage tileImage;
     175                 :          0 :   QByteArray tileBlob = tileData( z, x, y );
     176                 :          0 :   if ( !tileImage.loadFromData( tileBlob ) )
     177                 :            :   {
     178                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTile data failed to load: z=%1 x=%2 y=%3" ).arg( z ).arg( x ).arg( y ) );
     179                 :          0 :     return QImage();
     180                 :            :   }
     181                 :          0 :   return tileImage;
     182                 :          0 : }
     183                 :            : 
     184                 :          0 : void QgsMbTiles::setTileData( int z, int x, int y, const QByteArray &data )
     185                 :            : {
     186                 :          0 :   if ( !mDatabase )
     187                 :            :   {
     188                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTiles database not open: " ) + mFilename );
     189                 :          0 :     return;
     190                 :            :   }
     191                 :            : 
     192                 :            :   int result;
     193                 :          0 :   QString sql = QStringLiteral( "insert into tiles values (%1, %2, %3, ?)" ).arg( z ).arg( x ).arg( y );
     194                 :          0 :   sqlite3_statement_unique_ptr preparedStatement = mDatabase.prepare( sql, result );
     195                 :          0 :   if ( result != SQLITE_OK )
     196                 :            :   {
     197                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTile failed to prepare statement: " ) + sql );
     198                 :          0 :     return;
     199                 :            :   }
     200                 :            : 
     201                 :          0 :   sqlite3_bind_blob( preparedStatement.get(), 1, data.constData(), data.size(), SQLITE_TRANSIENT );
     202                 :            : 
     203                 :          0 :   if ( preparedStatement.step() != SQLITE_DONE )
     204                 :            :   {
     205                 :          0 :     QgsDebugMsg( QStringLiteral( "MBTile tile failed to be set: %1,%2,%3" ).arg( z ).arg( x ).arg( y ) );
     206                 :          0 :     return;
     207                 :            :   }
     208                 :          0 : }
     209                 :            : 
     210                 :          0 : bool QgsMbTiles::decodeGzip( const QByteArray &bytesIn, QByteArray &bytesOut )
     211                 :            : {
     212                 :          0 :   unsigned char *bytesInPtr = reinterpret_cast<unsigned char *>( const_cast<char *>( bytesIn.constData() ) );
     213                 :          0 :   uint bytesInLeft = static_cast<uint>( bytesIn.count() );
     214                 :            : 
     215                 :          0 :   const uint CHUNK = 16384;
     216                 :            :   unsigned char out[CHUNK];
     217                 :          0 :   const int DEC_MAGIC_NUM_FOR_GZIP = 16;
     218                 :            : 
     219                 :            :   // allocate inflate state
     220                 :            :   z_stream strm;
     221                 :          0 :   strm.zalloc = Z_NULL;
     222                 :          0 :   strm.zfree = Z_NULL;
     223                 :          0 :   strm.opaque = Z_NULL;
     224                 :          0 :   strm.avail_in = 0;
     225                 :          0 :   strm.next_in = Z_NULL;
     226                 :            : 
     227                 :          0 :   int ret = inflateInit2( &strm, MAX_WBITS + DEC_MAGIC_NUM_FOR_GZIP );
     228                 :          0 :   if ( ret != Z_OK )
     229                 :          0 :     return false;
     230                 :            : 
     231                 :          0 :   while ( ret != Z_STREAM_END ) // done when inflate() says it's done
     232                 :            :   {
     233                 :            :     // prepare next chunk
     234                 :          0 :     uint bytesToProcess = std::min( CHUNK, bytesInLeft );
     235                 :          0 :     strm.next_in = bytesInPtr;
     236                 :          0 :     strm.avail_in = bytesToProcess;
     237                 :          0 :     bytesInPtr += bytesToProcess;
     238                 :          0 :     bytesInLeft -= bytesToProcess;
     239                 :            : 
     240                 :          0 :     if ( bytesToProcess == 0 )
     241                 :          0 :       break;  // we end with an error - no more data but inflate() wants more data
     242                 :            : 
     243                 :            :     // run inflate() on input until output buffer not full
     244                 :          0 :     do
     245                 :            :     {
     246                 :          0 :       strm.avail_out = CHUNK;
     247                 :          0 :       strm.next_out = out;
     248                 :          0 :       ret = inflate( &strm, Z_NO_FLUSH );
     249                 :            :       Q_ASSERT( ret != Z_STREAM_ERROR ); // state not clobbered
     250                 :          0 :       if ( ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR )
     251                 :            :       {
     252                 :          0 :         inflateEnd( &strm );
     253                 :          0 :         return false;
     254                 :            :       }
     255                 :          0 :       unsigned have = CHUNK - strm.avail_out;
     256                 :          0 :       bytesOut.append( QByteArray::fromRawData( reinterpret_cast<const char *>( out ), static_cast<int>( have ) ) );
     257                 :          0 :     }
     258                 :          0 :     while ( strm.avail_out == 0 );
     259                 :            :   }
     260                 :            : 
     261                 :          0 :   inflateEnd( &strm );
     262                 :          0 :   return ret == Z_STREAM_END;
     263                 :          0 : }
     264                 :            : 
     265                 :            : 
     266                 :          0 : bool QgsMbTiles::encodeGzip( const QByteArray &bytesIn, QByteArray &bytesOut )
     267                 :            : {
     268                 :          0 :   unsigned char *bytesInPtr = reinterpret_cast<unsigned char *>( const_cast<char *>( bytesIn.constData() ) );
     269                 :          0 :   uint bytesInLeft = static_cast<uint>( bytesIn.count() );
     270                 :            : 
     271                 :          0 :   const uint CHUNK = 16384;
     272                 :            :   unsigned char out[CHUNK];
     273                 :          0 :   const int DEC_MAGIC_NUM_FOR_GZIP = 16;
     274                 :            : 
     275                 :            :   // allocate deflate state
     276                 :            :   z_stream strm;
     277                 :          0 :   strm.zalloc = Z_NULL;
     278                 :          0 :   strm.zfree = Z_NULL;
     279                 :          0 :   strm.opaque = Z_NULL;
     280                 :            : 
     281                 :          0 :   int ret = deflateInit2( &strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + DEC_MAGIC_NUM_FOR_GZIP, 8, Z_DEFAULT_STRATEGY );
     282                 :          0 :   if ( ret != Z_OK )
     283                 :          0 :     return false;
     284                 :            : 
     285                 :          0 :   strm.avail_in = bytesInLeft;
     286                 :          0 :   strm.next_in = bytesInPtr;
     287                 :            : 
     288                 :            :   // run deflate() on input until output buffer not full, finish
     289                 :            :   // compression if all of source has been read in
     290                 :          0 :   do
     291                 :            :   {
     292                 :          0 :     strm.avail_out = CHUNK;
     293                 :          0 :     strm.next_out = out;
     294                 :          0 :     ret = deflate( &strm, Z_FINISH );  // no bad return value
     295                 :            :     Q_ASSERT( ret != Z_STREAM_ERROR ); // state not clobbered
     296                 :            : 
     297                 :          0 :     unsigned have = CHUNK - strm.avail_out;
     298                 :          0 :     bytesOut.append( QByteArray::fromRawData( reinterpret_cast<const char *>( out ), static_cast<int>( have ) ) );
     299                 :          0 :   }
     300                 :          0 :   while ( strm.avail_out == 0 );
     301                 :            :   Q_ASSERT( ret == Z_STREAM_END );      // stream will be complete
     302                 :            : 
     303                 :            :   // clean up and return
     304                 :          0 :   deflateEnd( &strm );
     305                 :          0 :   return true;
     306                 :          0 : }

Generated by: LCOV version 1.14