29#include <QDomDocument>
33#include <QTextDocumentFragment>
35using namespace Qt::StringLiterals;
38 : mFilename( filename )
56 const QByteArray fileNamePtr = mFilename.toUtf8();
58 mZip = zip_open( fileNamePtr.constData(), ZIP_CHECKCONS, &rc );
59 if ( rc == ZIP_ER_OK && mZip )
61 const int count = zip_get_num_entries( mZip, ZIP_FL_UNCHANGED );
76 QgsMessageLog::logMessage( QObject::tr(
"Error opening zip archive: '%1' (Error code: %2)" ).arg( mZip ? zip_strerror( mZip ) : mFilename ).arg( rc ) );
88 return static_cast< bool >( mZip );
93 if ( !mMetadata.isEmpty() )
99 const char *name =
"p12/root.json";
100 struct zip_stat stat;
101 zip_stat_init( &stat );
102 zip_stat( mZip, name, 0, &stat );
104 const size_t len = stat.size;
105 const std::unique_ptr< char[] > buf(
new char[len + 1] );
108 zip_file *file = zip_fopen( mZip, name, 0 );
109 if ( zip_fread( file, buf.get(), len ) != -1 )
112 std::string jsonString( buf.get( ) );
125 mTileMapPath = mMetadata.value( u
"tileMap"_s ).toString();
133 return QVariantMap();
135 const char *name =
"p12/resources/styles/root.json";
136 struct zip_stat stat;
137 zip_stat_init( &stat );
138 zip_stat( mZip, name, 0, &stat );
140 const size_t len = stat.size;
141 const std::unique_ptr< char[] > buf(
new char[len + 1] );
145 zip_file *file = zip_fopen( mZip, name, 0 );
146 if ( zip_fread( file, buf.get(), len ) != -1 )
149 std::string jsonString( buf.get( ) );
167 return QVariantMap();
169 for (
int resolution = 2; resolution > 0; resolution-- )
171 const QString spriteFileCandidate = u
"p12/resources/sprites/sprite%1.json"_s.arg( resolution > 1 ? u
"@%1x"_s.arg( resolution ) : QString() );
172 const QByteArray spriteFileCandidateBa = spriteFileCandidate.toLocal8Bit();
173 const char *name = spriteFileCandidateBa.constData();
174 struct zip_stat stat;
175 zip_stat_init( &stat );
176 zip_stat( mZip, name, 0, &stat );
181 const size_t len = stat.size;
182 const std::unique_ptr< char[] > buf(
new char[len + 1] );
184 QVariantMap definition;
186 zip_file *file = zip_fopen( mZip, name, 0 );
187 if ( zip_fread( file, buf.get(), len ) != -1 )
190 std::string jsonString( buf.get( ) );
205 return QVariantMap();
213 for (
int resolution = 2; resolution > 0; resolution-- )
215 const QString spriteFileCandidate = u
"p12/resources/sprites/sprite%1.png"_s.arg( resolution > 1 ? u
"@%1x"_s.arg( resolution ) : QString() );
216 const QByteArray spriteFileCandidateBa = spriteFileCandidate.toLocal8Bit();
217 const char *name = spriteFileCandidateBa.constData();
218 struct zip_stat stat;
219 zip_stat_init( &stat );
220 zip_stat( mZip, name, 0, &stat );
225 const size_t len = stat.size;
226 const std::unique_ptr< char[] > buf(
new char[len + 1] );
230 zip_file *file = zip_fopen( mZip, name, 0 );
231 if ( zip_fread( file, buf.get(), len ) != -1 )
235 result = QImage::fromData(
reinterpret_cast<const uchar *
>( buf.get() ), len );
258 const char *name =
"esriinfo/iteminfo.xml";
259 struct zip_stat stat;
260 zip_stat_init( &stat );
261 zip_stat( mZip, name, 0, &stat );
263 const size_t len = stat.size;
264 QByteArray buf( len, Qt::Uninitialized );
268 zip_file *file = zip_fopen( mZip, name, 0 );
269 if ( zip_fread( file, buf.data(), len ) != -1 )
275 QString errorMessage;
278 if ( !doc.setContent( buf,
false, &errorMessage, &errorLine, &errorColumn ) )
280 QgsMessageLog::logMessage( QObject::tr(
"Error reading layer metadata (line %1, col %2): %3" ).arg( errorLine ).arg( errorColumn ).arg( errorMessage ) );
286 const QDomElement infoElement = doc.firstChildElement( u
"ESRI_ItemInformation"_s );
288 metadata.setLanguage( infoElement.attribute( u
"Culture"_s ) );
290 const QDomElement guidElement = infoElement.firstChildElement( u
"guid"_s );
291 metadata.setIdentifier( guidElement.text() );
293 const QDomElement nameElement = infoElement.firstChildElement( u
"name"_s );
294 metadata.setTitle( nameElement.text() );
296 const QDomElement descriptionElement = infoElement.firstChildElement( u
"description"_s );
297 metadata.setAbstract( QTextDocumentFragment::fromHtml( descriptionElement.text() ).toPlainText() );
299 const QDomElement tagsElement = infoElement.firstChildElement( u
"tags"_s );
301 const QStringList rawTags = tagsElement.text().split(
',' );
303 tags.reserve( rawTags.size() );
304 for (
const QString &tag : rawTags )
305 tags.append( tag.trimmed() );
306 metadata.addKeywords( u
"keywords"_s, tags );
308 const QDomElement accessInformationElement = infoElement.firstChildElement( u
"accessinformation"_s );
309 metadata.setRights( { accessInformationElement.text() } );
311 const QDomElement licenseInfoElement = infoElement.firstChildElement( u
"licenseinfo"_s );
312 metadata.setLicenses( { QTextDocumentFragment::fromHtml( licenseInfoElement.text() ).toPlainText() } );
314 const QDomElement extentElement = infoElement.firstChildElement( u
"extent"_s );
315 const double xMin = extentElement.firstChildElement( u
"xmin"_s ).text().toDouble();
316 const double xMax = extentElement.firstChildElement( u
"xmax"_s ).text().toDouble();
317 const double yMin = extentElement.firstChildElement( u
"ymin"_s ).text().toDouble();
318 const double yMax = extentElement.firstChildElement( u
"ymax"_s ).text().toDouble();
326 extent.setSpatialExtents( { spatialExtent } );
348 if ( mHasReadTileMap || mTileMapPath.isEmpty() )
352 return QVariantMap();
354 const QString tileMapPath = u
"p12/%1/root.json"_s.arg( mTileMapPath );
355 struct zip_stat stat;
356 zip_stat_init( &stat );
357 zip_stat( mZip, tileMapPath.toLocal8Bit().constData(), 0, &stat );
359 const size_t len = stat.size;
360 const std::unique_ptr< char[] > buf(
new char[len + 1] );
363 zip_file *file = zip_fopen( mZip, tileMapPath.toLocal8Bit().constData(), 0 );
366 QgsDebugError( u
"Tilemap %1 was not found in vtpk archive"_s.arg( tileMapPath ) );
367 mTileMapPath.clear();
371 if ( zip_fread( file, buf.get(), len ) != -1 )
374 std::string jsonString( buf.get( ) );
384 QgsDebugError( u
"Tilemap %1 could not be read from vtpk archive"_s.arg( tileMapPath ) );
385 mTileMapPath.clear();
387 mHasReadTileMap =
true;
393 if ( !mMatrixSet.isEmpty() )
409 const QVariantMap fullExtent = md.value( u
"fullExtent"_s ).toMap();
410 if ( !fullExtent.isEmpty() )
413 fullExtent.value( u
"xmin"_s ).toDouble(),
414 fullExtent.value( u
"ymin"_s ).toDouble(),
415 fullExtent.value( u
"xmax"_s ).toDouble(),
416 fullExtent.value( u
"ymax"_s ).toDouble()
427 QgsDebugError( u
"Could not transform layer fullExtent to layer CRS"_s );
438 QgsDebugError( u
"VTPK tile package not open: "_s + mFilename );
441 if ( mPacketSize < 0 )
442 mPacketSize =
metadata().value( u
"resourceInfo"_s ).toMap().value( u
"cacheInfo"_s ).toMap().value( u
"storageInfo"_s ).toMap().value( u
"packetSize"_s ).toInt();
444 const int fileRow = mPacketSize *
static_cast< int >( std::floor( y /
static_cast< double>( mPacketSize ) ) );
445 const int fileCol = mPacketSize *
static_cast< int >( std::floor( x /
static_cast< double>( mPacketSize ) ) );
447 const QString tileName = u
"R%1C%2"_s
448 .arg( fileRow, 4, 16,
'0'_L1 )
449 .arg( fileCol, 4, 16,
'0'_L1 );
451 const QString fileName = u
"p12/tile/L%1/%2.bundle"_s
452 .arg( z, 2, 10,
'0'_L1 ).arg( tileName );
453 struct zip_stat stat;
454 zip_stat_init( &stat );
455 zip_stat( mZip, fileName.toLocal8Bit().constData(), 0, &stat );
457 const size_t tileIndexOffset = 64 + 8 * ( mPacketSize * ( y % mPacketSize ) + ( x % mPacketSize ) );
460 const size_t len = stat.size;
461 if ( len <= tileIndexOffset )
464 res = QByteArray(
"" );
468 const std::unique_ptr< char[] > buf(
new char[len] );
471 zip_file *file = zip_fopen( mZip, fileName.toLocal8Bit().constData(), 0 );
472 if ( zip_fread( file, buf.get(), len ) != -1 )
474 unsigned long long indexValue;
475 memcpy( &indexValue, buf.get() + tileIndexOffset, 8 );
477 const std::size_t tileOffset = indexValue % ( 2ULL << 39 );
478 const std::size_t tileSize =
static_cast< std::size_t
>( std::floor( indexValue / ( 2ULL << 39 ) ) );
483 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())
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)