QGIS API Documentation 4.1.0-Master (376402f9aeb)
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
21#include "qgslayertree.h"
22#include "qgslayertreelayer.h"
23#include "qgsmaplayerutils.h"
24#include "qgsprovidermetadata.h"
25
26#include <QBuffer>
27#include <QString>
28
29using namespace Qt::StringLiterals;
30
32
33int tile2tms( const int y, const int zoom )
34{
35 double n = std::pow( 2, zoom );
36 return ( int ) std::floor( n - y - 1 );
37}
38
39int lon2tileX( const double lon, const int z )
40{
41 return ( int ) ( std::floor( ( lon + 180.0 ) / 360.0 * ( 1 << z ) ) );
42}
43
44int lat2tileY( const double lat, const int z )
45{
46 double latRad = lat * M_PI / 180.0;
47 return ( int ) ( std::floor( ( 1.0 - std::asinh( std::tan( latRad ) ) / M_PI ) / 2.0 * ( 1 << z ) ) );
48}
49
50double tileX2lon( const int x, const int z )
51{
52 return x / ( double ) ( 1 << z ) * 360.0 - 180;
53}
54
55double tileY2lat( const int y, const int z )
56{
57 double n = M_PI - 2.0 * M_PI * y / ( double ) ( 1 << z );
58 return 180.0 / M_PI * std::atan( 0.5 * ( std::exp( n ) - std::exp( -n ) ) );
59}
60
61void extent2TileXY( QgsRectangle extent, const int zoom, int &xMin, int &yMin, int &xMax, int &yMax )
62{
63 xMin = lon2tileX( extent.xMinimum(), zoom );
64 yMin = lat2tileY( extent.yMinimum(), zoom );
65 xMax = lon2tileX( extent.xMaximum(), zoom );
66 yMax = lat2tileY( extent.xMaximum(), zoom );
67}
68
69QList<MetaTile> getMetatiles( const QgsRectangle extent, const int zoom, const int tileSize )
70{
71 int minX = lon2tileX( extent.xMinimum(), zoom );
72 int minY = lat2tileY( extent.yMaximum(), zoom );
73 int maxX = lon2tileX( extent.xMaximum(), zoom );
74 int maxY = lat2tileY( extent.yMinimum(), zoom );
75 ;
76
77 int i = 0;
78 QMap<QString, MetaTile> tiles;
79 for ( int x = minX; x <= maxX; x++ )
80 {
81 int j = 0;
82 for ( int y = minY; y <= maxY; y++ )
83 {
84 QString key = u"%1:%2"_s.arg( ( int ) ( i / tileSize ) ).arg( ( int ) ( j / tileSize ) );
85 MetaTile tile = tiles.value( key, MetaTile() );
86 tile.addTile( i % tileSize, j % tileSize, Tile( x, y, zoom ) );
87 tiles.insert( key, tile );
88 j++;
89 }
90 i++;
91 }
92 return tiles.values();
93}
94
96
97QString QgsXyzTilesBaseAlgorithm::group() const
98{
99 return QObject::tr( "Raster tools" );
100}
101
102QString QgsXyzTilesBaseAlgorithm::groupId() const
103{
104 return u"rastertools"_s;
105}
106
107Qgis::ProcessingAlgorithmFlags QgsXyzTilesBaseAlgorithm::flags() const
108{
110}
111
112void QgsXyzTilesBaseAlgorithm::createCommonParameters()
113{
114 addParameter( new QgsProcessingParameterExtent( u"EXTENT"_s, QObject::tr( "Extent" ) ) );
115 addParameter( new QgsProcessingParameterNumber( u"ZOOM_MIN"_s, QObject::tr( "Minimum zoom" ), Qgis::ProcessingNumberParameterType::Integer, 12, false, 0, 25 ) );
116 addParameter( new QgsProcessingParameterNumber( u"ZOOM_MAX"_s, QObject::tr( "Maximum zoom" ), Qgis::ProcessingNumberParameterType::Integer, 12, false, 0, 25 ) );
117 addParameter( new QgsProcessingParameterNumber( u"DPI"_s, QObject::tr( "DPI" ), Qgis::ProcessingNumberParameterType::Integer, 96, false, 48, 600 ) );
118 addParameter( new QgsProcessingParameterColor( u"BACKGROUND_COLOR"_s, QObject::tr( "Background color" ), QColor( Qt::transparent ), true, true ) );
119 addParameter( new QgsProcessingParameterBoolean( u"ANTIALIAS"_s, QObject::tr( "Enable antialiasing" ), true ) );
120 addParameter( new QgsProcessingParameterEnum( u"TILE_FORMAT"_s, QObject::tr( "Tile format" ), QStringList() << u"PNG"_s << u"JPG"_s, false, 0 ) );
121 addParameter( new QgsProcessingParameterNumber( u"QUALITY"_s, QObject::tr( "Quality (JPG only)" ), Qgis::ProcessingNumberParameterType::Integer, 75, false, 1, 100 ) );
122 addParameter( new QgsProcessingParameterNumber( u"METATILESIZE"_s, QObject::tr( "Metatile size" ), Qgis::ProcessingNumberParameterType::Integer, 4, false, 1, 20 ) );
123}
124
125bool QgsXyzTilesBaseAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
126{
127 Q_UNUSED( feedback );
128
129 QgsProject *project = context.project();
130
131 const QList<QgsLayerTreeLayer *> projectLayers = project->layerTreeRoot()->findLayers();
132 QSet<QString> visibleLayers;
133 for ( const QgsLayerTreeLayer *layer : projectLayers )
134 {
135 if ( layer->isVisible() )
136 {
137 visibleLayers << layer->layer()->id();
138 }
139 }
140
141 QList<QgsMapLayer *> renderLayers = project->layerTreeRoot()->layerOrder();
142 for ( QgsMapLayer *layer : renderLayers )
143 {
144 if ( visibleLayers.contains( layer->id() ) )
145 {
146 QgsMapLayer *clonedLayer = layer->clone();
147 clonedLayer->moveToThread( nullptr );
148 mLayers << clonedLayer;
149 }
150 }
151
152 QgsRectangle extent = parameterAsExtent( parameters, u"EXTENT"_s, context );
153 QgsCoordinateReferenceSystem extentCrs = parameterAsExtentCrs( parameters, u"EXTENT"_s, context );
154 QgsCoordinateTransform ct( extentCrs, project->crs(), context.transformContext() );
155 try
156 {
157 mExtent = ct.transformBoundingBox( extent );
158 }
159 catch ( QgsCsException & )
160 {
161 feedback->reportError( QObject::tr( "Could not transform the extent into the project CRS" ), true );
162 return false;
163 }
164
165 mMinZoom = parameterAsInt( parameters, u"ZOOM_MIN"_s, context );
166 mMaxZoom = parameterAsInt( parameters, u"ZOOM_MAX"_s, context );
167 mDpi = parameterAsInt( parameters, u"DPI"_s, context );
168 mBackgroundColor = parameterAsColor( parameters, u"BACKGROUND_COLOR"_s, context );
169 mAntialias = parameterAsBool( parameters, u"ANTIALIAS"_s, context );
170 mTileFormat = parameterAsEnum( parameters, u"TILE_FORMAT"_s, context ) ? u"JPG"_s : u"PNG"_s;
171 mJpgQuality = mTileFormat == "JPG"_L1 ? parameterAsInt( parameters, u"QUALITY"_s, context ) : -1;
172 mMetaTileSize = parameterAsInt( parameters, u"METATILESIZE"_s, context );
173 mThreadsNumber = context.maximumThreads();
174 mTransformContext = context.transformContext();
175 mFeedback = feedback;
176
177 mWgs84Crs = QgsCoordinateReferenceSystem( "EPSG:4326" );
178 mMercatorCrs = QgsCoordinateReferenceSystem( "EPSG:3857" );
179 mSrc2Wgs = QgsCoordinateTransform( project->crs(), mWgs84Crs, context.transformContext() );
180 mWgs2Mercator = QgsCoordinateTransform( mWgs84Crs, mMercatorCrs, context.transformContext() );
181 try
182 {
183 mWgs84Extent = mSrc2Wgs.transformBoundingBox( mExtent );
184 }
185 catch ( QgsCsException & )
186 {
187 feedback->reportError( QObject::tr( "Could not transform the extent into WGS84" ), true );
188 return false;
189 }
190
191 if ( parameters.contains( u"TILE_WIDTH"_s ) )
192 {
193 mTileWidth = parameterAsInt( parameters, u"TILE_WIDTH"_s, context );
194 }
195
196 if ( parameters.contains( u"TILE_HEIGHT"_s ) )
197 {
198 mTileHeight = parameterAsInt( parameters, u"TILE_HEIGHT"_s, context );
199 }
200
201 if ( mTileFormat != "PNG"_L1 && mBackgroundColor.alpha() != 255 )
202 {
203 feedback->pushWarning( QObject::tr( "Background color setting ignored, the JPG format only supports fully opaque colors" ) );
204 }
205
206 mScaleMethod = project->scaleMethod();
207
208 return true;
209}
210
211void QgsXyzTilesBaseAlgorithm::checkLayersUsagePolicy( QgsProcessingFeedback *feedback )
212{
213 if ( mTotalTiles > MAXIMUM_OPENSTREETMAP_TILES_FETCH )
214 {
215 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
216 {
218 {
219 // Prevent bulk downloading of tiles from openstreetmap.org as per OSMF tile usage policy
220 feedback->pushFormattedMessage(
221 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" )
222 .arg( layer->name(), u"<a href=\"https://operations.osmfoundation.org/policies/tiles/\">"_s, u"</a>"_s ),
223 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" )
224 .arg( layer->name(), QString(), QString() )
225 );
226 mLayers.removeAll( layer );
227 delete layer;
228 }
229 }
230 }
231}
232
233void QgsXyzTilesBaseAlgorithm::startJobs()
234{
235 while ( mRendererJobs.size() < mThreadsNumber && !mMetaTiles.empty() )
236 {
237 MetaTile metaTile = mMetaTiles.takeFirst();
238
239 QgsMapSettings settings;
240 try
241 {
242 settings.setExtent( mWgs2Mercator.transformBoundingBox( metaTile.extent() ) );
243 }
244 catch ( QgsCsException & )
245 {
246 continue;
247 }
249 settings.setOutputImageFormat( QImage::Format_ARGB32_Premultiplied );
250 settings.setTransformContext( mTransformContext );
251 settings.setDestinationCrs( mMercatorCrs );
252 settings.setLayers( mLayers );
253 settings.setOutputDpi( mDpi );
254 settings.setFlag( Qgis::MapSettingsFlag::Antialiasing, mAntialias );
255 settings.setScaleMethod( mScaleMethod );
256 if ( mTileFormat == "PNG"_L1 || mBackgroundColor.alpha() == 255 )
257 {
258 settings.setBackgroundColor( mBackgroundColor );
259 }
260 QSize size( mTileWidth * metaTile.rows, mTileHeight * metaTile.cols );
261 settings.setOutputSize( size );
262
263 QgsLabelingEngineSettings labelingSettings = settings.labelingEngineSettings();
264 labelingSettings.setFlag( Qgis::LabelingFlag::UsePartialCandidates, false );
265 settings.setLabelingEngineSettings( labelingSettings );
266
267 QgsExpressionContext exprContext = settings.expressionContext();
269 settings.setExpressionContext( exprContext );
270
272 mRendererJobs.insert( job, metaTile );
273 QObject::connect( job, &QgsMapRendererJob::finished, mFeedback, [this, job]() { processMetaTile( job ); } );
274 job->start();
275 }
276}
277
278// Native XYZ tiles (directory) algorithm
279
280QString QgsXyzTilesDirectoryAlgorithm::name() const
281{
282 return u"tilesxyzdirectory"_s;
283}
284
285QString QgsXyzTilesDirectoryAlgorithm::displayName() const
286{
287 return QObject::tr( "Generate XYZ tiles (Directory)" );
288}
289
290QStringList QgsXyzTilesDirectoryAlgorithm::tags() const
291{
292 return QObject::tr( "tiles,xyz,tms,directory" ).split( ',' );
293}
294
295QString QgsXyzTilesDirectoryAlgorithm::shortHelpString() const
296{
297 return QObject::tr( "Generates XYZ tiles of map canvas content and saves them as individual images in a directory." );
298}
299
300QgsXyzTilesDirectoryAlgorithm *QgsXyzTilesDirectoryAlgorithm::createInstance() const
301{
302 return new QgsXyzTilesDirectoryAlgorithm();
303}
304
305void QgsXyzTilesDirectoryAlgorithm::initAlgorithm( const QVariantMap & )
306{
307 createCommonParameters();
308 addParameter( new QgsProcessingParameterNumber( u"TILE_WIDTH"_s, QObject::tr( "Tile width" ), Qgis::ProcessingNumberParameterType::Integer, 256, false, 1, 4096 ) );
309 addParameter( new QgsProcessingParameterNumber( u"TILE_HEIGHT"_s, QObject::tr( "Tile height" ), Qgis::ProcessingNumberParameterType::Integer, 256, false, 1, 4096 ) );
310 addParameter( new QgsProcessingParameterBoolean( u"TMS_CONVENTION"_s, QObject::tr( "Use inverted tile Y axis (TMS convention)" ), false ) );
311
312 auto titleParam = std::make_unique<QgsProcessingParameterString>( u"HTML_TITLE"_s, QObject::tr( "Leaflet HTML output title" ), QVariant(), false, true );
313 titleParam->setFlags( titleParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
314 addParameter( titleParam.release() );
315 auto attributionParam = std::make_unique<QgsProcessingParameterString>( u"HTML_ATTRIBUTION"_s, QObject::tr( "Leaflet HTML output attribution" ), QVariant(), false, true );
316 attributionParam->setFlags( attributionParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
317 addParameter( attributionParam.release() );
318 auto osmParam = std::make_unique<QgsProcessingParameterBoolean>( u"HTML_OSM"_s, QObject::tr( "Include OpenStreetMap basemap in Leaflet HTML output" ), false );
319 osmParam->setFlags( osmParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
320 addParameter( osmParam.release() );
321
322 addParameter( new QgsProcessingParameterFolderDestination( u"OUTPUT_DIRECTORY"_s, QObject::tr( "Output directory" ) ) );
323 addParameter( new QgsProcessingParameterFileDestination( u"OUTPUT_HTML"_s, QObject::tr( "Output html (Leaflet)" ), QObject::tr( "HTML files (*.html)" ), QVariant(), true ) );
324}
325
326QVariantMap QgsXyzTilesDirectoryAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
327{
328 const bool tms = parameterAsBoolean( parameters, u"TMS_CONVENTION"_s, context );
329 const QString title = parameterAsString( parameters, u"HTML_TITLE"_s, context );
330 const QString attribution = parameterAsString( parameters, u"HTML_ATTRIBUTION"_s, context );
331 const bool useOsm = parameterAsBoolean( parameters, u"HTML_OSM"_s, context );
332 QString outputDir = parameterAsString( parameters, u"OUTPUT_DIRECTORY"_s, context );
333 const QString outputHtml = parameterAsString( parameters, u"OUTPUT_HTML"_s, context );
334
335 mOutputDir = outputDir;
336 mTms = tms;
337
338 mTotalTiles = 0;
339 for ( int z = mMinZoom; z <= mMaxZoom; z++ )
340 {
341 if ( feedback->isCanceled() )
342 break;
343
344 mMetaTiles += getMetatiles( mWgs84Extent, z, mMetaTileSize );
345 feedback->pushWarning( QObject::tr( "%1 tiles will be created for zoom level %2" ).arg( mMetaTiles.size() - mTotalTiles ).arg( z ) );
346 mTotalTiles = mMetaTiles.size();
347 }
348 feedback->pushWarning( QObject::tr( "A total of %1 tiles will be created" ).arg( mTotalTiles ) );
349
350 checkLayersUsagePolicy( feedback );
351
352 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
353 {
354 layer->moveToThread( QThread::currentThread() );
355 }
356
357 QEventLoop loop;
358 // cppcheck-suppress danglingLifetime
359 mEventLoop = &loop;
360 startJobs();
361 loop.exec();
362
363 qDeleteAll( mLayers );
364 mLayers.clear();
365
366 QVariantMap results;
367 results.insert( u"OUTPUT_DIRECTORY"_s, outputDir );
368
369 if ( !outputHtml.isEmpty() )
370 {
371 QString osm = QStringLiteral(
372 "var osm_layer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',"
373 "{minZoom: %1, maxZoom: %2, attribution: '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'}).addTo(map);"
374 )
375 .arg( mMinZoom )
376 .arg( mMaxZoom );
377
378 QString addOsm = useOsm ? osm : QString();
379 QString tmsConvention = tms ? u"true"_s : u"false"_s;
380 QString attr = attribution.isEmpty() ? u"Created by QGIS"_s : attribution;
381 QString tileSource = u"'file:///%1/{z}/{x}/{y}.%2'"_s.arg( outputDir.replace( "\\", "/" ).toHtmlEscaped() ).arg( mTileFormat.toLower() );
382
383 QString html = QStringLiteral(
384 "<!DOCTYPE html><html><head><title>%1</title><meta charset=\"utf-8\"/>"
385 "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
386 "<link rel=\"stylesheet\" href=\"https://unpkg.com/[email protected]/dist/leaflet.css\""
387 "integrity=\"sha384-sHL9NAb7lN7rfvG5lfHpm643Xkcjzp4jFvuavGOndn6pjVqS6ny56CAt3nsEVT4H\""
388 "crossorigin=\"\"/>"
389 "<script src=\"https://unpkg.com/[email protected]/dist/leaflet.js\""
390 "integrity=\"sha384-cxOPjt7s7Iz04uaHJceBmS+qpjv2JkIHNVcuOrM+YHwZOmJGBXI00mdUXEq65HTH\""
391 "crossorigin=\"\"></script>"
392 "<style type=\"text/css\">body {margin: 0;padding: 0;} html, body, #map{width: 100%;height: 100%;}</style></head>"
393 "<body><div id=\"map\"></div><script>"
394 "var map = L.map('map', {attributionControl: false}).setView([%2, %3], %4);"
395 "L.control.attribution({prefix: false}).addTo(map);"
396 "%5"
397 "var tilesource_layer = L.tileLayer(%6, {minZoom: %7, maxZoom: %8, tms: %9, attribution: '%10'}).addTo(map);"
398 "</script></body></html>"
399 )
400 .arg( title.isEmpty() ? u"Leaflet preview"_s : title )
401 .arg( mWgs84Extent.center().y() )
402 .arg( mWgs84Extent.center().x() )
403 .arg( ( mMaxZoom + mMinZoom ) / 2 )
404 .arg( addOsm )
405 .arg( tileSource )
406 .arg( mMinZoom )
407 .arg( mMaxZoom )
408 .arg( tmsConvention )
409 .arg( attr );
410
411 QFile htmlFile( outputHtml );
412 if ( !htmlFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
413 {
414 throw QgsProcessingException( QObject::tr( "Could not open html file %1" ).arg( outputHtml ) );
415 }
416 QTextStream fout( &htmlFile );
417 fout << html;
418
419 results.insert( u"OUTPUT_HTML"_s, outputHtml );
420 }
421
422 return results;
423}
424
425void QgsXyzTilesDirectoryAlgorithm::processMetaTile( QgsMapRendererSequentialJob *job )
426{
427 MetaTile metaTile = mRendererJobs.value( job );
428 QImage img = job->renderedImage();
429
430 QMap<QPair<int, int>, Tile>::const_iterator it = metaTile.tiles.constBegin();
431 while ( it != metaTile.tiles.constEnd() )
432 {
433 QPair<int, int> tm = it.key();
434 Tile tile = it.value();
435 QImage tileImage = img.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
436 QDir tileDir( u"%1/%2/%3"_s.arg( mOutputDir ).arg( tile.z ).arg( tile.x ) );
437 tileDir.mkpath( tileDir.absolutePath() );
438 int y = tile.y;
439 if ( mTms )
440 {
441 y = tile2tms( y, tile.z );
442 }
443 tileImage.save( u"%1/%2.%3"_s.arg( tileDir.absolutePath() ).arg( y ).arg( mTileFormat.toLower() ), mTileFormat.toStdString().c_str(), mJpgQuality );
444 ++it;
445 }
446
447 mRendererJobs.remove( job );
448 job->deleteLater();
449
450 mFeedback->setProgress( 100.0 * ( mProcessedTiles++ ) / mTotalTiles );
451
452 if ( mFeedback->isCanceled() )
453 {
454 while ( mRendererJobs.size() > 0 )
455 {
456 QgsMapRendererSequentialJob *j = mRendererJobs.firstKey();
457 j->cancel();
458 mRendererJobs.remove( j );
459 j->deleteLater();
460 }
461 mRendererJobs.clear();
462 if ( mEventLoop )
463 {
464 mEventLoop->exit();
465 }
466 return;
467 }
468
469 if ( mMetaTiles.size() > 0 )
470 {
471 startJobs();
472 }
473 else if ( mMetaTiles.size() == 0 && mRendererJobs.size() == 0 )
474 {
475 if ( mEventLoop )
476 {
477 mEventLoop->exit();
478 }
479 }
480}
481
482// Native XYZ tiles (MBTiles) algorithm
483
484QString QgsXyzTilesMbtilesAlgorithm::name() const
485{
486 return u"tilesxyzmbtiles"_s;
487}
488
489QString QgsXyzTilesMbtilesAlgorithm::displayName() const
490{
491 return QObject::tr( "Generate XYZ tiles (MBTiles)" );
492}
493
494QStringList QgsXyzTilesMbtilesAlgorithm::tags() const
495{
496 return QObject::tr( "tiles,xyz,tms,mbtiles" ).split( ',' );
497}
498
499QString QgsXyzTilesMbtilesAlgorithm::shortHelpString() const
500{
501 return QObject::tr( "Generates XYZ tiles of map canvas content and saves them as an MBTiles file." );
502}
503
504QgsXyzTilesMbtilesAlgorithm *QgsXyzTilesMbtilesAlgorithm::createInstance() const
505{
506 return new QgsXyzTilesMbtilesAlgorithm();
507}
508
509void QgsXyzTilesMbtilesAlgorithm::initAlgorithm( const QVariantMap & )
510{
511 createCommonParameters();
512 addParameter( new QgsProcessingParameterFileDestination( u"OUTPUT_FILE"_s, QObject::tr( "Output" ), QObject::tr( "MBTiles files (*.mbtiles *.MBTILES)" ) ) );
513}
514
515QVariantMap QgsXyzTilesMbtilesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
516{
517 const QString outputFile = parameterAsString( parameters, u"OUTPUT_FILE"_s, context );
518
519 mMbtilesWriter = std::make_unique<QgsMbTiles>( outputFile );
520 if ( !mMbtilesWriter->create() )
521 {
522 throw QgsProcessingException( QObject::tr( "Failed to create MBTiles file %1" ).arg( outputFile ) );
523 }
524 mMbtilesWriter->setMetadataValue( "format", mTileFormat.toLower() );
525 mMbtilesWriter->setMetadataValue( "name", QFileInfo( outputFile ).baseName() );
526 mMbtilesWriter->setMetadataValue( "description", QFileInfo( outputFile ).baseName() );
527 mMbtilesWriter->setMetadataValue( "version", u"1.1"_s );
528 mMbtilesWriter->setMetadataValue( "type", u"overlay"_s );
529 mMbtilesWriter->setMetadataValue( "minzoom", QString::number( mMinZoom ) );
530 mMbtilesWriter->setMetadataValue( "maxzoom", QString::number( mMaxZoom ) );
531 QString boundsStr = QString( "%1,%2,%3,%4" ).arg( mWgs84Extent.xMinimum() ).arg( mWgs84Extent.yMinimum() ).arg( mWgs84Extent.xMaximum() ).arg( mWgs84Extent.yMaximum() );
532 mMbtilesWriter->setMetadataValue( "bounds", boundsStr );
533
534 mTotalTiles = 0;
535 for ( int z = mMinZoom; z <= mMaxZoom; z++ )
536 {
537 if ( feedback->isCanceled() )
538 break;
539
540 mMetaTiles += getMetatiles( mWgs84Extent, z, mMetaTileSize );
541 feedback->pushInfo( QObject::tr( "%1 tiles will be created for zoom level %2" ).arg( mMetaTiles.size() - mTotalTiles ).arg( z ) );
542 mTotalTiles = mMetaTiles.size();
543 }
544 feedback->pushInfo( QObject::tr( "A total of %1 tiles will be created" ).arg( mTotalTiles ) );
545
546 checkLayersUsagePolicy( feedback );
547
548 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
549 {
550 layer->moveToThread( QThread::currentThread() );
551 }
552
553 QEventLoop loop;
554 // cppcheck-suppress danglingLifetime
555 mEventLoop = &loop;
556 startJobs();
557 loop.exec();
558
559 qDeleteAll( mLayers );
560 mLayers.clear();
561
562 QVariantMap results;
563 results.insert( u"OUTPUT_FILE"_s, outputFile );
564 return results;
565}
566
567void QgsXyzTilesMbtilesAlgorithm::processMetaTile( QgsMapRendererSequentialJob *job )
568{
569 MetaTile metaTile = mRendererJobs.value( job );
570 QImage img = job->renderedImage();
571
572 QMap<QPair<int, int>, Tile>::const_iterator it = metaTile.tiles.constBegin();
573 while ( it != metaTile.tiles.constEnd() )
574 {
575 QPair<int, int> tm = it.key();
576 Tile tile = it.value();
577 QImage tileImage = img.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
578 QByteArray ba;
579 QBuffer buffer( &ba );
580 buffer.open( QIODevice::WriteOnly );
581 tileImage.save( &buffer, mTileFormat.toStdString().c_str(), mJpgQuality );
582 mMbtilesWriter->setTileData( tile.z, tile.x, tile2tms( tile.y, tile.z ), ba );
583 ++it;
584 }
585
586 mRendererJobs.remove( job );
587 job->deleteLater();
588
589 mFeedback->setProgress( 100.0 * ( mProcessedTiles++ ) / mTotalTiles );
590
591 if ( mFeedback->isCanceled() )
592 {
593 while ( mRendererJobs.size() > 0 )
594 {
595 QgsMapRendererSequentialJob *j = mRendererJobs.firstKey();
596 j->cancel();
597 mRendererJobs.remove( j );
598 j->deleteLater();
599 }
600 mRendererJobs.clear();
601 if ( mEventLoop )
602 {
603 mEventLoop->exit();
604 }
605 return;
606 }
607
608 if ( mMetaTiles.size() > 0 )
609 {
610 startJobs();
611 }
612 else if ( mMetaTiles.size() == 0 && mRendererJobs.size() == 0 )
613 {
614 if ( mEventLoop )
615 {
616 mEventLoop->exit();
617 }
618 }
619}
620
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
Definition qgis.h:3011
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3791
@ Export
Renderer used for printing or exporting to a file.
Definition qgis.h:3628
@ RequiresProject
The algorithm requires that a valid QgsProject is available from the processing context in order to e...
Definition qgis.h:3778
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3947
@ Antialiasing
Enable anti-aliasing for map rendering.
Definition qgis.h:2879
Represents a coordinate reference system (CRS).
Handles coordinate transforms between two coordinate systems.
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
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.
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.
void cancel() override
Stop the rendering job - does not return until the job has terminated.
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.
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 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.
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 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: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