28#include <QDomDocument> 
   29#include <QTextDocumentFragment> 
   35  : mFilename( filename )
 
 
   53  const QByteArray fileNamePtr = mFilename.toUtf8();
 
   55  mZip = zip_open( fileNamePtr.constData(), ZIP_CHECKCONS, &rc );
 
   56  if ( rc == ZIP_ER_OK && mZip )
 
   58    const int count = zip_get_num_entries( mZip, ZIP_FL_UNCHANGED );
 
   73    QgsMessageLog::logMessage( QObject::tr( 
"Error opening zip archive: '%1' (Error code: %2)" ).arg( mZip ? zip_strerror( mZip ) : mFilename ).arg( rc ) );
 
 
   85  return static_cast< bool >( mZip );
 
 
   90  if ( !mMetadata.isEmpty() )
 
   96  const char *name = 
"p12/root.json";
 
   98  zip_stat_init( &stat );
 
   99  zip_stat( mZip, name, 0, &stat );
 
  101  const size_t len = stat.size;
 
  102  const std::unique_ptr< char[] > buf( 
new char[len + 1] );
 
  105  zip_file *file = zip_fopen( mZip, name, 0 );
 
  106  if ( zip_fread( file, buf.get(), len ) != -1 )
 
  109    std::string jsonString( buf.get( ) );
 
  122  mTileMapPath = mMetadata.value( QStringLiteral( 
"tileMap" ) ).toString();
 
 
  130    return QVariantMap();
 
  132  const char *name = 
"p12/resources/styles/root.json";
 
  133  struct zip_stat stat;
 
  134  zip_stat_init( &stat );
 
  135  zip_stat( mZip, name, 0, &stat );
 
  137  const size_t len = stat.size;
 
  138  const std::unique_ptr< char[] > buf( 
new char[len + 1] );
 
  142  zip_file *file = zip_fopen( mZip, name, 0 );
 
  143  if ( zip_fread( file, buf.get(), len ) != -1 )
 
  146    std::string jsonString( buf.get( ) );
 
 
  164    return QVariantMap();
 
  166  for ( 
int resolution = 2; resolution > 0; resolution-- )
 
  168    const QString spriteFileCandidate = QStringLiteral( 
"p12/resources/sprites/sprite%1.json" ).arg( resolution > 1 ? QStringLiteral( 
"@%1x" ).arg( resolution ) : QString() );
 
  169    const QByteArray spriteFileCandidateBa = spriteFileCandidate.toLocal8Bit();
 
  170    const char *name = spriteFileCandidateBa.constData();
 
  171    struct zip_stat stat;
 
  172    zip_stat_init( &stat );
 
  173    zip_stat( mZip, name, 0, &stat );
 
  178    const size_t len = stat.size;
 
  179    const std::unique_ptr< char[] > buf( 
new char[len + 1] );
 
  181    QVariantMap definition;
 
  183    zip_file *file = zip_fopen( mZip, name, 0 );
 
  184    if ( zip_fread( file, buf.get(), len ) != -1 )
 
  187      std::string jsonString( buf.get( ) );
 
  202  return QVariantMap();
 
 
  210  for ( 
int resolution = 2; resolution > 0; resolution-- )
 
  212    const QString spriteFileCandidate = QStringLiteral( 
"p12/resources/sprites/sprite%1.png" ).arg( resolution > 1 ? QStringLiteral( 
"@%1x" ).arg( resolution ) : QString() );
 
  213    const QByteArray spriteFileCandidateBa = spriteFileCandidate.toLocal8Bit();
 
  214    const char *name = spriteFileCandidateBa.constData();
 
  215    struct zip_stat stat;
 
  216    zip_stat_init( &stat );
 
  217    zip_stat( mZip, name, 0, &stat );
 
  222    const size_t len = stat.size;
 
  223    const std::unique_ptr< char[] > buf( 
new char[len + 1] );
 
  227    zip_file *file = zip_fopen( mZip, name, 0 );
 
  228    if ( zip_fread( file, buf.get(), len ) != -1 )
 
  232      result = QImage::fromData( 
reinterpret_cast<const uchar *
>( buf.get() ), len );
 
 
  255  const char *name = 
