29#include <QDomDocument>
33#include <QTextDocumentFragment>
35using namespace Qt::StringLiterals;
38 : mFilename( filename )
55 const QByteArray fileNamePtr = mFilename.toUtf8();
57 mZip = zip_open( fileNamePtr.constData(), ZIP_CHECKCONS, &rc );
58 if ( rc == ZIP_ER_OK && mZip )
60 const int count = zip_get_num_entries( mZip, ZIP_FL_UNCHANGED );
75 QgsMessageLog::logMessage( QObject::tr(
"Error opening zip archive: '%1' (Error code: %2)" ).arg( mZip ? zip_strerror( mZip ) : mFilename ).arg( rc ) );
87 return static_cast< bool >( mZip );
92 if ( !mMetadata.isEmpty() )
98 const char *name =
"p12/root.json";
100 zip_stat_init( &stat );
101 zip_stat( mZip, name, 0, &stat );
103 const size_t len = stat.size;
104 const std::unique_ptr< char[] > buf(
new char[len + 1] );
107 zip_file *file = zip_fopen( mZip, name, 0 );
108 if ( zip_fread( file, buf.get(), len ) != -1 )
111 std::string jsonString( buf.get() );
124 mTileMapPath = mMetadata.value( u
"tileMap"_s ).toString();
132 return QVariantMap();
134 const char *name =
"p12/resources/styles/root.json";
135 struct zip_stat stat;
136 zip_stat_init( &stat );
137 zip_stat( mZip, name, 0, &stat );
139 const size_t len = stat.size;
140 const std::unique_ptr< char[] > buf(
new char[len + 1] );
144 zip_file *file = zip_fopen( mZip, name, 0 );
145 if ( zip_fread( file, buf.get(), len ) != -1 )
148 std::string jsonString( buf.get() );
166 return QVariantMap();
168 for (
int resolution = 2; resolution > 0; resolution-- )
170 const QString spriteFileCandidate = u
"p12/resources/sprites/sprite%1.json"_s.arg( resolution > 1 ? u
"@%1x"_s.arg( resolution ) : QString() );
171 const QByteArray spriteFileCandidateBa = spriteFileCandidate.toLocal8Bit();
172 const char *name = spriteFileCandidateBa.constData();
173 struct zip_stat stat;
174 zip_stat_init( &stat );
175 zip_stat( mZip, name, 0, &stat );
180 const size_t len = stat.size;
181 const std::unique_ptr< char[] > buf(
new char[len + 1] );
183 QVariantMap definition;
185 zip_file *file = zip_fopen( mZip, name, 0 );
186 if ( zip_fread( file, buf.get(), len ) != -1 )
189 std::string jsonString( buf.get() );
204 return QVariantMap();
212 for (
int resolution = 2; resolution > 0; resolution-- )
214 const QString spriteFileCandidate = u
"p12/resources/sprites/sprite%1.png"_s.arg( resolution > 1 ? u
"@%1x"_s.arg( resolution ) : QString() );
215 const QByteArray spriteFileCandidateBa = spriteFileCandidate.toLocal8Bit();
216 const char *name = spriteFileCandidateBa.constData();
217 struct zip_stat stat;
218 zip_stat_init( &stat );
219 zip_stat( mZip, name, 0, &stat );
224 const size_t len = stat.size;
225 const std::unique_ptr< char[] > buf(
new char[len + 1] );
229 zip_file *file = zip_fopen( mZip, name, 0 );
230 if ( zip_fread( file, buf.get(), len ) != -1 )
234 result = QImage::fromData(
reinterpret_cast<const uchar *
>( buf.get() ), len );
257 const char *name =
"esriinfo/iteminfo.xml";
258 struct zip_stat stat;
259 zip_stat_init( &stat );
260 zip_stat( mZip, name, 0, &stat );
262 const size_t len = stat.size;
263 QByteArray buf( len, Qt::Uninitialized );
267 zip_file *file = zip_fopen( mZip, name, 0 );
268 if ( zip_fread( file, buf.data(), len ) != -1 )
274 QString errorMessage;
277 if ( !doc.setContent( buf,
false, &errorMessage, &errorLine, &errorColumn ) )
279 QgsMessageLog::logMessage( QObject::tr(
"Error reading layer metadata (line %1, col %2): %3" ).arg( errorLine ).arg( errorColumn ).arg( errorMessage ) );
285 const QDomElement infoElement = doc.firstChildElement( u
"ESRI_ItemInformation"_s );
287 metadata.setLanguage( infoElement.attribute( u
"Culture"_s ) );
289 const QDomElement guidElement = infoElement.firstChildElement( u
"guid"_s );
290 metadata.setIdentifier( guidElement.text() );
292 const QDomElement nameElement = infoElement.firstChildElement( u
"name"_s );
293 metadata.setTitle( nameElement.text() );
295 const QDomElement descriptionElement = infoElement.firstChildElement( u
"description"_s );
296 metadata.setAbstract( QTextDocumentFragment::fromHtml( descriptionElement.text() ).toPlainText() );
298 const QDomElement tagsElement = infoElement.firstChildElement( u
"tags"_s );
300 const QStringList rawTags = tagsElement.text().split(
',' );
302 tags.reserve( rawTags.size() );
303 for (
const QString &tag : rawTags )
304 tags.append( tag.trimmed() );
305 metadata.addKeywords( u
"keywords"_s, tags );
307 const QDomElement accessInformationElement = infoElement.firstChildElement( u
"accessinformation"_s );
308 metadata.setRights( { accessInformationElement.text() } );
310 const QDomElement licenseInfoElement = infoElement.firstChildElement( u
"licenseinfo"_s );
311 metadata.setLicenses( { QTextDocumentFragment::fromHtml( licenseInfoElement.text() ).toPlainText() } );
313 const QDomElement extentElement = infoElement.firstChildElement( u
"extent"_s );
314 const double xMin = extentElement.firstChildElement( u
"xmin"_s ).text().toDouble();
315 const double xMax = extentElement.firstChildElement( u
"xmax"_s ).text().toDouble();
316 const double yMin = extentElement.firstChildElement( u
"ymin"_s ).text().toDouble();
317 const double yMax = extentElement.firstChildElement( u
"ymax"_s ).text().toDouble();
325 extent.setSpatialExtents( { spatialExtent } );
347 if ( mHasReadTileMap || mTileMapPath.isEmpty() )
351 return QVariantMap();
353 const QString tileMapPath = u
"p12/%1/root.json"_s.arg( mTileMapPath );
354 struct zip_stat stat;
355 zip_stat_init( &stat );
356 zip_stat( mZip, tileMapPath.toLocal8Bit().constData(), 0, &stat );
358 const size_t len = stat.size;
359 const std::unique_ptr< char[] > buf(
new char[len + 1] );
362 zip_file *file = zip_fopen( mZip, tileMapPath.toLocal8Bit().constData(), 0 );
365 QgsDebugError( u
"Tilemap %1 was not found in vtpk archive"_s.arg( tileMapPath ) );
366 mTileMapPath.clear();
370 if ( zip_fread( file, buf.get(), len ) != -1 )
373 std::string jsonString( buf.get() );
383 QgsDebugError( u
"Tilemap %1 could not be read from vtpk archive"_s.arg( tileMapPath ) );
384 mTileMapPath.clear();
386 mHasReadTileMap =
true;
392 if ( !mMatrixSet.isEmpty() )
408 const QVariantMap fullExtent = md.value( u
"fullExtent"_s ).toMap();
409 if ( !fullExtent.isEmpty() )
411 QgsRectangle fullExtentRect( fullExtent.value( u
"xmin"_s ).toDouble(), fullExtent.value( u
"ymin"_s ).toDouble(), fullExtent.value( u
"xmax"_s ).toDouble(), fullExtent.value( u
"ymax"_s ).toDouble() );
421 QgsDebugError( u
"Could not transform layer fullExtent to layer CRS"_s );
432 QgsDebugError( u
"VTPK tile package not open: "_s + mFilename );
435 if ( mPacketSize < 0 )
436 mPacketSize =
metadata().value( u
"resourceInfo"_s ).toMap().value( u
"cacheInfo"_s ).toMap().value( u
"storageInfo"_s ).toMap().value( u
"packetSize"_s ).toInt();
438 const int fileRow = mPacketSize *
static_cast< int >( std::floor( y /
static_cast< double>( mPacketSize ) ) );
439 const int fileCol = mPacketSize *
static_cast< int >( std::floor( x /
static_cast< double>( mPacketSize ) ) );
441 const QString tileName = u
"R%1C%2"_s.arg( fileRow, 4, 16,
'0'_L1 ).arg( fileCol, 4, 16,
'0'_L1 );
443 const QString fileName = u
"p12/tile/L%1/%2.bundle"_s.arg( z, 2, 10,
'0'_L1 ).arg( tileName );
444 struct zip_stat stat;
445 zip_stat_init( &stat );
446 zip_stat( mZip, fileName.toLocal8Bit().constData(), 0, &stat );
448 const size_t tileIndexOffset = 64 + 8 * ( mPacketSize * ( y % mPacketSize ) + ( x % mPacketSize ) );
451 const size_t len = stat.size;
452 if ( len <= tileIndexOffset )
455 res = QByteArray(
"" );
459 const std::unique_ptr< char[] > buf(
new char[len] );
462 zip_file *file = zip_fopen( mZip, fileName.toLocal8Bit().constData(), 0 );
463 if ( zip_fread( file, buf.get(), len ) != -1 )
465 unsigned long long indexValue;
466 memcpy( &indexValue, buf.get() + tileIndexOffset, 8 );
468 const std::size_t tileOffset = indexValue % ( 2ULL << 39 );
469 const std::size_t tileSize =
static_cast< std::size_t
>( std::floor( indexValue / ( 2ULL << 39 ) ) );
474 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.
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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.
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.
static bool decodeGzip(const QByteArray &bytesIn, QByteArray &bytesOut)
Decodes gzip byte stream, returns true on success.
#define QgsDebugError(str)