30int tile2tms(
const int y,
const int zoom )
32 double n = std::pow( 2, zoom );
33 return (
int )std::floor( n - y - 1 );
36int lon2tileX(
const double lon,
const int z )
38 return (
int )( std::floor( ( lon + 180.0 ) / 360.0 * ( 1 << z ) ) );
41int lat2tileY(
const double lat,
const int z )
43 double latRad = lat * M_PI / 180.0;
44 return (
int )( std::floor( ( 1.0 - std::asinh( std::tan( latRad ) ) / M_PI ) / 2.0 * ( 1 << z ) ) );
47double tileX2lon(
const int x,
const int z )
49 return x / ( double )( 1 << z ) * 360.0 - 180 ;
52double tileY2lat(
const int y,
const int z )
54 double n = M_PI - 2.0 * M_PI * y / ( double )( 1 << z );
55 return 180.0 / M_PI * std::atan( 0.5 * ( std::exp( n ) - std::exp( -n ) ) );
58void extent2TileXY(
QgsRectangle extent,
const int zoom,
int &xMin,
int &yMin,
int &xMax,
int &yMax )
60 xMin = lon2tileX( extent.
xMinimum(), zoom );
61 yMin = lat2tileY( extent.
yMinimum(), zoom );
62 xMax = lon2tileX( extent.
xMaximum(), zoom );
63 yMax = lat2tileY( extent.
xMaximum(), zoom );
66QList< MetaTile > getMetatiles(
const QgsRectangle extent,
const int zoom,
const int tileSize )
68 int minX = lon2tileX( extent.
xMinimum(), zoom );
69 int minY = lat2tileY( extent.
yMaximum(), zoom );
70 int maxX = lon2tileX( extent.
xMaximum(), zoom );
71 int maxY = lat2tileY( extent.
yMinimum(), zoom );;
74 QMap< QString, MetaTile > tiles;
75 for (
int x = minX; x <= maxX; x++ )
78 for (
int y = minY; y <= maxY; y++ )
80 QString key = QStringLiteral(
"%1:%2" ).arg( (
int )( i / tileSize ) ).arg( (
int )( j / tileSize ) );
81 MetaTile tile = tiles.value( key, MetaTile() );
82 tile.addTile( i % tileSize, j % tileSize, Tile( x, y, zoom ) );
83 tiles.insert( key, tile );
88 return tiles.values();
93QString QgsXyzTilesBaseAlgorithm::group()
const
95 return QObject::tr(
"Raster tools" );
98QString QgsXyzTilesBaseAlgorithm::groupId()
const
100 return QStringLiteral(
"rastertools" );
108void QgsXyzTilesBaseAlgorithm::createCommonParameters()
114 addParameter(
new QgsProcessingParameterColor( QStringLiteral(
"BACKGROUND_COLOR" ), QObject::tr(
"Background color" ), QColor( Qt::transparent ),
true,
true ) );
116 addParameter(
new QgsProcessingParameterEnum( QStringLiteral(
"TILE_FORMAT" ), QObject::tr(
"Tile format" ), QStringList() << QStringLiteral(
"PNG" ) << QStringLiteral(
"JPG" ),
false, 0 ) );
123 Q_UNUSED( feedback );
128 QSet< QString > visibleLayers;
131 if ( layer->isVisible() )
133 visibleLayers << layer->layer()->id();
140 if ( visibleLayers.contains( layer->id() ) )
143 clonedLayer->moveToThread(
nullptr );
144 mLayers << clonedLayer;
148 QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral(
"EXTENT" ), context );
151 mExtent = ct.transformBoundingBox( extent );
153 mMinZoom = parameterAsInt( parameters, QStringLiteral(
"ZOOM_MIN" ), context );
154 mMaxZoom = parameterAsInt( parameters, QStringLiteral(
"ZOOM_MAX" ), context );
155 mDpi = parameterAsInt( parameters, QStringLiteral(
"DPI" ), context );
156 mBackgroundColor = parameterAsColor( parameters, QStringLiteral(
"BACKGROUND_COLOR" ), context );
157 mAntialias = parameterAsBool( parameters, QStringLiteral(
"ANTIALIAS" ), context );
158 mTileFormat = parameterAsEnum( parameters, QStringLiteral(
"TILE_FORMAT" ), context ) ? QStringLiteral(
"JPG" ) : QStringLiteral(
"PNG" );
159 mJpgQuality = mTileFormat == QLatin1String(
"JPG" ) ? parameterAsInt( parameters, QStringLiteral(
"QUALITY" ), context ) : -1;
160 mMetaTileSize = parameterAsInt( parameters, QStringLiteral(
"METATILESIZE" ), context );
163 mFeedback = feedback;
170 mWgs84Extent = mSrc2Wgs.transformBoundingBox( mExtent );
172 if ( parameters.contains( QStringLiteral(
"TILE_WIDTH" ) ) )
174 mTileWidth = parameterAsInt( parameters, QStringLiteral(
"TILE_WIDTH" ), context );
177 if ( parameters.contains( QStringLiteral(
"TILE_HEIGHT" ) ) )
179 mTileHeight = parameterAsInt( parameters, QStringLiteral(
"TILE_HEIGHT" ), context );
182 if ( mTileFormat != QLatin1String(
"PNG" ) && mBackgroundColor.alpha() != 255 )
184 feedback->
pushWarning( QObject::tr(
"Background color setting ignored, the JPG format only supports fully opaque colors" ) );
194 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
199 feedback->
pushFormattedMessage( 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" ).arg( layer->name(), QStringLiteral(
"<a href=\"https://operations.osmfoundation.org/policies/tiles/\">" ), QStringLiteral(
"</a>" ) ),
200 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" ).arg( layer->name(), QString(), QString() ) );
201 mLayers.removeAll( layer );
208void QgsXyzTilesBaseAlgorithm::startJobs()
210 while ( mRendererJobs.size() < mThreadsNumber && !mMetaTiles.empty() )
212 MetaTile metaTile = mMetaTiles.takeFirst();
215 settings.
setExtent( mWgs2Mercator.transformBoundingBox( metaTile.extent() ) );
222 if ( mTileFormat == QLatin1String(
"PNG" ) || mBackgroundColor.alpha() == 255 )
226 QSize size( mTileWidth * metaTile.rows, mTileHeight * metaTile.cols );
238 mRendererJobs.insert( job, metaTile );
246QString QgsXyzTilesDirectoryAlgorithm::name()
const
248 return QStringLiteral(
"tilesxyzdirectory" );
251QString QgsXyzTilesDirectoryAlgorithm::displayName()
const
253 return QObject::tr(
"Generate XYZ tiles (Directory)" );
256QStringList QgsXyzTilesDirectoryAlgorithm::tags()
const
258 return QObject::tr(
"tiles,xyz,tms,directory" ).split(
',' );
261QString QgsXyzTilesDirectoryAlgorithm::shortHelpString()
const
263 return QObject::tr(
"Generates XYZ tiles of map canvas content and saves them as individual images in a directory." );
266QgsXyzTilesDirectoryAlgorithm *QgsXyzTilesDirectoryAlgorithm::createInstance()
const
268 return new QgsXyzTilesDirectoryAlgorithm();
271void QgsXyzTilesDirectoryAlgorithm::initAlgorithm(
const QVariantMap & )
273 createCommonParameters();
276 addParameter(
new QgsProcessingParameterBoolean( QStringLiteral(
"TMS_CONVENTION" ), QObject::tr(
"Use inverted tile Y axis (TMS convention)" ),
false,
true ) );
278 std::unique_ptr< QgsProcessingParameterString > titleParam = std::make_unique< QgsProcessingParameterString >( QStringLiteral(
"HTML_TITLE" ), QObject::tr(
"Leaflet HTML output title" ), QVariant(),
false,
true );
280 addParameter( titleParam.release() );
281 std::unique_ptr< QgsProcessingParameterString > attributionParam = std::make_unique< QgsProcessingParameterString >( QStringLiteral(
"HTML_ATTRIBUTION" ), QObject::tr(
"Leaflet HTML output attribution" ), QVariant(),
false,
true );
283 addParameter( attributionParam.release() );
284 std::unique_ptr< QgsProcessingParameterBoolean > osmParam = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral(
"HTML_OSM" ), QObject::tr(
"Include OpenStreetMap basemap in Leaflet HTML output" ),
false,
true );
286 addParameter( osmParam.release() );
289 addParameter(
new QgsProcessingParameterFileDestination( QStringLiteral(
"OUTPUT_HTML" ), QObject::tr(
"Output html (Leaflet)" ), QObject::tr(
"HTML files (*.html)" ), QVariant(),
true ) );
294 const bool tms = parameterAsBoolean( parameters, QStringLiteral(
"TMS_CONVENTION" ), context );
295 const QString title = parameterAsString( parameters, QStringLiteral(
"HTML_TITLE" ), context );
296 const QString attribution = parameterAsString( parameters, QStringLiteral(
"HTML_ATTRIBUTION" ), context );
297 const bool useOsm = parameterAsBoolean( parameters, QStringLiteral(
"HTML_OSM" ), context );
298 QString outputDir = parameterAsString( parameters, QStringLiteral(
"OUTPUT_DIRECTORY" ), context );
299 const QString outputHtml = parameterAsString( parameters, QStringLiteral(
"OUTPUT_HTML" ), context );
301 mOutputDir = outputDir;
305 for (
int z = mMinZoom; z <= mMaxZoom; z++ )
310 mMetaTiles += getMetatiles( mWgs84Extent, z, mMetaTileSize );
311 feedback->
pushWarning( QObject::tr(
"%1 tiles will be created for zoom level %2" ).arg( mMetaTiles.size() - mTotalTiles ).arg( z ) );
312 mTotalTiles = mMetaTiles.size();
314 feedback->
pushWarning( QObject::tr(
"A total of %1 tiles will be created" ).arg( mTotalTiles ) );
316 checkLayersUsagePolicy( feedback );
318 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
320 layer->moveToThread( QThread::currentThread() );
329 qDeleteAll( mLayers );
333 results.insert( QStringLiteral(
"OUTPUT_DIRECTORY" ), outputDir );
335 if ( !outputHtml.isEmpty() )
337 QString osm = QStringLiteral(
338 "var osm_layer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',"
339 "{minZoom: %1, maxZoom: %2, attribution: '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'}).addTo(map);" )
340 .arg( mMinZoom ).arg( mMaxZoom );
342 QString addOsm = useOsm ? osm : QString();
343 QString tmsConvention = tms ? QStringLiteral(
"true" ) : QStringLiteral(
"false" );
344 QString attr = attribution.isEmpty() ? QStringLiteral(
"Created by QGIS" ) : attribution;
345 QString tileSource = QStringLiteral(
"'file:///%1/{z}/{x}/{y}.%2'" )
346 .arg( outputDir.replace(
"\\",
"/" ).toHtmlEscaped() ).arg( mTileFormat.toLower() );
348 QString html = QStringLiteral(
349 "<!DOCTYPE html><html><head><title>%1</title><meta charset=\"utf-8\"/>"
350 "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
351 "<link rel=\"stylesheet\" href=\"https://unpkg.com/[email protected]/dist/leaflet.css\""
352 "integrity=\"sha384-sHL9NAb7lN7rfvG5lfHpm643Xkcjzp4jFvuavGOndn6pjVqS6ny56CAt3nsEVT4H\""
355 "integrity=\"sha384-cxOPjt7s7Iz04uaHJceBmS+qpjv2JkIHNVcuOrM+YHwZOmJGBXI00mdUXEq65HTH\""
356 "crossorigin=\"\"></script>"
357 "<style type=\"text/css\">body {margin: 0;padding: 0;} html, body, #map{width: 100%;height: 100%;}</style></head>"
358 "<body><div id=\"map\"></div><script>"
359 "var map = L.map('map', {attributionControl: false}).setView([%2, %3], %4);"
360 "L.control.attribution({prefix: false}).addTo(map);"
362 "var tilesource_layer = L.tileLayer(%6, {minZoom: %7, maxZoom: %8, tms: %9, attribution: '%10'}).addTo(map);"
363 "</script></body></html>"
365 .arg( title.isEmpty() ? QStringLiteral(
"Leaflet preview" ) : title )
366 .arg( mWgs84Extent.center().y() )
367 .arg( mWgs84Extent.center().x() )
368 .arg( ( mMaxZoom + mMinZoom ) / 2 )
373 .arg( tmsConvention )
376 QFile htmlFile( outputHtml );
377 if ( !htmlFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
381 QTextStream fout( &htmlFile );
382#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
383 fout.setCodec(
"UTF-8" );
387 results.insert( QStringLiteral(
"OUTPUT_HTML" ), outputHtml );
395 MetaTile metaTile = mRendererJobs.value( job );
398 QMap<QPair<int, int>, Tile>::const_iterator it = metaTile.tiles.constBegin();
399 while ( it != metaTile.tiles.constEnd() )
401 QPair<int, int> tm = it.key();
402 Tile tile = it.value();
403 QImage tileImage = img.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
404 QDir tileDir( QStringLiteral(
"%1/%2/%3" ).arg( mOutputDir ).arg( tile.z ).arg( tile.x ) );
405 tileDir.mkpath( tileDir.absolutePath() );
409 y = tile2tms( y, tile.z );
411 tileImage.save( QStringLiteral(
"%1/%2.%3" ).arg( tileDir.absolutePath() ).arg( y ).arg( mTileFormat.toLower() ), mTileFormat.toStdString().c_str(), mJpgQuality );
415 mRendererJobs.remove( job );
418 mFeedback->setProgress( 100.0 * ( mProcessedTiles++ ) / mTotalTiles );
420 if ( mFeedback->isCanceled() )
422 while ( mRendererJobs.size() > 0 )
426 mRendererJobs.remove( j );
429 mRendererJobs.clear();
437 if ( mMetaTiles.size() > 0 )
441 else if ( mMetaTiles.size() == 0 && mRendererJobs.size() == 0 )
452QString QgsXyzTilesMbtilesAlgorithm::name()
const
454 return QStringLiteral(
"tilesxyzmbtiles" );
457QString QgsXyzTilesMbtilesAlgorithm::displayName()
const
459 return QObject::tr(
"Generate XYZ tiles (MBTiles)" );
462QStringList QgsXyzTilesMbtilesAlgorithm::tags()
const
464 return QObject::tr(
"tiles,xyz,tms,mbtiles" ).split(
',' );
467QString QgsXyzTilesMbtilesAlgorithm::shortHelpString()
const
469 return QObject::tr(
"Generates XYZ tiles of map canvas content and saves them as an MBTiles file." );
472QgsXyzTilesMbtilesAlgorithm *QgsXyzTilesMbtilesAlgorithm::createInstance()
const
474 return new QgsXyzTilesMbtilesAlgorithm();
477void QgsXyzTilesMbtilesAlgorithm::initAlgorithm(
const QVariantMap & )
479 createCommonParameters();
485 const QString outputFile = parameterAsString( parameters, QStringLiteral(
"OUTPUT_FILE" ), context );
487 mMbtilesWriter = std::make_unique<QgsMbTiles>( outputFile );
488 if ( !mMbtilesWriter->create() )
492 mMbtilesWriter->setMetadataValue(
"format", mTileFormat.toLower() );
493 mMbtilesWriter->setMetadataValue(
"name", QFileInfo( outputFile ).baseName() );
494 mMbtilesWriter->setMetadataValue(
"version", QStringLiteral(
"1.1" ) );
495 mMbtilesWriter->setMetadataValue(
"type", QStringLiteral(
"overlay" ) );
496 mMbtilesWriter->setMetadataValue(
"minzoom", QString::number( mMinZoom ) );
497 mMbtilesWriter->setMetadataValue(
"maxzoom", QString::number( mMaxZoom ) );
498 QString boundsStr = QString(
"%1,%2,%3,%4" )
499 .arg( mWgs84Extent.xMinimum() ).arg( mWgs84Extent.yMinimum() )
500 .arg( mWgs84Extent.xMaximum() ).arg( mWgs84Extent.yMaximum() );
501 mMbtilesWriter->setMetadataValue(
"bounds", boundsStr );
504 for (
int z = mMinZoom; z <= mMaxZoom; z++ )
509 mMetaTiles += getMetatiles( mWgs84Extent, z, mMetaTileSize );
510 feedback->
pushInfo( QObject::tr(
"%1 tiles will be created for zoom level %2" ).arg( mMetaTiles.size() - mTotalTiles ).arg( z ) );
511 mTotalTiles = mMetaTiles.size();
513 feedback->
pushInfo( QObject::tr(
"A total of %1 tiles will be created" ).arg( mTotalTiles ) );
515 checkLayersUsagePolicy( feedback );
517 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
519 layer->moveToThread( QThread::currentThread() );
528 qDeleteAll( mLayers );
532 results.insert( QStringLiteral(
"OUTPUT_FILE" ), outputFile );
538 MetaTile metaTile = mRendererJobs.value( job );
541 QMap<QPair<int, int>, Tile>::const_iterator it = metaTile.tiles.constBegin();
542 while ( it != metaTile.tiles.constEnd() )
544 QPair<int, int> tm = it.key();
545 Tile tile = it.value();
546 QImage tileImage = img.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
548 QBuffer buffer( &ba );
549 buffer.open( QIODevice::WriteOnly );
550 tileImage.save( &buffer, mTileFormat.toStdString().c_str(), mJpgQuality );
551 mMbtilesWriter->setTileData( tile.z, tile.x, tile2tms( tile.y, tile.z ), ba );
555 mRendererJobs.remove( job );
558 mFeedback->setProgress( 100.0 * ( mProcessedTiles++ ) / mTotalTiles );
560 if ( mFeedback->isCanceled() )
562 while ( mRendererJobs.size() > 0 )
566 mRendererJobs.remove( j );
569 mRendererJobs.clear();
577 if ( mMetaTiles.size() > 0 )
581 else if ( mMetaTiles.size() == 0 && mRendererJobs.size() == 0 )
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
@ RequiresProject
The algorithm requires that a valid QgsProject is available from the processing context in order to e...
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
@ Antialiasing
Enable anti-aliasing for map rendering.
This class represents a coordinate reference system (CRS).
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.
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.
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
void finished()
emitted when asynchronous rendering is finished (or canceled).
void start()
Start the rendering job and immediately return.
Job implementation that renders everything sequentially in one thread.
QImage renderedImage() override
Gets a preview/resulting image.
void cancel() override
Stop the rendering job - does not return until the job has terminated.
The QgsMapSettings class contains configuration for rendering of the map.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
void setOutputDpi(double dpi)
Sets the dpi (dots per inch) used for conversion between real world units (e.g.
void setOutputImageFormat(QImage::Format format)
sets format of internal QImage
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets the global configuration of the labeling engine.
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context, which stores various information regarding which datum transfo...
void setOutputSize(QSize size)
Sets the size of the resulting map image, in pixels.
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
void setFlag(Qgis::MapSettingsFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
const QgsExpressionContext & expressionContext() const
Gets the expression context.
virtual Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
int maximumThreads() const
Returns the (optional) number of threads to use when running algorithms.
Custom exception class for processing related exceptions.
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.
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,...
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
QgsCoordinateReferenceSystem crs
A rectangle specified with double values.
#define MAXIMUM_OPENSTREETMAP_TILES_FETCH