QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
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 <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
101QgsProcessingAlgorithm::Flags 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" ), QgsProcessingParameterNumber::Integer, 12, false, 0, 25 ) );
110 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "ZOOM_MAX" ), QObject::tr( "Maximum zoom" ), QgsProcessingParameterNumber::Integer, 12, false, 0, 25 ) );
111 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "DPI" ), QObject::tr( "DPI" ), QgsProcessingParameterNumber::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)" ), QgsProcessingParameterNumber::Integer, 75, false, 1, 100 ) );
116 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "METATILESIZE" ), QObject::tr( "Metatile size" ), QgsProcessingParameterNumber::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 if ( mTileFormat != QLatin1String( "PNG" ) && mBackgroundColor.alpha() != 255 )
156 {
157 feedback->pushWarning( QObject::tr( "Background color setting ignored, the JPG format only supports fully opaque colors" ) );
158 }
159 mAntialias = parameterAsBool( parameters, QStringLiteral( "ANTIALIAS" ), context );
160 mTileFormat = parameterAsEnum( parameters, QStringLiteral( "TILE_FORMAT" ), context ) ? QStringLiteral( "JPG" ) : QStringLiteral( "PNG" );
161 mJpgQuality = parameterAsInt( parameters, QStringLiteral( "QUALITY" ), context );
162 mMetaTileSize = parameterAsInt( parameters, QStringLiteral( "METATILESIZE" ), context );
163 mThreadsNumber = context.maximumThreads();
164 mTransformContext = context.transformContext();
165 mFeedback = feedback;
166
167 mWgs84Crs = QgsCoordinateReferenceSystem( "EPSG:4326" );
168 mMercatorCrs = QgsCoordinateReferenceSystem( "EPSG:3857" );
169 mSrc2Wgs = QgsCoordinateTransform( project->crs(), mWgs84Crs, context.transformContext() );
170 mWgs2Mercator = QgsCoordinateTransform( mWgs84Crs, mMercatorCrs, context.transformContext() );
171
172 mWgs84Extent = mSrc2Wgs.transformBoundingBox( mExtent );
173
174 if ( parameters.contains( QStringLiteral( "TILE_WIDTH" ) ) )
175 {
176 mTileWidth = parameterAsInt( parameters, QStringLiteral( "TILE_WIDTH" ), context );
177 }
178
179 if ( parameters.contains( QStringLiteral( "TILE_HEIGHT" ) ) )
180 {
181 mTileHeight = parameterAsInt( parameters, QStringLiteral( "TILE_HEIGHT" ), context );
182 }
183
184 return true;
185}
186
187void QgsXyzTilesBaseAlgorithm::startJobs()
188{
189 while ( mRendererJobs.size() < mThreadsNumber && !mMetaTiles.empty() )
190 {
191 MetaTile metaTile = mMetaTiles.takeFirst();
192
193 QgsMapSettings settings;
194 settings.setExtent( mWgs2Mercator.transformBoundingBox( metaTile.extent() ) );
195 settings.setOutputImageFormat( QImage::Format_ARGB32_Premultiplied );
196 settings.setTransformContext( mTransformContext );
197 settings.setDestinationCrs( mMercatorCrs );
198 settings.setLayers( mLayers );
199 settings.setOutputDpi( mDpi );
200 if ( mTileFormat == QLatin1String( "PNG" ) || mBackgroundColor.alpha() == 255 )
201 {
202 settings.setBackgroundColor( mBackgroundColor );
203 }
204 QSize size( mTileWidth * metaTile.rows, mTileHeight * metaTile.cols );
205 settings.setOutputSize( size );
206
207 QgsLabelingEngineSettings labelingSettings = settings.labelingEngineSettings();
208 labelingSettings.setFlag( Qgis::LabelingFlag::UsePartialCandidates, false );
209 settings.setLabelingEngineSettings( labelingSettings );
210
211 QgsExpressionContext exprContext = settings.expressionContext();
213 settings.setExpressionContext( exprContext );
214
216 mRendererJobs.insert( job, metaTile );
217 QObject::connect( job, &QgsMapRendererJob::finished, mFeedback, [ this, job ]() { processMetaTile( job ); } );
218 job->start();
219 }
220}
221
222// Native XYZ tiles (directory) algorithm
223
224QString QgsXyzTilesDirectoryAlgorithm::name() const
225{
226 return QStringLiteral( "tilesxyzdirectory" );
227}
228
229QString QgsXyzTilesDirectoryAlgorithm::displayName() const
230{
231 return QObject::tr( "Generate XYZ tiles (Directory)" );
232}
233
234QStringList QgsXyzTilesDirectoryAlgorithm::tags() const
235{
236 return QObject::tr( "tiles,xyz,tms,directory" ).split( ',' );
237}
238
239QString QgsXyzTilesDirectoryAlgorithm::shortHelpString() const
240{
241 return QObject::tr( "Generates XYZ tiles of map canvas content and saves them as individual images in a directory." );
242}
243
244QgsXyzTilesDirectoryAlgorithm *QgsXyzTilesDirectoryAlgorithm::createInstance() const
245{
246 return new QgsXyzTilesDirectoryAlgorithm();
247}
248
249void QgsXyzTilesDirectoryAlgorithm::initAlgorithm( const QVariantMap & )
250{
251 createCommonParameters();
252 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "TILE_WIDTH" ), QObject::tr( "Tile width" ), QgsProcessingParameterNumber::Integer, 256, false, 1, 4096 ) );
253 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "TILE_HEIGHT" ), QObject::tr( "Tile height" ), QgsProcessingParameterNumber::Integer, 256, false, 1, 4096 ) );
254 addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "TMS_CONVENTION" ), QObject::tr( "Use inverted tile Y axis (TMS convention)" ), false, true ) );
255
256 std::unique_ptr< QgsProcessingParameterString > titleParam = std::make_unique< QgsProcessingParameterString >( QStringLiteral( "HTML_TITLE" ), QObject::tr( "Leaflet HTML output title" ), QVariant(), false, true );
257 titleParam->setFlags( titleParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
258 addParameter( titleParam.release() );
259 std::unique_ptr< QgsProcessingParameterString > attributionParam = std::make_unique< QgsProcessingParameterString >( QStringLiteral( "HTML_ATTRIBUTION" ), QObject::tr( "Leaflet HTML output attribution" ), QVariant(), false, true );
260 attributionParam->setFlags( attributionParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
261 addParameter( attributionParam.release() );
262 std::unique_ptr< QgsProcessingParameterBoolean > osmParam = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "HTML_OSM" ), QObject::tr( "Include OpenStreetMap basemap in Leaflet HTML output" ), false, true );
263 osmParam->setFlags( osmParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
264 addParameter( osmParam.release() );
265
266 addParameter( new QgsProcessingParameterFolderDestination( QStringLiteral( "OUTPUT_DIRECTORY" ), QObject::tr( "Output directory" ) ) );
267 addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT_HTML" ), QObject::tr( "Output html (Leaflet)" ), QObject::tr( "HTML files (*.html)" ), QVariant(), true ) );
268}
269
270QVariantMap QgsXyzTilesDirectoryAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
271{
272 const bool tms = parameterAsBoolean( parameters, QStringLiteral( "TMS_CONVENTION" ), context );
273 const QString title = parameterAsString( parameters, QStringLiteral( "HTML_TITLE" ), context );
274 const QString attribution = parameterAsString( parameters, QStringLiteral( "HTML_ATTRIBUTION" ), context );
275 const bool useOsm = parameterAsBoolean( parameters, QStringLiteral( "HTML_OSM" ), context );
276 QString outputDir = parameterAsString( parameters, QStringLiteral( "OUTPUT_DIRECTORY" ), context );
277 const QString outputHtml = parameterAsString( parameters, QStringLiteral( "OUTPUT_HTML" ), context );
278
279 mOutputDir = outputDir;
280 mTms = tms;
281
282 for ( int z = mMinZoom; z <= mMaxZoom; z++ )
283 {
284 if ( feedback->isCanceled() )
285 break;
286
287 mMetaTiles += getMetatiles( mWgs84Extent, z, mMetaTileSize );
288 }
289
290 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
291 {
292 layer->moveToThread( QThread::currentThread() );
293 }
294
295 mTotalTiles = mMetaTiles.size();
296
297 QEventLoop loop;
298 // cppcheck-suppress danglingLifetime
299 mEventLoop = &loop;
300 startJobs();
301 loop.exec();
302
303 qDeleteAll( mLayers );
304 mLayers.clear();
305
306 QVariantMap results;
307 results.insert( QStringLiteral( "OUTPUT_DIRECTORY" ), outputDir );
308
309 if ( !outputHtml.isEmpty() )
310 {
311 QString osm = QStringLiteral(
312 "var osm_layer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',"
313 "{minZoom: %1, maxZoom: %2, attribution: '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'}).addTo(map);" )
314 .arg( mMinZoom ).arg( mMaxZoom );
315
316 QString addOsm = useOsm ? osm : QString();
317 QString tmsConvention = tms ? QStringLiteral( "true" ) : QStringLiteral( "false" );
318 QString attr = attribution.isEmpty() ? QStringLiteral( "Created by QGIS" ) : attribution;
319 QString tileSource = QStringLiteral( "'file:///%1/{z}/{x}/{y}.%2'" )
320 .arg( outputDir.replace( "\\", "/" ).toHtmlEscaped() ).arg( mTileFormat.toLower() );
321
322 QString html = QStringLiteral(
323 "<!DOCTYPE html><html><head><title>%1</title><meta charset=\"utf-8\"/>"
324 "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
325 "<link rel=\"stylesheet\" href=\"https://unpkg.com/[email protected]/dist/leaflet.css\""
326 "integrity=\"sha384-o/2yZuJZWGJ4s/adjxVW71R+EO/LyCwdQfP5UWSgX/w87iiTXuvDZaejd3TsN7mf\""
327 "crossorigin=\"\"/>"
328 "<script src=\"https://unpkg.com/[email protected]/dist/leaflet.js\""
329 "integrity=\"sha384-okbbMvvx/qfQkmiQKfd5VifbKZ/W8p1qIsWvE1ROPUfHWsDcC8/BnHohF7vPg2T6\""
330 "crossorigin=\"\"></script>"
331 "<style type=\"text/css\">body {margin: 0;padding: 0;} html, body, #map{width: 100%;height: 100%;}</style></head>"
332 "<body><div id=\"map\"></div><script>"
333 "var map = L.map('map', {attributionControl: false}).setView([%2, %3], %4);"
334 "L.control.attribution({prefix: false}).addTo(map);"
335 "%5"
336 "var tilesource_layer = L.tileLayer(%6, {minZoom: %7, maxZoom: %8, tms: %9, attribution: '%10'}).addTo(map);"
337 "</script></body></html>"
338 )
339 .arg( title.isEmpty() ? QStringLiteral( "Leaflet preview" ) : title )
340 .arg( mWgs84Extent.center().y() )
341 .arg( mWgs84Extent.center().x() )
342 .arg( ( mMaxZoom + mMinZoom ) / 2 )
343 .arg( addOsm )
344 .arg( tileSource )
345 .arg( mMinZoom )
346 .arg( mMaxZoom )
347 .arg( tmsConvention )
348 .arg( attr );
349
350 QFile htmlFile( outputHtml );
351 if ( !htmlFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
352 {
353 throw QgsProcessingException( QObject::tr( "Could not open html file %1" ).arg( outputHtml ) );
354 }
355 QTextStream fout( &htmlFile );
356#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
357 fout.setCodec( "UTF-8" );
358#endif
359 fout << html;
360
361 results.insert( QStringLiteral( "OUTPUT_HTML" ), outputHtml );
362 }
363
364 return results;
365}
366
367void QgsXyzTilesDirectoryAlgorithm::processMetaTile( QgsMapRendererSequentialJob *job )
368{
369 MetaTile metaTile = mRendererJobs.value( job );
370 QImage img = job->renderedImage();
371
372 QMap<QPair<int, int>, Tile>::const_iterator it = metaTile.tiles.constBegin();
373 while ( it != metaTile.tiles.constEnd() )
374 {
375 QPair<int, int> tm = it.key();
376 Tile tile = it.value();
377 QImage tileImage = img.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
378 QDir tileDir( QStringLiteral( "%1/%2/%3" ).arg( mOutputDir ).arg( tile.z ).arg( tile.x ) );
379 tileDir.mkpath( tileDir.absolutePath() );
380 int y = tile.y;
381 if ( mTms )
382 {
383 y = tile2tms( y, tile.z );
384 }
385 tileImage.save( QStringLiteral( "%1/%2.%3" ).arg( tileDir.absolutePath() ).arg( y ).arg( mTileFormat.toLower() ), mTileFormat.toStdString().c_str(), mJpgQuality );
386 ++it;
387 }
388
389 mRendererJobs.remove( job );
390 job->deleteLater();
391
392 mFeedback->setProgress( 100.0 * ( mProcessedTiles++ ) / mTotalTiles );
393
394 if ( mFeedback->isCanceled() )
395 {
396 while ( mRendererJobs.size() > 0 )
397 {
398 QgsMapRendererSequentialJob *j = mRendererJobs.firstKey();
399 j->cancel();
400 mRendererJobs.remove( j );
401 j->deleteLater();
402 }
403 mRendererJobs.clear();
404 mEventLoop->exit();
405 return;
406 }
407
408 if ( mMetaTiles.size() > 0 )
409 {
410 startJobs();
411 }
412 else if ( mMetaTiles.size() == 0 && mRendererJobs.size() == 0 )
413 {
414 mEventLoop->exit();
415 }
416}
417
418// Native XYZ tiles (MBTiles) algorithm
419
420QString QgsXyzTilesMbtilesAlgorithm::name() const
421{
422 return QStringLiteral( "tilesxyzmbtiles" );
423}
424
425QString QgsXyzTilesMbtilesAlgorithm::displayName() const
426{
427 return QObject::tr( "Generate XYZ tiles (MBTiles)" );
428}
429
430QStringList QgsXyzTilesMbtilesAlgorithm::tags() const
431{
432 return QObject::tr( "tiles,xyz,tms,mbtiles" ).split( ',' );
433}
434
435QString QgsXyzTilesMbtilesAlgorithm::shortHelpString() const
436{
437 return QObject::tr( "Generates XYZ tiles of map canvas content and saves them as an MBTiles file." );
438}
439
440QgsXyzTilesMbtilesAlgorithm *QgsXyzTilesMbtilesAlgorithm::createInstance() const
441{
442 return new QgsXyzTilesMbtilesAlgorithm();
443}
444
445void QgsXyzTilesMbtilesAlgorithm::initAlgorithm( const QVariantMap & )
446{
447 createCommonParameters();
448 addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT_FILE" ), QObject::tr( "Output" ), QObject::tr( "MBTiles files (*.mbtiles *.MBTILES)" ) ) );
449}
450
451QVariantMap QgsXyzTilesMbtilesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
452{
453 const QString outputFile = parameterAsString( parameters, QStringLiteral( "OUTPUT_FILE" ), context );
454
455 mMbtilesWriter = std::make_unique<QgsMbTiles>( outputFile );
456 if ( !mMbtilesWriter->create() )
457 {
458 throw QgsProcessingException( QObject::tr( "Failed to create MBTiles file %1" ).arg( outputFile ) );
459 }
460 mMbtilesWriter->setMetadataValue( "format", mTileFormat.toLower() );
461 mMbtilesWriter->setMetadataValue( "name", QFileInfo( outputFile ).baseName() );
462 mMbtilesWriter->setMetadataValue( "version", QStringLiteral( "1.1" ) );
463 mMbtilesWriter->setMetadataValue( "type", QStringLiteral( "overlay" ) );
464 mMbtilesWriter->setMetadataValue( "minzoom", QString::number( mMinZoom ) );
465 mMbtilesWriter->setMetadataValue( "maxzoom", QString::number( mMaxZoom ) );
466 QString boundsStr = QString( "%1,%2,%3,%4" )
467 .arg( mWgs84Extent.xMinimum() ).arg( mWgs84Extent.yMinimum() )
468 .arg( mWgs84Extent.xMaximum() ).arg( mWgs84Extent.yMaximum() );
469 mMbtilesWriter->setMetadataValue( "bounds", boundsStr );
470
471 for ( int z = mMinZoom; z <= mMaxZoom; z++ )
472 {
473 if ( feedback->isCanceled() )
474 break;
475
476 mMetaTiles += getMetatiles( mWgs84Extent, z, mMetaTileSize );
477 }
478
479 mTotalTiles = mMetaTiles.size();
480
481 QEventLoop loop;
482 // cppcheck-suppress danglingLifetime
483 mEventLoop = &loop;
484 startJobs();
485 loop.exec();
486
487 QVariantMap results;
488 results.insert( QStringLiteral( "OUTPUT_FILE" ), outputFile );
489 return results;
490}
491
492void QgsXyzTilesMbtilesAlgorithm::processMetaTile( QgsMapRendererSequentialJob *job )
493{
494 MetaTile metaTile = mRendererJobs.value( job );
495 QImage img = job->renderedImage();
496
497 QMap<QPair<int, int>, Tile>::const_iterator it = metaTile.tiles.constBegin();
498 while ( it != metaTile.tiles.constEnd() )
499 {
500 QPair<int, int> tm = it.key();
501 Tile tile = it.value();
502 QImage tileImage = img.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
503 QByteArray ba;
504 QBuffer buffer( &ba );
505 buffer.open( QIODevice::WriteOnly );
506 tileImage.save( &buffer, mTileFormat.toStdString().c_str(), mJpgQuality );
507 mMbtilesWriter->setTileData( tile.z, tile.x, tile2tms( tile.y, tile.z ), ba );
508 ++it;
509 }
510
511 mRendererJobs.remove( job );
512 job->deleteLater();
513
514 mFeedback->setProgress( 100.0 * ( mProcessedTiles++ ) / mTotalTiles );
515
516 if ( mFeedback->isCanceled() )
517 {
518 while ( mRendererJobs.size() > 0 )
519 {
520 QgsMapRendererSequentialJob *j = mRendererJobs.firstKey();
521 j->cancel();
522 mRendererJobs.remove( j );
523 j->deleteLater();
524 }
525 mRendererJobs.clear();
526 mEventLoop->exit();
527 return;
528 }
529
530 if ( mMetaTiles.size() > 0 )
531 {
532 startJobs();
533 }
534 else if ( mMetaTiles.size() == 0 && mRendererJobs.size() == 0 )
535 {
536 mEventLoop->exit();
537 }
538}
539
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
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:54
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:74
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 setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
const QgsExpressionContext & expressionContext() const
Gets the expression context.
virtual Flags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
@ FlagRequiresProject
The algorithm requires that a valid QgsProject is available from the processing context in order to e...
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.
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.
@ FlagAdvanced
Parameter is an advanced parameter which should be hidden from users by default.
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.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).