"esriinfo/iteminfo.xml";
 
  256  struct zip_stat stat;
 
  257  zip_stat_init( &stat );
 
  258  zip_stat( mZip, name, 0, &stat );
 
  260  const size_t len = stat.size;
 
  261  QByteArray buf( len, Qt::Uninitialized );
 
  265  zip_file *file = zip_fopen( mZip, name, 0 );
 
  266  if ( zip_fread( file, buf.data(), len ) != -1 )
 
  272    QString errorMessage;
 
  275    if ( !doc.setContent( buf, 
false, &errorMessage, &errorLine, &errorColumn ) )
 
  277      QgsMessageLog::logMessage( QObject::tr( 
"Error reading layer metadata (line %1, col %2): %3" ).arg( errorLine ).arg( errorColumn ).arg( errorMessage ) );
 
  281      metadata.setType( QStringLiteral( 
"dataset" ) );
 
  283      const QDomElement infoElement = doc.firstChildElement( QStringLiteral( 
"ESRI_ItemInformation" ) );
 
  285      metadata.setLanguage( infoElement.attribute( QStringLiteral( 
"Culture" ) ) );
 
  287      const QDomElement guidElement = infoElement.firstChildElement( QStringLiteral( 
"guid" ) );
 
  288      metadata.setIdentifier( guidElement.text() );
 
  290      const QDomElement nameElement = infoElement.firstChildElement( QStringLiteral( 
"name" ) );
 
  291      metadata.setTitle( nameElement.text() );
 
  293      const QDomElement descriptionElement = infoElement.firstChildElement( QStringLiteral( 
"description" ) );
 
  294      metadata.setAbstract( QTextDocumentFragment::fromHtml( descriptionElement.text() ).toPlainText() );
 
  296      const QDomElement tagsElement = infoElement.firstChildElement( QStringLiteral( 
"tags" ) );
 
  298      const QStringList rawTags = tagsElement.text().split( 
',' );
 
  300      tags.reserve( rawTags.size() );
 
  301      for ( 
const QString &tag : rawTags )
 
  302        tags.append( tag.trimmed() );
 
  303      metadata.addKeywords( QStringLiteral( 
"keywords" ), tags );
 
  305      const QDomElement accessInformationElement = infoElement.firstChildElement( QStringLiteral( 
"accessinformation" ) );
 
  306      metadata.setRights( { accessInformationElement.text() } );
 
  308      const QDomElement licenseInfoElement = infoElement.firstChildElement( QStringLiteral( 
"licenseinfo" ) );
 
  309      metadata.setLicenses( { QTextDocumentFragment::fromHtml( licenseInfoElement.text() ).toPlainText() } );
 
  311      const QDomElement extentElement = infoElement.firstChildElement( QStringLiteral( 
"extent" ) );
 
  312      const double xMin = extentElement.firstChildElement( QStringLiteral( 
"xmin" ) ).text().toDouble();
 
  313      const double xMax = extentElement.firstChildElement( QStringLiteral( 
"xmax" ) ).text().toDouble();
 
  314      const double yMin = extentElement.firstChildElement( QStringLiteral( 
"ymin" ) ).text().toDouble();
 
  315      const double yMax = extentElement.firstChildElement( QStringLiteral( 
"ymax" ) ).text().toDouble();
 
  323      extent.setSpatialExtents( { spatialExtent } );
 
 
  345  if ( mHasReadTileMap || mTileMapPath.isEmpty() )
 
  349    return QVariantMap();
 
  351  const QString tileMapPath = QStringLiteral( 
"p12/%1/root.json" ).arg( mTileMapPath );
 
  352  struct zip_stat stat;
 
  353  zip_stat_init( &stat );
 
  354  zip_stat( mZip, tileMapPath.toLocal8Bit().constData(), 0, &stat );
 
  356  const size_t len = stat.size;
 
  357  const std::unique_ptr< char[] > buf( 
new char[len + 1] );
 
  360  zip_file *file = zip_fopen( mZip, tileMapPath.toLocal8Bit().constData(), 0 );
 
  363    QgsDebugError( QStringLiteral( 
"Tilemap %1 was not found in vtpk archive" ).arg( tileMapPath ) );
 
  364    mTileMapPath.clear();
 
  368  if ( zip_fread( file, buf.get(), len ) != -1 )
 
  371    std::string jsonString( buf.get( ) );
 
  381    QgsDebugError( QStringLiteral( 
"Tilemap %1 could not be read from vtpk archive" ).arg( tileMapPath ) );
 
  382    mTileMapPath.clear();
 
  384  mHasReadTileMap = 
true;
 
 
  406  const QVariantMap fullExtent = md.value( QStringLiteral( 
"fullExtent" ) ).toMap();
 
  407  if ( !fullExtent.isEmpty() )
 
  410      fullExtent.value( QStringLiteral( 
"xmin" ) ).toDouble(),
 
  411      fullExtent.value( QStringLiteral( 
"ymin" ) ).toDouble(),
 
  412      fullExtent.value( QStringLiteral( 
"xmax" ) ).toDouble(),
 
  413      fullExtent.value( QStringLiteral( 
"ymax" ) ).toDouble()
 
  424      QgsDebugError( QStringLiteral( 
"Could not transform layer fullExtent to layer CRS" ) );
 
 
  435    QgsDebugError( QStringLiteral( 
"VTPK tile package not open: " ) + mFilename );
 
  438  if ( mPacketSize < 0 )
 
  439    mPacketSize = 
