29#include <QDomDocument>
30#include <QTextDocumentFragment>
36 : mFilename( filename )
54 const QByteArray fileNamePtr = mFilename.toUtf8();
56 mZip = zip_open( fileNamePtr.constData(), ZIP_CHECKCONS, &rc );
57 if ( rc == ZIP_ER_OK && mZip )
59 const int count = zip_get_num_files( mZip );
74 QgsMessageLog::logMessage( QObject::tr(
"Error opening zip archive: '%1' (Error code: %2)" ).arg( mZip ? zip_strerror( mZip ) : mFilename ).arg( rc ) );
86 return static_cast< bool >( mZip );
91 if ( !mMetadata.isEmpty() )
97 const char *name =
"p12/root.json";
99 zip_stat_init( &stat );
100 zip_stat( mZip, name, 0, &stat );
102 const size_t len = stat.size;
103 const std::unique_ptr< char[] > buf(
new char[len + 1] );
106 zip_file *file = zip_fopen( mZip, name, 0 );
107 if ( zip_fread( file, buf.get(), len ) != -1 )
110 std::string jsonString( buf.get( ) );
127 return QVariantMap();
129 const char *name =
"p12/resources/styles/root.json";
130 struct zip_stat stat;
131 zip_stat_init( &stat );
132 zip_stat( mZip, name, 0, &stat );
134 const size_t len = stat.size;
135 const std::unique_ptr< char[] > buf(
new char[len + 1] );
139 zip_file *file = zip_fopen( mZip, name, 0 );
140 if ( zip_fread( file, buf.get(), len ) != -1 )
143 std::string jsonString( buf.get( ) );
160 return QVariantMap();
162 for (
int resolution = 2; resolution > 0; resolution-- )
164 const QString spriteFileCandidate = QStringLiteral(
"p12/resources/sprites/sprite%1.json" ).arg( resolution > 1 ? QStringLiteral(
"@%1x" ).arg( resolution ) : QString() );
165 const QByteArray spriteFileCandidateBa = spriteFileCandidate.toLocal8Bit();
166 const char *name = spriteFileCandidateBa.constData();
167 struct zip_stat stat;
168 zip_stat_init( &stat );
169 zip_stat( mZip, name, 0, &stat );
174 const size_t len = stat.size;
175 const std::unique_ptr< char[] > buf(
new char[len + 1] );
177 QVariantMap definition;
179 zip_file *file = zip_fopen( mZip, name, 0 );
180 if ( zip_fread( file, buf.get(), len ) != -1 )
183 std::string jsonString( buf.get( ) );
197 return QVariantMap();
205 for (
int resolution = 2; resolution > 0; resolution-- )
207 const QString spriteFileCandidate = QStringLiteral(
"p12/resources/sprites/sprite%1.png" ).arg( resolution > 1 ? QStringLiteral(
"@%1x" ).arg( resolution ) : QString() );
208 const QByteArray spriteFileCandidateBa = spriteFileCandidate.toLocal8Bit();
209 const char *name = spriteFileCandidateBa.constData();
210 struct zip_stat stat;
211 zip_stat_init( &stat );
212 zip_stat( mZip, name, 0, &stat );
217 const size_t len = stat.size;
218 const std::unique_ptr< char[] > buf(
new char[len + 1] );
222 zip_file *file = zip_fopen( mZip, name, 0 );
223 if ( zip_fread( file, buf.get(), len ) != -1 )
227 result = QImage::fromData(
reinterpret_cast<const uchar *
>( buf.get() ), len );
249 const char *name =
"esriinfo/iteminfo.xml";
250 struct zip_stat stat;
251 zip_stat_init( &stat );
252 zip_stat( mZip, name, 0, &stat );
254 const size_t len = stat.size;
255 QByteArray buf( len, Qt::Uninitialized );
259 zip_file *file = zip_fopen( mZip, name, 0 );
260 if ( zip_fread( file, buf.data(), len ) != -1 )
266 QString errorMessage;
269 if ( !doc.setContent( buf,
false, &errorMessage, &errorLine, &errorColumn ) )
271 QgsMessageLog::logMessage( QObject::tr(
"Error reading layer metadata (line %1, col %2): %3" ).arg( errorLine ).arg( errorColumn ).arg( errorMessage ) );
275 metadata.setType( QStringLiteral(
"dataset" ) );
277 const QDomElement infoElement = doc.firstChildElement( QStringLiteral(
"ESRI_ItemInformation" ) );
279 metadata.setLanguage( infoElement.attribute( QStringLiteral(
"Culture" ) ) );
281 const QDomElement guidElement = infoElement.firstChildElement( QStringLiteral(
"guid" ) );
282 metadata.setIdentifier( guidElement.text() );
284 const QDomElement nameElement = infoElement.firstChildElement( QStringLiteral(
"name" ) );
285 metadata.setTitle( nameElement.text() );
287 const QDomElement descriptionElement = infoElement.firstChildElement( QStringLiteral(
"description" ) );
288 metadata.setAbstract( QTextDocumentFragment::fromHtml( descriptionElement.text() ).toPlainText() );
290 const QDomElement tagsElement = infoElement.firstChildElement( QStringLiteral(
"tags" ) );
292 const QStringList rawTags = tagsElement.text().split(
',' );
294 tags.reserve( rawTags.size() );
295 for (
const QString &tag : rawTags )
296 tags.append( tag.trimmed() );
297 metadata.addKeywords( QStringLiteral(
"keywords" ), tags );
299 const QDomElement accessInformationElement = infoElement.firstChildElement( QStringLiteral(
"accessinformation" ) );
300 metadata.setRights( { accessInformationElement.text() } );
302 const QDomElement licenseInfoElement = infoElement.firstChildElement( QStringLiteral(
"licenseinfo" ) );
303 metadata.setLicenses( { QTextDocumentFragment::fromHtml( licenseInfoElement.text() ).toPlainText() } );
305 const QDomElement extentElement = infoElement.firstChildElement( QStringLiteral(
"extent" ) );
306 const double xMin = extentElement.firstChildElement( QStringLiteral(
"xmin" ) ).text().toDouble();
307 const double xMax = extentElement.firstChildElement( QStringLiteral(
"xmax" ) ).text().toDouble();
308 const double yMin = extentElement.firstChildElement( QStringLiteral(
"ymin" ) ).text().toDouble();
309 const double yMax = extentElement.firstChildElement( QStringLiteral(
"ymax" ) ).text().toDouble();
317 extent.setSpatialExtents( { spatialExtent } );
351 const QVariantMap fullExtent = md.value( QStringLiteral(
"fullExtent" ) ).toMap();
352 if ( !fullExtent.isEmpty() )
355 fullExtent.value( QStringLiteral(
"xmin" ) ).toDouble(),
356 fullExtent.value( QStringLiteral(
"ymin" ) ).toDouble(),
357 fullExtent.value( QStringLiteral(
"xmax" ) ).toDouble(),
358 fullExtent.value( QStringLiteral(
"ymax" ) ).toDouble()
369 QgsDebugMsg( QStringLiteral(
"Could not transform layer fullExtent to layer CRS" ) );
380 QgsDebugMsg( QStringLiteral(
"VTPK tile package not open: " ) + mFilename );
383 if ( mPacketSize < 0 )
384 mPacketSize =
metadata().value( QStringLiteral(
"resourceInfo" ) ).toMap().value( QStringLiteral(
"cacheInfo" ) ).toMap().value( QStringLiteral(
"storageInfo" ) ).toMap().value( QStringLiteral(
"packetSize" ) ).toInt();
386 const int fileRow = mPacketSize *
static_cast< int >( std::floor( y /
static_cast< double>( mPacketSize ) ) );
387 const int fileCol = mPacketSize *
static_cast< int >( std::floor( x /
static_cast< double>( mPacketSize ) ) );
389 const QString tileName = QStringLiteral(
"R%1C%2" )
390 .arg( fileRow, 4, 16, QLatin1Char(
'0' ) )
391 .arg( fileCol, 4, 16, QLatin1Char(
'0' ) );
393 const QString fileName = QStringLiteral(
"p12/tile/L%1/%2.bundle" )
394 .arg( z, 2, 10, QLatin1Char(
'0' ) ).arg( tileName );
395 struct zip_stat stat;
396 zip_stat_init( &stat );
397 zip_stat( mZip, fileName.toLocal8Bit().constData(), 0, &stat );
399 const size_t tileIndexOffset = 64 + 8 * ( mPacketSize * ( y % mPacketSize ) + ( x % mPacketSize ) );
402 const size_t len = stat.size;
403 if ( len <= tileIndexOffset )
405 QgsMessageLog::logMessage( QObject::tr(
"Cannot read gzip contents at offset %1: %2" ).arg( tileIndexOffset ).arg( fileName ) );
409 const std::unique_ptr< char[] > buf(
new char[len] );
412 zip_file *file = zip_fopen( mZip, fileName.toLocal8Bit().constData(), 0 );
413 if ( zip_fread( file, buf.get(), len ) != -1 )
415 unsigned long long indexValue;
416 memcpy( &indexValue, buf.get() + tileIndexOffset, 8 );
418 const std::size_t tileOffset = indexValue % ( 2ULL << 39 );
419 const std::size_t tileSize =
static_cast< std::size_t
>( std::floor( indexValue / ( 2ULL << 39 ) ) );
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)
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 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.