QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsalgorithmxyztiles.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmxyztiles.cpp
3 ---------------------
4 begin : August 2023
5 copyright : (C) 2023 by Alexander Bruy
6 email : alexander dot bruy at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include <QBuffer>
21
22#include "qgslayertree.h"
23#include "qgslayertreelayer.h"
25
27
28int tile2tms( const int y, const int zoom )
29{
30 double n = std::pow( 2, zoom );
31 return ( int )std::floor( n - y - 1 );
32}
33
34int lon2tileX( const double lon, const int z )
35{
36 return ( int )( std::floor( ( lon + 180.0 ) / 360.0 * ( 1 << z ) ) );
37}
38
39int lat2tileY( const double lat, const int z )
40{
41 double latRad = lat * M_PI / 180.0;
42 return ( int )( std::floor( ( 1.0 - std::asinh( std::tan( latRad ) ) / M_PI ) / 2.0 * ( 1 << z ) ) );
43}
44
45double tileX2lon( const int x, const int z )
46{
47 return x / ( double )( 1 << z ) * 360.0 - 180 ;
48}
49
50double tileY2lat( const int y, const int z )
51{
52 double n = M_PI - 2.0 * M_PI * y / ( double )( 1 << z );
53 return 180.0 / M_PI * std::atan( 0.5 * ( std::exp( n ) - std::exp( -n ) ) );
54}
55
56void extent2TileXY( QgsRectangle extent, const int zoom, int &xMin, int &yMin, int &xMax, int &yMax )
57{
58 xMin = lon2tileX( extent.xMinimum(), zoom );
59 yMin = lat2tileY( extent.yMinimum(), zoom );
60 xMax = lon2tileX( extent.xMaximum(), zoom );
61 yMax = lat2tileY( extent.xMaximum(), zoom );
62}
63
64QList< MetaTile > getMetatiles( const QgsRectangle extent, const int zoom, const int tileSize )
65{
66 int minX = lon2tileX( extent.xMinimum(), zoom );
67 int minY = lat2tileY( extent.yMaximum(), zoom );
68 int maxX = lon2tileX( extent.xMaximum(), zoom );
69 int maxY = lat2tileY( extent.yMinimum(), zoom );;
70
71 int i = 0;
72 QMap< QString, MetaTile > tiles;
73 for ( int x = minX; x <= maxX; x++ )
74 {
75 int j = 0;
76 for ( int y = minY; y <= maxY; y++ )
77 {
78 QString key = QStringLiteral( "%1:%2" ).arg( ( int )( i / tileSize ) ).arg( ( int )( j / tileSize ) );
79 MetaTile tile = tiles.value( key, MetaTile() );
80 tile.addTile( i % tileSize, j % tileSize, Tile( x, y, zoom ) );
81 tiles.insert( key, tile );
82 j++;
83 }
84 i++;
85 }
86 return tiles.values();
87}
88
90
91QString QgsXyzTilesBaseAlgorithm::group() const
92{
93 return QObject::tr( "Raster tools" );
94}
95
96QString QgsXyzTilesBaseAlgorithm::groupId() const
97{
98 return QStringLiteral( "rastertools" );
99}
100
101Qgis::ProcessingAlgorithmFlags QgsXyzTilesBaseAlgorithm::flags() const
102{
104}
105
106void QgsXyzTilesBaseAlgorithm::createCommonParameters()
107{
108 addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ) ) );
109 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "ZOOM_MIN" ), QObject::tr( "Minimum zoom" ), Qgis::ProcessingNumberParameterType::Integer, 12, false, 0, 25 ) );
110 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "ZOOM_MAX" ), QObject::tr( "Maximum zoom" ), Qgis::ProcessingNumberParameterType::Integer, 12, false, 0, 25 ) );
111 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "DPI" ), QObject::tr( "DPI" ), Qgis::ProcessingNumberParameterType::Integer, 96, false, 48, 600 ) );
112 addParameter( new QgsProcessingParameterColor( QStringLiteral( "BACKGROUND_COLOR" ), QObject::tr( "Background color" ), QColor( Qt::transparent ), true, true ) );
113 addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "ANTIALIAS" ), QObject::tr( "Enable antialiasing" ), true ) );
114 addParameter( new QgsProcessingParameterEnum( QStringLiteral( "TILE_FORMAT" ), QObject::tr( "Tile format" ), QStringList() << QStringLiteral( "PNG" ) << QStringLiteral( "JPG" ), false, 0 ) );
115 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "QUALITY" ), QObject::tr( "Quality (JPG only)" ), Qgis::ProcessingNumberParameterType::Integer, 75, false, 1, 100 ) );
116 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "METATILESIZE" ), QObject::tr( "Metatile size" ), Qgis::ProcessingNumberParameterType::Integer, 4, false, 1, 20 ) );
117}
118
119bool QgsXyzTilesBaseAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
120{
121 Q_UNUSED( feedback );
122
123 QgsProject *project = context.project();
124
125 const QList< QgsLayerTreeLayer * > projectLayers = project->layerTreeRoot()->findLayers();
126 QSet< QString > visibleLayers;
127 for ( const QgsLayerTreeLayer *layer : projectLayers )
128 {
129 if ( layer->isVisible() )
130 {
131 visibleLayers << layer->layer()->id();
132 }
133 }
134
135 QList< QgsMapLayer * > renderLayers = project->layerTreeRoot()->layerOrder();
136 for ( QgsMapLayer *layer : renderLayers )
137 {
138 if ( visibleLayers.contains( layer->id() ) )
139 {
140 QgsMapLayer *clonedLayer = layer->clone();
141 clonedLayer->moveToThread( nullptr );
142 mLayers << clonedLayer;
143 }
144 }
145
146 QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context );
147 QgsCoordinateReferenceSystem extentCrs = parameterAsExtentCrs( parameters, QStringLiteral( "EXTENT" ), context );
148 QgsCoordinateTransform ct( extentCrs, project->crs(), context.transformContext() );
149 mExtent = ct.transformBoundingBox( extent );
150
151 mMinZoom = parameterAsInt( parameters, QStringLiteral( "ZOOM_MIN" ), context );
152 mMaxZoom = parameterAsInt( parameters, QStringLiteral( "ZOOM_MAX" ), context );
153 mDpi = parameterAsInt( parameters, QStringLiteral( "DPI" ), context );
154 mBackgroundColor = parameterAsColor( parameters, QStringLiteral( "BACKGROUND_COLOR" ), context );
155 mAntialias = parameterAsBool( parameters, QStringLiteral( "ANTIALIAS" ), context );
156 mTileFormat = parameterAsEnum( parameters, QStringLiteral( "TILE_FORMAT" ), context ) ? QStringLiteral( "JPG" ) : QStringLiteral( "PNG" );
157 mJpgQuality = mTileFormat == QLatin1String( "JPG" ) ? parameterAsInt( parameters, QStringLiteral( "QUALITY" ), context ) : -1;
158 mMetaTileSize = parameterAsInt( parameters, QStringLiteral( "METATILESIZE" ), context );
159 mThreadsNumber = context.maximumThreads();
160 mTransformContext = context.transformContext();
161 mFeedback = feedback;
162
163 mWgs84Crs = QgsCoordinateReferenceSystem( "EPSG:4326" );
164 mMercatorCrs = QgsCoordinateReferenceSystem( "EPSG:3857" );
165 mSrc2Wgs = QgsCoordinateTransform( project->crs(), mWgs84Crs, context.transformContext() );
166 mWgs2Mercator = QgsCoordinateTransform( mWgs84Crs, mMercatorCrs, context.transformContext() );
167
168 mWgs84Extent = mSrc2Wgs.transformBoundingBox( mExtent );
169
170 if ( parameters.contains( QStringLiteral( "TILE_WIDTH" ) ) )
171 {
172 mTileWidth = parameterAsInt( parameters, QStringLiteral( "TILE_WIDTH" ), context );
173 }
174
175 if ( parameters.contains( QStringLiteral( "TILE_HEIGHT" ) ) )
176 {
177 mTileHeight = parameterAsInt( parameters, QStringLiteral( "TILE_HEIGHT" ), context );
178 }
179
180 if ( mTileFormat != QLatin1String( "PNG" ) && mBackgroundColor.alpha() != 255 )
181 {
182 feedback->pushWarning( QObject::tr( "Background color setting ignored, the JPG format only supports fully opaque colors" ) );
183 }
184
185 return true;
186}
187
188void QgsXyzTilesBaseAlgorithm::startJobs()
189{
190 while ( mRendererJobs.size() < mThreadsNumber && !mMetaTiles.empty() )
191 {
192 MetaTile metaTile = mMetaTiles.takeFirst();
193
194 QgsMapSettings settings;
195 settings.setExtent( mWgs2Mercator.transformBoundingBox( metaTile.extent() ) );
196 settings.setOutputImageFormat( QImage::Format_ARGB32_Premultiplied );
197 settings.setTransformContext( mTransformContext );
198 settings.setDestinationCrs( mMercatorCrs );
199 settings.setLayers( mLayers );
200 settings.setOutputDpi( mDpi );
201 settings.setFlag( Qgis::MapSettingsFlag::Antialiasing, mAntialias );
202 if ( mTileFormat == QLatin1String( "PNG" ) || mBackgroundColor.alpha() == 255 )
203 {
204 settings.setBackgroundColor( mBackgroundColor );
205 }
206 QSize size( mTileWidth * metaTile.rows, mTileHeight * metaTile.cols );
207 settings.setOutputSize( size );
208
209 QgsLabelingEngineSettings labelingSettings = settings.labelingEngineSettings();
210 labelingSettings.setFlag( Qgis::LabelingFlag::UsePartialCandidates, false );
211 settings.setLabelingEngineSettings( labelingSettings );
212
213 QgsExpressionContext exprContext = settings.expressionContext();
215 settings.setExpressionContext( exprContext );
216
218 mRendererJobs.insert( job, metaTile );
219 QObject::connect( job, &QgsMapRendererJob::finished, mFeedback, [ this, job ]() { processMetaTile( job ); } );
220 job->start();
221 }
222}
223
224// Native XYZ tiles (directory) algorithm
225
226QString QgsXyzTilesDirectoryAlgorithm::name() const
227{
228 return QStringLiteral( "tilesxyzdirectory" );
229}
230
231QString QgsXyzTilesDirectoryAlgorithm::displayName() const
232{
233 return QObject::tr( "Generate XYZ tiles (Directory)" );
234}
235
236QStringList QgsXyzTilesDirectoryAlgorithm::tags() const
237{
238 return QObject::tr( "tiles,xyz,tms,directory" ).split( ',' );
239}
240
241QString QgsXyzTilesDirectoryAlgorithm::shortHelpString() const
242{
243 return QObject::tr( "Generates XYZ tiles of map canvas content and saves them as individual images in a directory." );
244}
245
246QgsXyzTilesDirectoryAlgorithm *QgsXyzTilesDirectoryAlgorithm::createInstance() const
247{
248 return new QgsXyzTilesDirectoryAlgorithm();
249}
250
251void QgsXyzTilesDirectoryAlgorithm::initAlgorithm( const QVariantMap & )
252{
253 createCommonParameters();
254 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "TILE_WIDTH" ), QObject::tr( "Tile width" ), Qgis::ProcessingNumberParameterType::Integer, 256, false, 1, 4096 ) );
255 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "TILE_HEIGHT" ), QObject::tr( "Tile height" ), Qgis::ProcessingNumberParameterType::Integer, 256, false, 1, 4096 ) );
256 addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "TMS_CONVENTION" ), QObject::tr( "Use inverted tile Y axis (TMS convention)" ), false, true ) );
257
258 std::unique_ptr< QgsProcessingParameterString > titleParam = std::make_unique< QgsProcessingParameterString >( QStringLiteral( "HTML_TITLE" ), QObject::tr( "Leaflet HTML output title" ), QVariant(), false, true );
259 titleParam->setFlags( titleParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
260 addParameter( titleParam.release() );
261 std::unique_ptr< QgsProcessingParameterString > attributionParam = std::make_unique< QgsProcessingParameterString >( QStringLiteral( "HTML_ATTRIBUTION" ), QObject::tr( "Leaflet HTML output attribution" ), QVariant(), false, true );
262 attributionParam->setFlags( attributionParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
263 addParameter( attributionParam.release() );
264 std::unique_ptr< QgsProcessingParameterBoolean > osmParam = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "HTML_OSM" ), QObject::tr( "Include OpenStreetMap basemap in Leaflet HTML output" ), false, true );
265 osmParam->setFlags( osmParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
266 addParameter( osmParam.release() );
267
268 addParameter( new QgsProcessingParameterFolderDestination( QStringLiteral( "OUTPUT_DIRECTORY" ), QObject::tr( "Output directory" ) ) );
269 addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT_HTML" ), QObject::tr( "Output html (Leaflet)" ), QObject::tr( "HTML files (*.html)" ), QVariant(), true ) );
270}
271
272QVariantMap QgsXyzTilesDirectoryAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
273{
274 const bool tms = parameterAsBoolean( parameters, QStringLiteral( "TMS_CONVENTION" ), context );
275 const QString title = parameterAsString( parameters, QStringLiteral( "HTML_TITLE" ), context );
276 const QString attribution = parameterAsString( parameters, QStringLiteral( "HTML_ATTRIBUTION" ), context );
277 const bool useOsm = parameterAsBoolean( parameters, QStringLiteral( "HTML_OSM" ), context );
278 QString outputDir = parameterAsString( parameters, QStringLiteral( "OUTPUT_DIRECTORY" ), context );
279 const QString outputHtml = parameterAsString( parameters, QStringLiteral( "OUTPUT_HTML" ), context );
280
281 mOutputDir = outputDir;
282 mTms = tms;
283
284 for ( int z = mMinZoom; z <= mMaxZoom; z++ )
285 {
286 if ( feedback->isCanceled() )
287 break;
288
289 mMetaTiles += getMetatiles( mWgs84Extent, z, mMetaTileSize );
290 }
291
292 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
293 {
294 layer->moveToThread( QThread::currentThread() );
295 }
296
297 mTotalTiles = mMetaTiles.size();
298
299 QEventLoop loop;
300 // cppcheck-suppress danglingLifetime
301 mEventLoop = &loop;
302 startJobs();
303 loop.exec();
304
305 qDeleteAll( mLayers );
306 mLayers.clear();
307
308 QVariantMap results;
309 results.insert( QStringLiteral( "OUTPUT_DIRECTORY" ), outputDir );
310
311 if ( !outputHtml.isEmpty() )
312 {
313 QString osm = QStringLiteral(
314 "var osm_layer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',"
315 "{minZoom: %1, maxZoom: %2, attribution: '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'}).addTo(map);" )
316 .arg( mMinZoom ).arg( mMaxZoom );
317
318 QString addOsm = useOsm ? osm : QString();
319 QString tmsConvention = tms ? QStringLiteral( "true" ) : QStringLiteral( "false" );
320 QString attr = attribution.isEmpty() ? QStringLiteral( "Created by QGIS" ) : attribution;
321 QString tileSource = QStringLiteral( "'file:///%1/{z}/{x}/{y}.%2'" )
322 .arg( outputDir.replace( "\\", "/" ).toHtmlEscaped() ).arg( mTileFormat.toLower() );
323
324 QString html = QStringLiteral(
325 "<!DOCTYPE html><html><head><title>%1</title><meta charset=\"utf-8\"/>"
326 "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
327 "<link rel=\"stylesheet\" href=\"https://unpkg.com/[email protected]/dist/leaflet.css\""
328 "integrity=\"sha384-o/2yZuJZWGJ4s/adjxVW71R+EO/LyCwdQfP5UWSgX/w87iiTXuvDZaejd3TsN7mf\""
329 "crossorigin=\"\"/>"
330 "<script src=\"https://unpkg.com/[email protected]/dist/leaflet.js\""
331 "integrity=\"sha384-okbbMvvx/qfQkmiQKfd5VifbKZ/W8p1qIsWvE1ROPUfHWsDcC8/BnHohF7vPg2T6\""
332 "crossorigin=\"\"></script>"
333 "<style type=\"text/css\">body {margin: 0;padding: 0;} html, body, #map{width: 100%;height: 100%;}</style></head>"
334 "<body><div id=\"map\"></div><script>"
335 "var map = L.map('map', {attributionControl: false}).setView([%2, %3], %4);"
336 "L.control.attribution({prefix: false}).addTo(map);"
337 "%5"
338 "var tilesource_layer = L.tileLayer(%6, {minZoom: %7, maxZoom: %8, tms: %9, attribution: '%10'}).addTo(map);"
339 "</script></body></html>"
340 )
341 .arg( title.isEmpty() ? QStringLiteral( "Leaflet preview" ) : title )
342 .arg( mWgs84Extent.center().y() )
343 .arg( mWgs84Extent.center().x() )
344 .arg( ( mMaxZoom + mMinZoom ) / 2 )
345 .arg( addOsm )
346 .arg( tileSource )
347 .arg( mMinZoom )
348 .arg( mMaxZoom )
349 .arg( tmsConvention )
350 .arg( attr );
351
352 QFile htmlFile( outputHtml );
353 if ( !htmlFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
354 {
355 throw QgsProcessingException( QObject::tr( "Could not open html file %1" ).arg( outputHtml ) );
356 }
357 QTextStream fout( &htmlFile );
358#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
359 fout.setCodec( "UTF-8" );
360#endif
361 fout << html;
362
363 results.insert( QStringLiteral( "OUTPUT_HTML" ), outputHtml );
364 }
365
366 return results;
367}
368
369void QgsXyzTilesDirectoryAlgorithm::processMetaTile( QgsMapRendererSequentialJob *job )
370{
371 MetaTile metaTile = mRendererJobs.value( job );
372 QImage img = job->renderedImage();
373
374 QMap<QPair<int, int>, Tile>::const_iterator it = metaTile.tiles.constBegin();
375 while ( it != metaTile.tiles.constEnd() )
376 {
377 QPair<int, int> tm = it.key();
378 Tile tile = it.value();
379 QImage tileImage = img.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
380 QDir tileDir( QStringLiteral( "%1/%2/%3" ).arg( mOutputDir ).arg( tile.z ).arg( tile.x ) );
381 tileDir.mkpath( tileDir.absolutePath() );
382 int y = tile.y;
383 if ( mTms )
384 {
385 y = tile2tms( y, tile.z );
386 }
387 tileImage.save( QStringLiteral( "%1/%2.%3" ).arg( tileDir.absolutePath() ).arg( y ).arg( mTileFormat.toLower() ), mTileFormat.toStdString().c_str(), mJpgQuality );
388 ++it;
389 }
390
391 mRendererJobs.remove( job );
392 job->deleteLater();
393
394 mFeedback->setProgress( 100.0 * ( mProcessedTiles++ ) / mTotalTiles );
395
396 if ( mFeedback->isCanceled() )
397 {
398 while ( mRendererJobs.size() > 0 )
399 {
400 QgsMapRendererSequentialJob *j = mRendererJobs.firstKey();
401 j->cancel();
402 mRendererJobs.remove( j );
403 j->deleteLater();
404 }
405 mRendererJobs.clear();
406 mEventLoop->exit();
407 return;
408 }
409
410 if ( mMetaTiles.size() > 0 )
411 {
412 startJobs();
413 }
414 else if ( mMetaTiles.size() == 0 && mRendererJobs.size() == 0 )
415 {
416 mEventLoop->exit();
417 }
418}
419
420// Native XYZ tiles (MBTiles) algorithm
421
422QString QgsXyzTilesMbtilesAlgorithm::name() const
423{
424 return QStringLiteral( "tilesxyzmbtiles" );
425}
426
427QString QgsXyzTilesMbtilesAlgorithm::displayName() const
428{
429 return QObject::tr( "Generate XYZ tiles (MBTiles)" );
430}
431
432QStringList QgsXyzTilesMbtilesAlgorithm::tags() const
433{
434 return QObject::tr( "tiles,xyz,tms,mbtiles" ).split( ',' );
435}
436
437QString QgsXyzTilesMbtilesAlgorithm::shortHelpString() const
438{
439 return QObject::tr( "Generates XYZ tiles of map canvas content and saves them as an MBTiles file." );
440}
441
442QgsXyzTilesMbtilesAlgorithm *QgsXyzTilesMbtilesAlgorithm::createInstance() const
443{
444 return new QgsXyzTilesMbtilesAlgorithm();
445}
446
447void QgsXyzTilesMbtilesAlgorithm::initAlgorithm( const QVariantMap & )
448{
449 createCommonParameters();
450 addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT_FILE" ), QObject::tr( "Output" ), QObject::tr( "MBTiles files (*.mbtiles *.MBTILES)" ) ) );
451}
452
453QVariantMap QgsXyzTilesMbtilesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
454{
455 const QString outputFile = parameterAsString( parameters, QStringLiteral( "OUTPUT_FILE" ), context );
456
457 mMbtilesWriter = std::make_unique<QgsMbTiles>( outputFile );
458 if ( !mMbtilesWriter->create() )
459 {
460 throw QgsProcessingException( QObject::tr( "Failed to create MBTiles file %1" ).arg( outputFile ) );
461 }
462 mMbtilesWriter->setMetadataValue( "format", mTileFormat.toLower() );
463 mMbtilesWriter->setMetadataValue( "name", QFileInfo( outputFile ).baseName() );
464 mMbtilesWriter->setMetadataValue( "version", QStringLiteral( "1.1" ) );
465 mMbtilesWriter->setMetadataValue( "type", QStringLiteral( "overlay" ) );
466 mMbtilesWriter->setMetadataValue( "minzoom", QString::number( mMinZoom ) );
467 mMbtilesWriter->setMetadataValue( "maxzoom", QString::number( mMaxZoom ) );
468 QString boundsStr = QString( "%1,%2,%3,%4" )
469 .arg( mWgs84Extent.xMinimum() ).arg( mWgs84Extent.yMinimum() )
470 .arg( mWgs84Extent.xMaximum() ).arg( mWgs84Extent.yMaximum() );
471 mMbtilesWriter->setMetadataValue( "bounds", boundsStr );
472
473 for ( int z = mMinZoom; z <= mMaxZoom; z++ )
474 {
475 if ( feedback->isCanceled() )
476 break;
477
478 mMetaTiles += getMetatiles( mWgs84Extent, z, mMetaTileSize );
479 }
480
481 mTotalTiles = mMetaTiles.size();
482
483 QEventLoop loop;
484 // cppcheck-suppress danglingLifetime
485 mEventLoop = &loop;
486 startJobs();
487 loop.exec();
488
489 QVariantMap results;
490 results.insert( QStringLiteral( "OUTPUT_FILE" ), outputFile );
491 return results;
492}
493
494void QgsXyzTilesMbtilesAlgorithm::processMetaTile( QgsMapRendererSequentialJob *job )
495{
496 MetaTile metaTile = mRendererJobs.value( job );
497 QImage img = job->renderedImage();
498
499 QMap<QPair<int, int>, Tile>::const_iterator it = metaTile.tiles.constBegin();
500 while ( it != metaTile.tiles.constEnd() )
501 {
502 QPair<int, int> tm = it.key();
503 Tile tile = it.value();
504 QImage tileImage = img.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
505 QByteArray ba;
506 QBuffer buffer( &ba );
507 buffer.open( QIODevice::WriteOnly );
508 tileImage.save( &buffer, mTileFormat.toStdString().c_str(), mJpgQuality );
509 mMbtilesWriter->setTileData( tile.z, tile.x, tile2tms( tile.y, tile.z ), ba );
510 ++it;
511 }
512
513 mRendererJobs.remove( job );
514 job->deleteLater();
515
516 mFeedback->setProgress( 100.0 * ( mProcessedTiles++ ) / mTotalTiles );
517
518 if ( mFeedback->isCanceled() )
519 {
520 while ( mRendererJobs.size() > 0 )
521 {
522 QgsMapRendererSequentialJob *j = mRendererJobs.firstKey();
523 j->cancel();
524 mRendererJobs.remove( j );
525 j->deleteLater();
526 }
527 mRendererJobs.clear();
528 mEventLoop->exit();
529 return;
530 }
531
532 if ( mMetaTiles.size() > 0 )
533 {
534 startJobs();
535 }
536 else if ( mMetaTiles.size() == 0 && mRendererJobs.size() == 0 )
537 {
538 mEventLoop->exit();
539 }
540}
541
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition: qgis.h:2934
@ RequiresProject
The algorithm requires that a valid QgsProject is available from the processing context in order to e...
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
@ Antialiasing
Enable anti-aliasing for map rendering.
This class represents a coordinate reference system (CRS).
Class for doing transforms between two map coordinate systems.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
Stores global configuration for labeling engine.
void setFlag(Qgis::LabelingFlag f, bool enabled=true)
Sets whether a particual flag is enabled.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Layer tree node points to a map layer.
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
Base class for all map layer types.
Definition: qgsmaplayer.h:75
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
void finished()
emitted when asynchronous rendering is finished (or canceled).
void start()
Start the rendering job and immediately return.
Job implementation that renders everything sequentially in one thread.
QImage renderedImage() override
Gets a preview/resulting image.
void cancel() override
Stop the rendering job - does not return until the job has terminated.
The QgsMapSettings class contains configuration for rendering of the map.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
void setOutputDpi(double dpi)
Sets the dpi (dots per inch) used for conversion between real world units (e.g.
void setOutputImageFormat(QImage::Format format)
sets format of internal QImage
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets the global configuration of the labeling engine.
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context, which stores various information regarding which datum transfo...
void setOutputSize(QSize size)
Sets the size of the resulting map image, in pixels.
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
void setFlag(Qgis::MapSettingsFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
const QgsExpressionContext & expressionContext() const
Gets the expression context.
virtual Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
int maximumThreads() const
Returns the (optional) number of threads to use when running algorithms.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
A boolean parameter for processing algorithms.
A color parameter for processing algorithms.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
A rectangular map extent parameter for processing algorithms.
A generic file based destination parameter, for specifying the destination path for a file (non-map l...
A folder destination parameter, for specifying the destination path for a folder created by the algor...
A numeric parameter for processing algorithms.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:107
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:112
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:201
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:211
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:196
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:206