metadata().value( QStringLiteral( 
"resourceInfo" ) ).toMap().value( QStringLiteral( 
"cacheInfo" ) ).toMap().value( QStringLiteral( 
"storageInfo" ) ).toMap().value( QStringLiteral( 
"packetSize" ) ).toInt();
 
  441  const int fileRow = mPacketSize * 
static_cast< int >( std::floor( y / 
static_cast< double>( mPacketSize ) ) );
 
  442  const int fileCol = mPacketSize * 
static_cast< int >( std::floor( x / 
static_cast< double>( mPacketSize ) ) );
 
  444  const QString tileName = QStringLiteral( 
"R%1C%2" )
 
  445                           .arg( fileRow, 4, 16, QLatin1Char( 
'0' ) )
 
  446                           .arg( fileCol, 4, 16, QLatin1Char( 
'0' ) );
 
  448  const QString fileName = QStringLiteral( 
"p12/tile/L%1/%2.bundle" )
 
  449                           .arg( z, 2, 10, QLatin1Char( 
'0' ) ).arg( tileName );
 
  450  struct zip_stat stat;
 
  451  zip_stat_init( &stat );
 
  452  zip_stat( mZip, fileName.toLocal8Bit().constData(), 0, &stat );
 
  454  const size_t tileIndexOffset = 64 + 8 * ( mPacketSize * ( y % mPacketSize ) + ( x % mPacketSize ) );
 
  457  const size_t len = stat.size;
 
  458  if ( len <= tileIndexOffset )
 
  461    res = QByteArray( 
"" );
 
  465    const std::unique_ptr< char[] > buf( 
new char[len] );
 
  468    zip_file *file = zip_fopen( mZip, fileName.toLocal8Bit().constData(), 0 );
 
  469    if ( zip_fread( file, buf.get(), len ) != -1 )
 
  471      unsigned long long indexValue;
 
  472      memcpy( &indexValue, buf.get() + tileIndexOffset, 8 );
 
  474      const std::size_t tileOffset = indexValue % ( 2ULL << 39 );
 
  475      const std::size_t tileSize = 
static_cast< std::size_t
>( std::floor( indexValue / ( 2ULL << 39 ) ) );
 
  480        res = QByteArray( 
"" );
 
 
static QgsCoordinateReferenceSystem convertSpatialReference(const QVariantMap &spatialReferenceMap)
Converts a spatial reference JSON definition to a QgsCoordinateReferenceSystem value.
A 3-dimensional box composed of x, y, z coordinates.
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Custom exception class for Coordinate Reference System related exceptions.
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned and ...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
A rectangle specified with double values.
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system associated with the tiles.
bool isEmpty() const
Returns true if the matrix set is empty.
Encapsulates properties of a vector tile matrix set, including tile origins and scaling information.
bool fromEsriJson(const QVariantMap &json, const QVariantMap &rootTileMap=QVariantMap())
Initializes the tile structure settings from an ESRI REST VectorTileService json map.
QByteArray tileData(int z, int x, int y)
Returns the raw tile data for the matching tile.
QgsRectangle extent(const QgsCoordinateTransformContext &context) const
Returns bounding box from metadata, given in the tiles crs().
bool isOpen() const
Returns whether the VTPK file is currently opened.
QVariantMap rootTileMap() const
Returns the root tilemap content, if it exists.
QVariantMap spriteDefinition() const
Returns the VTPK sprites definitions.
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system of the tiles.
QgsLayerMetadata layerMetadata() const
Reads layer metadata from the VTPK file.
bool open()
Tries to open the file, returns true on success.
QgsVtpkTiles(const QString &filename)
Constructs VTPK reader (but it does not open the file yet)
QgsVectorTileMatrixSet matrixSet() const
Returns the vector tile matrix set representing the tiles.
QVariantMap styleDefinition() const
Returns the VTPK style definition.
QVariantMap metadata() const
Returns the VTPK metadata.
QImage spriteImage() const
Returns the VTPK sprite image, if it exists.
CORE_EXPORT bool decodeGzip(const QByteArray &bytesIn, QByteArray &bytesOut)
Decodes gzip byte stream, returns true on success.
#define QgsDebugError(str)