QGIS API Documentation 4.3.0-Master (2b7e6c9893e)
Loading...
Searching...
No Matches
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 <atomic>
21
23#include "qgsimageoperation.h"
24#include "qgslayertree.h"
25#include "qgslayertreelayer.h"
26#include "qgsmaplayerutils.h"
28
29#include <QBuffer>
30#include <QDir>
31#include <QQueue>
32#include <QSemaphore>
33#include <QSet>
34#include <QString>
35#include <QThread>
36#include <QThreadPool>
37#include <QWaitCondition>
38
39using namespace Qt::StringLiterals;
40
42
43
44namespace
45{
46 int tile2tms( const int y, const int zoom )
47 {
48 double n = std::pow( 2, zoom );
49 return ( int ) std::floor( n - y - 1 );
50 }
51
52 int lon2tileX( const double lon, const int z )
53 {
54 return ( int ) ( std::floor( ( lon + 180.0 ) / 360.0 * ( 1 << z ) ) );
55 }
56
57 int lat2tileY( const double lat, const int z )
58 {
59 double latRad = lat * M_PI / 180.0;
60 return ( int ) ( std::floor( ( 1.0 - std::asinh( std::tan( latRad ) ) / M_PI ) / 2.0 * ( 1 << z ) ) );
61 }
62
63 double tileX2lon( const int x, const int z )
64 {
65 return x / ( double ) ( 1 << z ) * 360.0 - 180;
66 }
67
68 double tileY2lat( const int y, const int z )
69 {
70 double n = M_PI - 2.0 * M_PI * y / ( double ) ( 1 << z );
71 return 180.0 / M_PI * std::atan( 0.5 * ( std::exp( n ) - std::exp( -n ) ) );
72 }
73} //namespace
74
75
76void MetaTile::addTile( const int row, const int col, Tile tileToAdd )
77{
78 tiles.insert( QPair<int, int>( row, col ), tileToAdd );
79 if ( row >= rows )
80 {
81 rows = row + 1;
82 }
83 if ( col >= cols )
84 {
85 cols = col + 1;
86 }
87}
88
89QgsRectangle MetaTile::extent() const
90{
91 const Tile first = tiles.first();
92 const Tile last = tiles.last();
93 return QgsRectangle( tileX2lon( first.x, first.z ), tileY2lat( last.y + 1, last.z ), tileX2lon( last.x + 1, last.z ), tileY2lat( first.y, first.z ) );
94}
95
96
97namespace
98{
99 QList<MetaTile> getMetatiles( const QgsRectangle extent, const int zoom, long long &tileCount, const int tileSize )
100 {
101 int minX = lon2tileX( extent.xMinimum(), zoom );
102 int minY = lat2tileY( extent.yMaximum(), zoom );
103 int maxX = lon2tileX( extent.xMaximum(), zoom );
104 int maxY = lat2tileY( extent.yMinimum(), zoom );
105 tileCount = static_cast<long long>( maxX - minX + 1 ) * static_cast<long long>( maxY - minY + 1 );
106
107 QHash<uint64_t, MetaTile> tiles;
108 int i = 0;
109 for ( int x = minX; x <= maxX; x++ )
110 {
111 int j = 0;
112 for ( int y = minY; y <= maxY; y++ )
113 {
114 const uint64_t key = ( static_cast<uint64_t>( i / tileSize ) << 32 ) | static_cast<uint32_t>( j / tileSize );
115 tiles[key].addTile( i % tileSize, j % tileSize, Tile( x, y, zoom ) );
116 j++;
117 }
118 i++;
119 }
120 return tiles.values();
121 }
122} //namespace
123
133class PendingTilesToWriteQueue
134{
135 public:
139 void push( const QList<QgsMbTiles::TileData> &tiles )
140 {
141 if ( tiles.isEmpty() )
142 return;
143
144 QMutexLocker locker( &mMutex );
145 for ( const QgsMbTiles::TileData &tile : tiles )
146 {
147 mQueue.enqueue( tile );
148 }
149 mNotEmpty.wakeOne();
150 }
151
159 bool popBatch( QList<QgsMbTiles::TileData> &batch, int maxBatchSize, unsigned long timeoutMs = 500 )
160 {
161 QMutexLocker locker( &mMutex );
162
163 // wait until there is at least one item or all rendering is finished
164 while ( mQueue.isEmpty() && !mFinished )
165 {
166 mNotEmpty.wait( &mMutex );
167 }
168
169 if ( mQueue.isEmpty() )
170 return false;
171
172 // collect up to to maxBatchSize, unless we finish rendering or timeout before that happens
173 while ( mQueue.size() < maxBatchSize && !mFinished )
174 {
175 if ( !mNotEmpty.wait( &mMutex, timeoutMs ) )
176 {
177 // timeout exceeded
178 break;
179 }
180 }
181
182 while ( !mQueue.isEmpty() && batch.size() < maxBatchSize )
183 {
184 batch.append( mQueue.dequeue() );
185 }
186 return true;
187 }
188
192 void setFinished()
193 {
194 QMutexLocker locker( &mMutex );
195 mFinished = true;
196 mNotEmpty.wakeAll();
197 }
198
199 private:
200 QQueue<QgsMbTiles::TileData> mQueue;
201 mutable QMutex mMutex;
202 QWaitCondition mNotEmpty;
203 bool mFinished = false;
204};
205
206//
207// QgsXyzTilesBaseAlgorithm
208//
209
210QString QgsXyzTilesBaseAlgorithm::group() const
211{
212 return QObject::tr( "Raster tools" );
213}
214
215QString QgsXyzTilesBaseAlgorithm::groupId() const
216{
217 return u"rastertools"_s;
218}
219
220Qgis::ProcessingAlgorithmFlags QgsXyzTilesBaseAlgorithm::flags() const
221{
223}
224
225void QgsXyzTilesBaseAlgorithm::createCommonParameters()
226{
227 auto extentParam = std::make_unique<QgsProcessingParameterExtent>( u"EXTENT"_s, QObject::tr( "Extent" ) );
228 extentParam->setHelp( QObject::tr( "Spatial extent of the area for tile generation." ) );
229 addParameter( extentParam.release() );
230
231 auto minZoomParam = std::make_unique<QgsProcessingParameterNumber>( u"ZOOM_MIN"_s, QObject::tr( "Minimum zoom" ), Qgis::ProcessingNumberParameterType::Integer, 12, false, 0, 25 );
232 minZoomParam->setHelp(
233 QObject::tr(
234 "Minimum zoom level for generated tiles (0–25). Lower zoom levels cover broader geographic areas "
235 "at lower spatial resolution. Must be less than or equal to the maximum zoom level."
236 )
237 );
238 addParameter( minZoomParam.release() );
239
240 auto maxZoomParam = std::make_unique<QgsProcessingParameterNumber>( u"ZOOM_MAX"_s, QObject::tr( "Maximum zoom" ), Qgis::ProcessingNumberParameterType::Integer, 12, false, 0, 25 );
241 maxZoomParam->setHelp(
242 QObject::tr(
243 "Maximum zoom level for generated tiles (0–25). Higher zoom levels capture finer map details "
244 "and higher resolution, but exponentially increase total tile count, storage requirements, and rendering time. "
245 "Must be greater than or equal to the minimum zoom level."
246 )
247 );
248 addParameter( maxZoomParam.release() );
249
250 auto dpiParam = std::make_unique<QgsProcessingParameterNumber>( u"DPI"_s, QObject::tr( "DPI" ), Qgis::ProcessingNumberParameterType::Integer, 96, false, 48, 600 );
251 dpiParam->setHelp( QObject::tr( "Output resolution in DPI for rendered map content." ) );
252 dpiParam->setFlags( dpiParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
253 addParameter( dpiParam.release() );
254
255 auto bgColorParam = std::make_unique<QgsProcessingParameterColor>( u"BACKGROUND_COLOR"_s, QObject::tr( "Background color" ), QColor( Qt::transparent ), true, true );
256 bgColorParam->setHelp( QObject::tr( "Background color used when rendering map tiles." ) );
257 addParameter( bgColorParam.release() );
258
259 auto antialiasParam = std::make_unique<QgsProcessingParameterBoolean>( u"ANTIALIAS"_s, QObject::tr( "Enable antialiasing" ), true );
260 antialiasParam->setHelp( QObject::tr( "Controls whether antialiasing is applied during tile rendering." ) );
261 antialiasParam->setFlags( antialiasParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
262 addParameter( antialiasParam.release() );
263
264 auto tileFormatParam = std::make_unique<QgsProcessingParameterEnum>( u"TILE_FORMAT"_s, QObject::tr( "Tile format" ), QStringList { u"PNG"_s, u"JPG"_s, u"WEBP"_s }, false, 0 );
265 tileFormatParam->setHelp( QObject::tr( "Output image format for the rendered tiles." ) );
266 addParameter( tileFormatParam.release() );
267
268 auto qualityParam = std::make_unique<QgsProcessingParameterNumber>( u"QUALITY"_s, QObject::tr( "Quality (JPG only)" ), Qgis::ProcessingNumberParameterType::Integer, 75, false, 1, 100 );
269 qualityParam->setHelp( QObject::tr( "Image quality percentage used when tile format is set to JPG (1–100)." ) );
270 qualityParam->setFlags( qualityParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
271 addParameter( qualityParam.release() );
272
273 auto metaTileSizeParam = std::make_unique<QgsProcessingParameterNumber>( u"METATILESIZE"_s, QObject::tr( "Metatile size" ), Qgis::ProcessingNumberParameterType::Integer, 4, false, 1, 20 );
274 metaTileSizeParam->setHelp( QObject::tr( "Size of metatiles (in tile units) used during rendering." ) );
275 metaTileSizeParam->setFlags( metaTileSizeParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
276 addParameter( metaTileSizeParam.release() );
277
278 auto skipEmptyTilesParam = std::make_unique<QgsProcessingParameterBoolean>( u"SKIP_EMPTY_TILES"_s, QObject::tr( "Skip empty tiles" ), false );
279 skipEmptyTilesParam->setHelp( QObject::tr( "If set, completely empty tiles will be skipped." ) );
280 skipEmptyTilesParam->setFlags( skipEmptyTilesParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
281 addParameter( skipEmptyTilesParam.release() );
282}
283
284bool QgsXyzTilesBaseAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
285{
286 Q_UNUSED( feedback );
287
288 QgsProject *project = context.project();
289
290 mExpressionContext = context.expressionContext();
291
292 const QList<QgsLayerTreeLayer *> projectLayers = project->layerTreeRoot()->findLayers();
293 QSet<QString> visibleLayers;
294 for ( const QgsLayerTreeLayer *layer : projectLayers )
295 {
296 if ( layer->isVisible() )
297 {
298 visibleLayers << layer->layer()->id();
299 }
300 }
301
302 const QList<QgsMapLayer *> renderLayers = project->layerTreeRoot()->layerOrder();
303 for ( QgsMapLayer *layer : renderLayers )
304 {
305 if ( visibleLayers.contains( layer->id() ) )
306 {
307 QgsMapLayer *clonedLayer = layer->clone();
308 clonedLayer->moveToThread( nullptr );
309 mLayers << clonedLayer;
310 }
311 }
312
313 QgsRectangle extent = parameterAsExtent( parameters, u"EXTENT"_s, context );
314 QgsCoordinateReferenceSystem extentCrs = parameterAsExtentCrs( parameters, u"EXTENT"_s, context );
315 QgsCoordinateTransform ct( extentCrs, project->crs(), context.transformContext() );
316 ct.setBallparkTransformsAreAppropriate( true );
317 try
318 {
319 mExtent = ct.transformBoundingBox( extent );
320 }
321 catch ( QgsCsException & )
322 {
323 throw QgsProcessingException( QObject::tr( "Could not transform the extent into the project CRS" ) );
324 }
325
326 mMinZoom = parameterAsInt( parameters, u"ZOOM_MIN"_s, context );
327 mMaxZoom = parameterAsInt( parameters, u"ZOOM_MAX"_s, context );
328 if ( mMaxZoom < mMinZoom )
329 {
330 throw QgsProcessingException( QObject::tr( "Maximum zoom (%1) must be ≥ minimum zoom (%2)" ).arg( mMaxZoom ).arg( mMinZoom ) );
331 }
332 mDpi = parameterAsInt( parameters, u"DPI"_s, context );
333 mBackgroundColor = parameterAsColor( parameters, u"BACKGROUND_COLOR"_s, context );
334 mAntialias = parameterAsBool( parameters, u"ANTIALIAS"_s, context );
335 mSkipEmptyTiles = parameterAsBool( parameters, u"SKIP_EMPTY_TILES"_s, context );
336 switch ( parameterAsEnum( parameters, u"TILE_FORMAT"_s, context ) )
337 {
338 case 0:
339 mTileFormat = u"PNG"_s;
340 break;
341 case 1:
342 mTileFormat = u"JPG"_s;
343 break;
344 case 2:
345 mTileFormat = u"WEBP"_s;
346 break;
347 default:
348 mTileFormat = u"PNG"_s;
349 break;
350 }
351
352 mJpgQuality = mTileFormat != "PNG"_L1 ? parameterAsInt( parameters, u"QUALITY"_s, context ) : -1;
353 mMetaTileSize = parameterAsInt( parameters, u"METATILESIZE"_s, context );
354 mThreadsNumber = context.maximumThreads();
355 mTransformContext = context.transformContext();
356 mEllipsoid = context.ellipsoid();
357
358 QgsCoordinateTransform src2Wgs = QgsCoordinateTransform( project->crs(), QgsCoordinateReferenceSystem( "EPSG:4326" ), context.transformContext() );
360 try
361 {
362 mWgs84Extent = src2Wgs.transformBoundingBox( mExtent );
363 }
364 catch ( QgsCsException & )
365 {
366 throw QgsProcessingException( QObject::tr( "Could not transform the extent into WGS84" ) );
367 }
368
369 if ( parameters.contains( u"TILE_WIDTH"_s ) )
370 {
371 mTileWidth = parameterAsInt( parameters, u"TILE_WIDTH"_s, context );
372 }
373
374 if ( parameters.contains( u"TILE_HEIGHT"_s ) )
375 {
376 mTileHeight = parameterAsInt( parameters, u"TILE_HEIGHT"_s, context );
377 }
378
379 if ( ( mTileFormat != "PNG"_L1 && mTileFormat != "WEBP"_L1 ) && mBackgroundColor.alpha() != 255 )
380 {
381 feedback->pushWarning(
382 QObject::tr( "A semi-transparent background color was set, but the JPG format only supports fully opaque colors. The background color setting will be ignored. Please use a fully opaque background color instead." )
383 );
384 }
385
386 mScaleMethod = project->scaleMethod();
387
388 return true;
389}
390
391void QgsXyzTilesBaseAlgorithm::checkLayersUsagePolicy( QgsProcessingFeedback *feedback )
392{
393 if ( mTotalMetaTiles > MAXIMUM_OPENSTREETMAP_TILES_FETCH )
394 {
395 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
396 {
398 {
399 // Prevent bulk downloading of tiles from openstreetmap.org as per OSMF tile usage policy
400 feedback->pushFormattedMessage(
401 QObject::tr( "Layer %1 will be skipped as the algorithm leads to bulk downloading behavior which is prohibited by the %2OpenStreetMap Foundation tile usage policy%3" )
402 .arg( layer->name(), u"<a href=\"https://operations.osmfoundation.org/policies/tiles/\">"_s, u"</a>"_s ),
403 QObject::tr( "Layer %1 will be skipped as the algorithm leads to bulk downloading behavior which is prohibited by the %2OpenStreetMap Foundation tile usage policy%3" )
404 .arg( layer->name(), QString(), QString() )
405 );
406 mLayers.removeAll( layer );
407 delete layer;
408 }
409 }
410 }
411}
412
413std::optional< QgsMapSettings > QgsXyzTilesBaseAlgorithm::mapSettingsForTile( const MetaTile &metaTile ) const
414{
416 QgsCoordinateTransform wgsToMercator = QgsCoordinateTransform( QgsCoordinateReferenceSystem( "EPSG:4326" ), mercatorCrs, mTransformContext );
417 wgsToMercator.setBallparkTransformsAreAppropriate( true );
418
419 QgsMapSettings settings;
420 try
421 {
422 settings.setExtent( wgsToMercator.transformBoundingBox( metaTile.extent() ) );
423 }
424 catch ( QgsCsException & )
425 {
426 return {};
427 }
429 settings.setOutputImageFormat( QImage::Format_ARGB32_Premultiplied );
430 settings.setTransformContext( mTransformContext );
431 settings.setEllipsoid( mEllipsoid );
432 settings.setDestinationCrs( mercatorCrs );
433 settings.setLayers( mLayers );
434 settings.setOutputDpi( mDpi );
435 settings.setFlag( Qgis::MapSettingsFlag::Antialiasing, mAntialias );
440 settings.setScaleMethod( mScaleMethod );
441 if ( mTileFormat == "PNG"_L1 || mTileFormat == "WEBP"_L1 || mBackgroundColor.alpha() == 255 )
442 {
443 settings.setBackgroundColor( mBackgroundColor );
444 }
445 QSize size( mTileWidth * metaTile.rows, mTileHeight * metaTile.cols );
446 settings.setOutputSize( size );
447
448 QgsLabelingEngineSettings labelingSettings = settings.labelingEngineSettings();
449 labelingSettings.setFlag( Qgis::LabelingFlag::UsePartialCandidates, false );
450 settings.setLabelingEngineSettings( labelingSettings );
451
452 QgsExpressionContext exprContext = mExpressionContext;
454 settings.setExpressionContext( exprContext );
455
456 return settings;
457}
458
459void QgsXyzTilesBaseAlgorithm::startJobs( QgsProcessingFeedback *feedback )
460{
461 while ( mRendererJobs.size() < mThreadsNumber && !mMetaTiles.empty() )
462 {
463 if ( feedback->isCanceled() )
464 break;
465
466 MetaTile metaTile = mMetaTiles.takeFirst();
467 const std::optional<QgsMapSettings> settings = mapSettingsForTile( metaTile );
468 if ( !settings.has_value() )
469 {
470 mProcessedMetaTiles++;
471 feedback->setProgress( 100.0 * mProcessedMetaTiles / mTotalMetaTiles );
472 continue;
473 }
474
476 mRendererJobs.insert( job, metaTile );
477
478 QObject::connect( job, &QgsMapRendererJob::finished, mJobOwner, [this, feedback, job]() {
479 const MetaTile tile = mRendererJobs.take( job );
480 const QImage renderedImage = job->renderedImage();
481 job->deleteLater();
482
483 mProcessedMetaTiles++;
484 feedback->setProgress( 100.0 * mProcessedMetaTiles / mTotalMetaTiles );
485
486 processMetaTile( tile, renderedImage, feedback );
487
488 if ( !feedback->isCanceled() )
489 {
490 startJobs( feedback );
491 }
492 checkPipelineFinished( feedback );
493 } );
494
495 job->start();
496 }
497
498 checkPipelineFinished( feedback );
499}
500
501void QgsXyzTilesBaseAlgorithm::checkPipelineFinished( QgsProcessingFeedback *feedback )
502{
503 if ( feedback->isCanceled() || ( mMetaTiles.isEmpty() && mRendererJobs.isEmpty() && mActivePostProcessingTasks == 0 ) )
504 {
505 if ( mEventLoop )
506 {
507 mEventLoop->exit();
508 }
509 }
510}
511
512//
513// QgsXyzTilesDirectoryAlgorithm
514//
515
516QString QgsXyzTilesDirectoryAlgorithm::name() const
517{
518 return u"tilesxyzdirectory"_s;
519}
520
521QString QgsXyzTilesDirectoryAlgorithm::displayName() const
522{
523 return QObject::tr( "Generate XYZ tiles (Directory)" );
524}
525
526QStringList QgsXyzTilesDirectoryAlgorithm::tags() const
527{
528 return QObject::tr( "tiles,xyz,tms,directory" ).split( ',' );
529}
530
531QString QgsXyzTilesDirectoryAlgorithm::shortHelpString() const
532{
533 return QObject::tr(
534 "This algorithm generates XYZ raster tiles from the current project and saves them as individual image files in a structured directory hierarchy ({z}/{x}/{y}.png or .jpg).\n\n"
535 "All visible map layers from the project will be rendered into tiles across the specified extent and zoom range.\n\n"
536 "Optionally, a standalone Leaflet HTML file can be generated for instant web previewing of the tiles."
537 );
538}
539
540QgsXyzTilesDirectoryAlgorithm *QgsXyzTilesDirectoryAlgorithm::createInstance() const
541{
542 return new QgsXyzTilesDirectoryAlgorithm();
543}
544
545void QgsXyzTilesDirectoryAlgorithm::initAlgorithm( const QVariantMap & )
546{
547 createCommonParameters();
548 auto tileWidthParam = std::make_unique<QgsProcessingParameterNumber>( u"TILE_WIDTH"_s, QObject::tr( "Tile width" ), Qgis::ProcessingNumberParameterType::Integer, 256, false, 1, 4096 );
549 tileWidthParam->setHelp( QObject::tr( "Width of each tile image in pixels." ) );
550 addParameter( tileWidthParam.release() );
551
552 auto tileHeightParam = std::make_unique<QgsProcessingParameterNumber>( u"TILE_HEIGHT"_s, QObject::tr( "Tile height" ), Qgis::ProcessingNumberParameterType::Integer, 256, false, 1, 4096 );
553 tileHeightParam->setHelp( QObject::tr( "Height of each tile image in pixels." ) );
554 addParameter( tileHeightParam.release() );
555
556 auto tmsParam = std::make_unique<QgsProcessingParameterBoolean>( u"TMS_CONVENTION"_s, QObject::tr( "Use inverted tile Y axis (TMS convention)" ), false );
557 tmsParam->setHelp( QObject::tr( "Inverts the Y tile coordinate naming convention to follow TMS format." ) );
558 addParameter( tmsParam.release() );
559
560 auto titleParam = std::make_unique<QgsProcessingParameterString>( u"HTML_TITLE"_s, QObject::tr( "Leaflet HTML output title" ), QVariant(), false, true );
561 titleParam->setFlags( titleParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
562 titleParam->setHelp( QObject::tr( "Title displayed in the generated Leaflet HTML web viewer." ) );
563 addParameter( titleParam.release() );
564
565 auto attributionParam = std::make_unique<QgsProcessingParameterString>( u"HTML_ATTRIBUTION"_s, QObject::tr( "Leaflet HTML output attribution" ), QVariant(), false, true );
566 attributionParam->setFlags( attributionParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
567 attributionParam->setHelp( QObject::tr( "Attribution text displayed in the generated Leaflet HTML web viewer." ) );
568 addParameter( attributionParam.release() );
569
570 auto osmParam = std::make_unique<QgsProcessingParameterBoolean>( u"HTML_OSM"_s, QObject::tr( "Include OpenStreetMap basemap in Leaflet HTML output" ), false );
571 osmParam->setFlags( osmParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
572 osmParam->setHelp( QObject::tr( "Includes an OpenStreetMap background layer in the generated Leaflet HTML viewer." ) );
573 addParameter( osmParam.release() );
574
575 auto outputDirParam = std::make_unique<QgsProcessingParameterFolderDestination>( u"OUTPUT_DIRECTORY"_s, QObject::tr( "Output directory" ) );
576 outputDirParam->setHelp( QObject::tr( "Destination folder where the generated directory structure and tile files will be stored." ) );
577 addParameter( outputDirParam.release() );
578
579 auto outputHtmlParam = std::make_unique<QgsProcessingParameterFileDestination>( u"OUTPUT_HTML"_s, QObject::tr( "Output HTML (Leaflet)" ), QObject::tr( "HTML files (*.html)" ), QVariant(), true );
580 outputHtmlParam->setHelp( QObject::tr( "Destination file path for the optional Leaflet HTML web map preview." ) );
581 addParameter( outputHtmlParam.release() );
582
583 addOutput( new QgsProcessingOutputRasterLayer( u"OUTPUT_LAYER"_s, QObject::tr( "Output tiles as raster layer" ) ) );
584}
585
586QVariantMap QgsXyzTilesDirectoryAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
587{
588 QGS_MARK_ALGORITHM_SOURCE
589
590 const bool tms = parameterAsBoolean( parameters, u"TMS_CONVENTION"_s, context );
591 const QString title = parameterAsString( parameters, u"HTML_TITLE"_s, context );
592 const QString attribution = parameterAsString( parameters, u"HTML_ATTRIBUTION"_s, context );
593 const bool useOsm = parameterAsBoolean( parameters, u"HTML_OSM"_s, context );
594 QString outputDir = parameterAsString( parameters, u"OUTPUT_DIRECTORY"_s, context );
595 const QString outputHtml = parameterAsString( parameters, u"OUTPUT_HTML"_s, context );
596
597 mOutputDir = outputDir;
598 mTms = tms;
599
600 long long totalTiles = 0;
601 mTotalMetaTiles = 0;
602 for ( int z = mMinZoom; z <= mMaxZoom; z++ )
603 {
604 if ( feedback->isCanceled() )
605 break;
606
607 long long tileCount = 0;
608 mMetaTiles += getMetatiles( mWgs84Extent, z, tileCount, mMetaTileSize );
609 feedback->pushInfo( QObject::tr( "%1 metatiles (%2 tiles) will be created for zoom level %3" ).arg( mMetaTiles.size() - mTotalMetaTiles ).arg( tileCount ).arg( z ) );
610 mTotalMetaTiles = mMetaTiles.size();
611 totalTiles += tileCount;
612 }
613 if ( mTotalMetaTiles == 0 )
614 {
615 throw QgsProcessingException( QObject::tr( "No metatiles will be created -- please check the extent and zoom limits" ) );
616 }
617
618 feedback->pushInfo( QObject::tr( "A total of %1 metatiles (%2 tiles) will be created" ).arg( mTotalMetaTiles ).arg( totalTiles ) );
619
620 checkLayersUsagePolicy( feedback );
621
622 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
623 {
624 layer->moveToThread( QThread::currentThread() );
625 }
626
627 doExport( feedback );
628
629 qDeleteAll( mLayers );
630 mLayers.clear();
631
632 if ( mSkipEmptyTiles )
633 {
634 feedback->pushInfo( QObject::tr( "Wrote %1 total tiles, skipped %2 empty tiles" ).arg( mTilesWritten.load() ).arg( mEmptyTiles.load() ) );
635 }
636
637 QVariantMap results;
638 results.insert( u"OUTPUT_DIRECTORY"_s, outputDir );
639
640 if ( !outputHtml.isEmpty() )
641 {
642 const QString osm = QStringLiteral(
643 "var osm_layer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',"
644 "{minZoom: %1, maxZoom: %2, attribution: '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'}).addTo(map);"
645 )
646 .arg( mMinZoom )
647 .arg( mMaxZoom );
648
649 const QString addOsm = useOsm ? osm : QString();
650 const QString tmsConvention = tms ? u"true"_s : u"false"_s;
651 const QString attr = attribution.isEmpty() ? u"Created by QGIS"_s : attribution;
652 const QString tileSource = u"'file:///%1/{z}/{x}/{y}.%2'"_s.arg( outputDir.replace( "\\", "/" ).toHtmlEscaped(), mTileFormat.toLower() );
653
654 const QString html = QStringLiteral(
655 "<!DOCTYPE html><html><head><title>%1</title><meta charset=\"utf-8\"/>"
656 "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
657 "<link rel=\"stylesheet\" href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\""
658 "integrity=\"sha384-sHL9NAb7lN7rfvG5lfHpm643Xkcjzp4jFvuavGOndn6pjVqS6ny56CAt3nsEVT4H\""
659 "crossorigin=\"\"/>"
660 "<script src=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js\""
661 "integrity=\"sha384-cxOPjt7s7Iz04uaHJceBmS+qpjv2JkIHNVcuOrM+YHwZOmJGBXI00mdUXEq65HTH\""
662 "crossorigin=\"\"></script>"
663 "<style type=\"text/css\">body {margin: 0;padding: 0;} html, body, #map{width: 100%;height: 100%;}</style></head>"
664 "<body><div id=\"map\"></div><script>"
665 "var map = L.map('map', {attributionControl: false}).setView([%2, %3], %4);"
666 "L.control.attribution({prefix: false}).addTo(map);"
667 "%5"
668 "var tilesource_layer = L.tileLayer(%6, {minZoom: %7, maxZoom: %8, tms: %9, attribution: '%10'}).addTo(map);"
669 "</script></body></html>"
670 )
671 .arg( title.isEmpty() ? u"Leaflet preview"_s : title )
672 .arg( mWgs84Extent.center().y() )
673 .arg( mWgs84Extent.center().x() )
674 .arg( ( mMaxZoom + mMinZoom ) / 2 )
675 .arg( addOsm, tileSource )
676 .arg( mMinZoom )
677 .arg( mMaxZoom )
678 .arg( tmsConvention, attr );
679
680 QFile htmlFile( outputHtml );
681 if ( !htmlFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
682 {
683 throw QgsProcessingException( QObject::tr( "Could not open html file %1" ).arg( outputHtml ) );
684 }
685 QTextStream fout( &htmlFile );
686 fout << html;
687
688 results.insert( u"OUTPUT_HTML"_s, outputHtml );
689 }
690
691 // try to load the result as a raster layer
692 if ( !feedback->isCanceled() )
693 {
694 const QString layerUri
695 = u"type=xyz&url=file:///%1/%7Bz%7D/%7Bx%7D/%7By%7D.%2&zmax=%3&zmin=%4"_s.arg( outputDir.replace( "\\", "/" ).toHtmlEscaped(), mTileFormat.toLower() ).arg( mMaxZoom ).arg( mMinZoom );
696 auto layer = std::make_unique<QgsRasterLayer>( layerUri, "OUTPUT_LAYER", u"wms"_s );
697 if ( !layer->isValid() )
698 {
699 feedback->reportError( QObject::tr( "Failed to open XYZ directory as a raster layer" ) );
700 }
701 const QString layerId = layer->id();
702 const QgsProcessingContext::LayerDetails details( layer->name(), context.project(), u"OUTPUT_LAYER"_s, QgsProcessingUtils::LayerHint::Raster );
703 details.setOutputLayerName( layer.get() );
704 context.addLayerToLoadOnCompletion( layerId, details );
705 context.temporaryLayerStore()->addMapLayer( layer.release() );
706 results.insert( u"OUTPUT_LAYER"_s, layerId );
707 }
708
709 return results;
710}
711
712void QgsXyzTilesDirectoryAlgorithm::processMetaTile( const MetaTile &metaTile, const QImage &renderedImage, QgsProcessingFeedback *feedback )
713{
714 mActivePostProcessingTasks++;
715
716 mPostProcessingPool->start( [this, metaTile, renderedImage, feedback]() {
717 const bool testEmptyTilesUsingAlpha0 = mTileFormat != "JPG"_L1 && mBackgroundColor.alpha() == 0;
718 long long localWritten = 0;
719 long long localEmpty = 0;
720
721 QSet<QString> createdDirs;
722
723 for ( auto it = metaTile.tiles.constBegin(); it != metaTile.tiles.constEnd(); ++it )
724 {
725 if ( feedback->isCanceled() )
726 break;
727
728 const QPair<int, int> tm = it.key();
729 const QImage tileImage = renderedImage.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
730 const bool skipTile = mSkipEmptyTiles && ( testEmptyTilesUsingAlpha0 ? QgsImageOperation::isBlankImage( tileImage ) : QgsImageOperation::isSingleColor( tileImage, mBackgroundColor ) );
731 if ( skipTile )
732 {
733 localEmpty++;
734 continue;
735 }
736
737 const Tile tile = it.value();
738 const QString dirPath = u"%1/%2/%3"_s.arg( mOutputDir ).arg( tile.z ).arg( tile.x );
739 if ( !createdDirs.contains( dirPath ) )
740 {
741 QDir().mkpath( dirPath );
742 createdDirs.insert( dirPath );
743 }
744
745 const int y = mTms ? tile2tms( tile.y, tile.z ) : tile.y;
746 const QString filePath = u"%1/%2.%3"_s.arg( dirPath ).arg( y ).arg( mTileFormat.toLower() );
747 tileImage.save( filePath, mTileFormat.toStdString().c_str(), mJpgQuality );
748
749 localWritten++;
750 }
751
752 mTilesWritten += localWritten;
753 mEmptyTiles += localEmpty;
754
755 mActivePostProcessingTasks--;
756 checkPipelineFinished( feedback );
757 } );
758}
759
760void QgsXyzTilesDirectoryAlgorithm::doExport( QgsProcessingFeedback *feedback )
761{
762 mPostProcessingPool = std::make_unique<QThreadPool>();
763 mPostProcessingPool->setMaxThreadCount( std::max( 1, mThreadsNumber ) );
764
765 QEventLoop loop;
766 mEventLoop = &loop;
767 mJobOwner.reset( new QObject() );
768
769 startJobs( feedback );
770
771 if ( !mMetaTiles.isEmpty() || !mRendererJobs.isEmpty() || mActivePostProcessingTasks.load() > 0 )
772 {
773 loop.exec();
774 }
775
776 for ( auto *j : mRendererJobs.keys() )
777 {
778 j->cancel();
779 j->deleteLater();
780 }
781 mRendererJobs.clear();
782
783 mPostProcessingPool->waitForDone();
784 mPostProcessingPool.reset();
785 mEventLoop = nullptr;
786}
787
788
789//
790// QgsXyzTilesMbtilesAlgorithm
791//
792
793QString QgsXyzTilesMbtilesAlgorithm::name() const
794{
795 return u"tilesxyzmbtiles"_s;
796}
797
798QString QgsXyzTilesMbtilesAlgorithm::displayName() const
799{
800 return QObject::tr( "Generate XYZ tiles (MBTiles)" );
801}
802
803QStringList QgsXyzTilesMbtilesAlgorithm::tags() const
804{
805 return QObject::tr( "tiles,xyz,tms,mbtiles" ).split( ',' );
806}
807
808QString QgsXyzTilesMbtilesAlgorithm::shortHelpString() const
809{
810 return QObject::tr(
811 "This algorithm generates XYZ raster tiles from the current project and packages them into a single, portable MBTiles (SQLite) database file.\n\n"
812 "All visible map layers from the project will be rendered into tiles across the specified extent and zoom range."
813 );
814}
815
816QgsXyzTilesMbtilesAlgorithm *QgsXyzTilesMbtilesAlgorithm::createInstance() const
817{
818 return new QgsXyzTilesMbtilesAlgorithm();
819}
820
821void QgsXyzTilesMbtilesAlgorithm::initAlgorithm( const QVariantMap & )
822{
823 createCommonParameters();
824 addParameter( new QgsProcessingParameterFileDestination( u"OUTPUT_FILE"_s, QObject::tr( "Output" ), QObject::tr( "MBTiles files (*.mbtiles *.MBTILES)" ) ) );
825
826 addOutput( new QgsProcessingOutputRasterLayer( u"OUTPUT_LAYER"_s, QObject::tr( "Output MBTiles raster layer" ) ) );
827}
828
829QVariantMap QgsXyzTilesMbtilesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
830{
831 QGS_MARK_ALGORITHM_SOURCE
832
833 const QString outputFile = parameterAsString( parameters, u"OUTPUT_FILE"_s, context );
834 if ( QFile::exists( outputFile ) )
835 {
836 feedback->pushWarning( QObject::tr( "Removing existing file '%1'" ).arg( QDir::toNativeSeparators( outputFile ) ) );
837 if ( !QFile( outputFile ).remove() )
838 {
839 throw QgsProcessingException( QObject::tr( "Could not remove existing file '%1'" ).arg( QDir::toNativeSeparators( outputFile ) ) );
840 }
841 }
842
843 mMbtilesWriter = std::make_unique<QgsMbTiles>( outputFile );
844 // use deferred index creation, as we'll be writing 1000s of tiles and don't want to update
845 // the index after every one
846 if ( !mMbtilesWriter->create( true ) )
847 {
848 throw QgsProcessingException( QObject::tr( "Failed to create MBTiles file %1" ).arg( outputFile ) );
849 }
850 mMbtilesWriter->setMetadataValue( u"format"_s, mTileFormat.toLower() );
851 mMbtilesWriter->setMetadataValue( u"name"_s, QFileInfo( outputFile ).baseName() );
852 mMbtilesWriter->setMetadataValue( u"description"_s, QFileInfo( outputFile ).baseName() );
853 mMbtilesWriter->setMetadataValue( u"version"_s, u"1.1"_s );
854 mMbtilesWriter->setMetadataValue( u"type"_s, u"overlay"_s );
855 mMbtilesWriter->setMetadataValue( u"minzoom"_s, QString::number( mMinZoom ) );
856 mMbtilesWriter->setMetadataValue( u"maxzoom"_s, QString::number( mMaxZoom ) );
857 QString boundsStr = QString( u"%1,%2,%3,%4"_s ).arg( mWgs84Extent.xMinimum() ).arg( mWgs84Extent.yMinimum() ).arg( mWgs84Extent.xMaximum() ).arg( mWgs84Extent.yMaximum() );
858 mMbtilesWriter->setMetadataValue( u"bounds"_s, boundsStr );
859
860 long long totalTiles = 0;
861 mTotalMetaTiles = 0;
862 for ( int z = mMinZoom; z <= mMaxZoom; z++ )
863 {
864 if ( feedback->isCanceled() )
865 break;
866
867 long long tileCount = 0;
868 mMetaTiles += getMetatiles( mWgs84Extent, z, tileCount, mMetaTileSize );
869 feedback->pushInfo( QObject::tr( "%1 metatiles (%2 tiles) will be created for zoom level %3" ).arg( mMetaTiles.size() - mTotalMetaTiles ).arg( tileCount ).arg( z ) );
870 mTotalMetaTiles = mMetaTiles.size();
871 totalTiles += tileCount;
872 }
873 if ( mTotalMetaTiles == 0 )
874 {
875 throw QgsProcessingException( QObject::tr( "No metatiles will be created -- please check the extent and zoom limits" ) );
876 }
877
878 feedback->pushInfo( QObject::tr( "A total of %1 metatiles (%2 tiles) will be created" ).arg( mTotalMetaTiles ).arg( totalTiles ) );
879
880 checkLayersUsagePolicy( feedback );
881
882 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
883 {
884 layer->moveToThread( QThread::currentThread() );
885 }
886
887 doExport( feedback );
888
889 qDeleteAll( mLayers );
890 mLayers.clear();
891
892 if ( !feedback->isCanceled() )
893 {
894 mMbtilesWriter->finalize();
895 }
896
897 if ( mSkipEmptyTiles )
898 {
899 feedback->pushInfo( QObject::tr( "Wrote %1 total tiles, skipped %2 empty tiles" ).arg( mTilesWritten.load() ).arg( mEmptyTiles.load() ) );
900 }
901 QVariantMap results;
902 results.insert( u"OUTPUT_FILE"_s, outputFile );
903
904 // try to load the result as a raster layer
905 if ( !feedback->isCanceled() )
906 {
907 auto layer = std::make_unique<QgsRasterLayer>( outputFile, "OUTPUT_LAYER", u"gdal"_s );
908 if ( !layer->isValid() )
909 {
910 feedback->reportError( QObject::tr( "Failed to open MBTiles file as a raster layer" ) );
911 }
912 const QString layerId = layer->id();
913 const QgsProcessingContext::LayerDetails details( layer->name(), context.project(), u"OUTPUT_LAYER"_s, QgsProcessingUtils::LayerHint::Raster );
914 details.setOutputLayerName( layer.get() );
915 context.addLayerToLoadOnCompletion( layerId, details );
916 context.temporaryLayerStore()->addMapLayer( layer.release() );
917 results.insert( u"OUTPUT_LAYER"_s, layerId );
918 }
919
920 return results;
921}
922
923void QgsXyzTilesMbtilesAlgorithm::processMetaTile( const MetaTile &metaTile, const QImage &renderedImg, QgsProcessingFeedback *feedback )
924{
925 mActivePostProcessingTasks++;
926
927 mPostProcessingPool->start( [this, feedback, metaTile, renderedImg]() {
928 const bool testEmptyTilesUsingAlpha0 = mTileFormat != "JPG"_L1 && mBackgroundColor.alpha() == 0;
929 long long localWritten = 0;
930 long long localEmpty = 0;
931
932 QList<QgsMbTiles::TileData> metatileTiles;
933 metatileTiles.reserve( metaTile.tiles.size() );
934
935 for ( auto it = metaTile.tiles.constBegin(); it != metaTile.tiles.constEnd(); ++it )
936 {
937 if ( feedback->isCanceled() )
938 break;
939
940 const QPair<int, int> tm = it.key();
941
942 const QImage tileImage = renderedImg.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
943
944 const bool skipTile = mSkipEmptyTiles && ( testEmptyTilesUsingAlpha0 ? QgsImageOperation::isBlankImage( tileImage ) : QgsImageOperation::isSingleColor( tileImage, mBackgroundColor ) );
945 if ( skipTile )
946 {
947 localEmpty++;
948 continue;
949 }
950
951 QByteArray bytes;
952 QBuffer buffer( &bytes );
953 buffer.open( QIODevice::WriteOnly );
954 tileImage.save( &buffer, mTileFormat.toStdString().c_str(), mJpgQuality );
955
956 const Tile tile = it.value();
957 const int tileY = tile2tms( tile.y, tile.z );
958 metatileTiles.append( { tile.z, tile.x, tileY, bytes } );
959 localWritten++;
960 }
961
962 mWriteQueue->push( metatileTiles );
963
964 mTilesWritten += localWritten;
965 mEmptyTiles += localEmpty;
966
967 mActivePostProcessingTasks--;
968 checkPipelineFinished( feedback );
969 } );
970}
971
972void QgsXyzTilesMbtilesAlgorithm::doExport( QgsProcessingFeedback *feedback )
973{
974 mPostProcessingPool = std::make_unique<QThreadPool>();
975 mPostProcessingPool->setMaxThreadCount( std::max( 1, mThreadsNumber ) );
976
977 PendingTilesToWriteQueue queue;
978 mWriteQueue = &queue;
979
980 QThread *dbThread = QThread::create( [this, &queue]() {
981 QList<QgsMbTiles::TileData> batch;
982 constexpr int BATCH_SIZE = 10000;
983 batch.reserve( BATCH_SIZE );
984 while ( queue.popBatch( batch, BATCH_SIZE ) )
985 {
986 mMbtilesWriter->setTileData( batch );
987 batch.clear();
988 }
989 } );
990 dbThread->start();
991
992 QEventLoop loop;
993 mEventLoop = &loop;
994 mJobOwner.reset( new QObject() );
995
996 startJobs( feedback );
997
998 if ( !mMetaTiles.isEmpty() || !mRendererJobs.isEmpty() || mActivePostProcessingTasks.load() > 0 )
999 {
1000 loop.exec();
1001 }
1002
1003 for ( auto *j : mRendererJobs.keys() )
1004 {
1005 j->cancel();
1006 j->deleteLater();
1007 }
1008 mRendererJobs.clear();
1009
1010 mPostProcessingPool->waitForDone();
1011 mPostProcessingPool.reset();
1012
1013 queue.setFinished();
1014 dbThread->wait();
1015 delete dbThread;
1016
1017 mWriteQueue = nullptr;
1018 mEventLoop = nullptr;
1019}
1020
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
Definition qgis.h:2897
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
Definition qgis.h:3044
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3828
@ Export
Renderer used for printing or exporting to a file.
Definition qgis.h:3665
@ RequiresProject
The algorithm requires that a valid QgsProject is available from the processing context in order to e...
Definition qgis.h:3815
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3984
@ RenderMapTile
Draw map such that there are no problems between adjacent tiles.
Definition qgis.h:2918
@ UseRenderingOptimization
Enable vector simplification and other rendering optimizations.
Definition qgis.h:2915
@ Antialiasing
Enable anti-aliasing for map rendering.
Definition qgis.h:2910
@ HighQualityImageTransforms
Enable high quality image transformations, which results in better appearance of scaled or rotated ra...
Definition qgis.h:2925
Represents a coordinate reference system (CRS).
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
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:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
Contains operations and filters which apply to QImages.
static bool isBlankImage(const QImage &image)
Tests whether an image is completely blank, i.e.
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.
QgsMapLayer * addMapLayer(QgsMapLayer *layer, bool takeOwnership=true)
Add a layer to the store.
static bool isOpenStreetMapLayer(QgsMapLayer *layer)
Returns true if the layer is served by OpenStreetMap server.
Base class for all map layer types.
Definition qgsmaplayer.h:83
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.
Contains configuration for rendering maps.
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.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
void setScaleMethod(Qgis::ScaleCalculationMethod method)
Sets the method to use for scale calculations for 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 setRendererUsage(Qgis::RendererUsage rendererUsage)
Sets the rendering usage.
void setRasterizedRenderingPolicy(Qgis::RasterizedRenderingPolicy policy)
Sets the policy controlling when rasterisation of content during renders is permitted.
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.
virtual Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Details for layers to load into projects.
Contains information about the context in which a processing algorithm is executed.
QgsExpressionContext & expressionContext()
Returns the expression context.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
void addLayerToLoadOnCompletion(const QString &layer, const QgsProcessingContext::LayerDetails &details)
Adds a layer to load (by ID or datasource) into the canvas upon completion of the algorithm or model.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
QString ellipsoid() const
Returns the ellipsoid to use for distance and area calculations.
QgsMapLayerStore * temporaryLayerStore()
Returns a reference to the layer store used for storing temporary layers during algorithm execution.
int maximumThreads() const
Returns the (optional) number of threads to use when running algorithms.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
virtual void pushFormattedMessage(const QString &html, const QString &text)
Pushes a pre-formatted message from the algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
A raster layer output for processing algorithms.
A generic file based destination parameter, for specifying the destination path for a file (non-map l...
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:120
Qgis::ScaleCalculationMethod scaleMethod
Definition qgsproject.h:136
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
#define MAXIMUM_OPENSTREETMAP_TILES_FETCH