29using namespace Qt::StringLiterals;
33int tile2tms(
const int y,
const int zoom )
35 double n = std::pow( 2, zoom );
36 return (
int ) std::floor( n - y - 1 );
39int lon2tileX(
const double lon,
const int z )
41 return (
int ) ( std::floor( ( lon + 180.0 ) / 360.0 * ( 1 << z ) ) );
44int lat2tileY(
const double lat,
const int z )
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 ) ) );
50double tileX2lon(
const int x,
const int z )
52 return x / ( double ) ( 1 << z ) * 360.0 - 180;
55double tileY2lat(
const int y,
const int z )
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 ) ) );
61void extent2TileXY(
QgsRectangle extent,
const int zoom,
int &xMin,
int &yMin,
int &xMax,
int &yMax )
63 xMin = lon2tileX( extent.
xMinimum(), zoom );
64 yMin = lat2tileY( extent.
yMinimum(), zoom );
65 xMax = lon2tileX( extent.
xMaximum(), zoom );
66 yMax = lat2tileY( extent.
xMaximum(), zoom );
69QList<MetaTile> getMetatiles(
const QgsRectangle extent,
const int zoom,
const int tileSize )
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 );
78 QMap<QString, MetaTile> tiles;
79 for (
int x = minX; x <= maxX; x++ )
82 for (
int y = minY; y <= maxY; y++ )
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 );
92 return tiles.values();
97QString QgsXyzTilesBaseAlgorithm::group()
const
99 return QObject::tr(
"Raster tools" );
102QString QgsXyzTilesBaseAlgorithm::groupId()
const
104 return u
"rastertools"_s;
112void QgsXyzTilesBaseAlgorithm::createCommonParameters()
118 addParameter(
new QgsProcessingParameterColor( u
"BACKGROUND_COLOR"_s, QObject::tr(
"Background color" ), QColor( Qt::transparent ),
true,
true ) );
120 addParameter(
new QgsProcessingParameterEnum( u
"TILE_FORMAT"_s, QObject::tr(
"Tile format" ), QStringList() << u
"PNG"_s << u
"JPG"_s,
false, 0 ) );
127 Q_UNUSED( feedback );
132 QSet<QString> visibleLayers;
135 if ( layer->isVisible() )
137 visibleLayers << layer->layer()->id();
144 if ( visibleLayers.contains( layer->id() ) )
147 clonedLayer->moveToThread(
nullptr );
148 mLayers << clonedLayer;
152 QgsRectangle extent = parameterAsExtent( parameters, u
"EXTENT"_s, context );
157 mExtent = ct.transformBoundingBox( extent );
161 feedback->
reportError( QObject::tr(
"Could not transform the extent into the project CRS" ),
true );
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 );
175 mFeedback = feedback;
183 mWgs84Extent = mSrc2Wgs.transformBoundingBox( mExtent );
187 feedback->
reportError( QObject::tr(
"Could not transform the extent into WGS84" ),
true );
191 if ( parameters.contains( u
"TILE_WIDTH"_s ) )
193 mTileWidth = parameterAsInt( parameters, u
"TILE_WIDTH"_s, context );
196 if ( parameters.contains( u
"TILE_HEIGHT"_s ) )
198 mTileHeight = parameterAsInt( parameters, u
"TILE_HEIGHT"_s, context );
201 if ( mTileFormat !=
"PNG"_L1 && mBackgroundColor.alpha() != 255 )
203 feedback->
pushWarning( QObject::tr(
"Background color setting ignored, the JPG format only supports fully opaque colors" ) );
215 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
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() )
226 mLayers.removeAll( layer );
233void QgsXyzTilesBaseAlgorithm::startJobs()
235 while ( mRendererJobs.size() < mThreadsNumber && !mMetaTiles.empty() )
237 MetaTile metaTile = mMetaTiles.takeFirst();
242 settings.
setExtent( mWgs2Mercator.transformBoundingBox( metaTile.extent() ) );
255 if ( mTileFormat ==
"PNG"_L1 || mBackgroundColor.alpha() == 255 )
259 QSize size( mTileWidth * metaTile.rows, mTileHeight * metaTile.cols );
271 mRendererJobs.insert( job, metaTile );
279QString QgsXyzTilesDirectoryAlgorithm::name()
const
281 return u
"tilesxyzdirectory"_s;
284QString QgsXyzTilesDirectoryAlgorithm::displayName()
const
286 return QObject::tr(
"Generate XYZ tiles (Directory)" );
289QStringList QgsXyzTilesDirectoryAlgorithm::tags()
const
291 return QObject::tr(
"tiles,xyz,tms,directory" ).split(
',' );
294QString QgsXyzTilesDirectoryAlgorithm::shortHelpString()
const
296 return QObject::tr(
"Generates XYZ tiles of map canvas content and saves them as individual images in a directory." );
299QgsXyzTilesDirectoryAlgorithm *QgsXyzTilesDirectoryAlgorithm::createInstance()
const
301 return new QgsXyzTilesDirectoryAlgorithm();
304void QgsXyzTilesDirectoryAlgorithm::initAlgorithm(
const QVariantMap & )
306 createCommonParameters();
311 auto titleParam = std::make_unique<QgsProcessingParameterString>( u
"HTML_TITLE"_s, QObject::tr(
"Leaflet HTML output title" ), QVariant(),
false,
true );
313 addParameter( titleParam.release() );
314 auto attributionParam = std::make_unique<QgsProcessingParameterString>( u
"HTML_ATTRIBUTION"_s, QObject::tr(
"Leaflet HTML output attribution" ), QVariant(),
false,
true );
316 addParameter( attributionParam.release() );
317 auto osmParam = std::make_unique<QgsProcessingParameterBoolean>( u
"HTML_OSM"_s, QObject::tr(
"Include OpenStreetMap basemap in Leaflet HTML output" ),
false );
319 addParameter( osmParam.release() );
327 const bool tms = parameterAsBoolean( parameters, u
"TMS_CONVENTION"_s, context );
328 const QString title = parameterAsString( parameters, u
"HTML_TITLE"_s, context );
329 const QString attribution = parameterAsString( parameters, u
"HTML_ATTRIBUTION"_s, context );
330 const bool useOsm = parameterAsBoolean( parameters, u
"HTML_OSM"_s, context );
331 QString outputDir = parameterAsString( parameters, u
"OUTPUT_DIRECTORY"_s, context );
332 const QString outputHtml = parameterAsString( parameters, u
"OUTPUT_HTML"_s, context );
334 mOutputDir = outputDir;
338 for (
int z = mMinZoom; z <= mMaxZoom; z++ )
343 mMetaTiles += getMetatiles( mWgs84Extent, z, mMetaTileSize );
344 feedback->
pushWarning( QObject::tr(
"%1 tiles will be created for zoom level %2" ).arg( mMetaTiles.size() - mTotalTiles ).arg( z ) );
345 mTotalTiles = mMetaTiles.size();
347 feedback->
pushWarning( QObject::tr(
"A total of %1 tiles will be created" ).arg( mTotalTiles ) );
349 checkLayersUsagePolicy( feedback );
351 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
353 layer->moveToThread( QThread::currentThread() );
362 qDeleteAll( mLayers );
366 results.insert( u
"OUTPUT_DIRECTORY"_s, outputDir );
368 if ( !outputHtml.isEmpty() )
370 QString osm = QStringLiteral(
371 "var osm_layer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',"
372 "{minZoom: %1, maxZoom: %2, attribution: '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'}).addTo(map);"
377 QString addOsm = useOsm ? osm : QString();
378 QString tmsConvention = tms ? u
"true"_s : u
"false"_s;
379 QString attr = attribution.isEmpty() ? u
"Created by QGIS"_s : attribution;
380 QString tileSource = u
"'file:///%1/{z}/{x}/{y}.%2'"_s.arg( outputDir.replace(
"\\",
"/" ).toHtmlEscaped() ).arg( mTileFormat.toLower() );
382 QString html = QStringLiteral(
383 "<!DOCTYPE html><html><head><title>%1</title><meta charset=\"utf-8\"/>"
384 "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
385 "<link rel=\"stylesheet\" href=\"https://unpkg.com/[email protected]/dist/leaflet.css\""
386 "integrity=\"sha384-sHL9NAb7lN7rfvG5lfHpm643Xkcjzp4jFvuavGOndn6pjVqS6ny56CAt3nsEVT4H\""
389 "integrity=\"sha384-cxOPjt7s7Iz04uaHJceBmS+qpjv2JkIHNVcuOrM+YHwZOmJGBXI00mdUXEq65HTH\""
390 "crossorigin=\"\"></script>"
391 "<style type=\"text/css\">body {margin: 0;padding: 0;} html, body, #map{width: 100%;height: 100%;}</style></head>"
392 "<body><div id=\"map\"></div><script>"
393 "var map = L.map('map', {attributionControl: false}).setView([%2, %3], %4);"
394 "L.control.attribution({prefix: false}).addTo(map);"
396 "var tilesource_layer = L.tileLayer(%6, {minZoom: %7, maxZoom: %8, tms: %9, attribution: '%10'}).addTo(map);"
397 "</script></body></html>"
399 .arg( title.isEmpty() ? u
"Leaflet preview"_s : title )
400 .arg( mWgs84Extent.center().y() )
401 .arg( mWgs84Extent.center().x() )
402 .arg( ( mMaxZoom + mMinZoom ) / 2 )
407 .arg( tmsConvention )
410 QFile htmlFile( outputHtml );
411 if ( !htmlFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
415 QTextStream fout( &htmlFile );
418 results.insert( u
"OUTPUT_HTML"_s, outputHtml );
426 MetaTile metaTile = mRendererJobs.value( job );
429 QMap<QPair<int, int>, Tile>::const_iterator it = metaTile.tiles.constBegin();
430 while ( it != metaTile.tiles.constEnd() )
432 QPair<int, int> tm = it.key();
433 Tile tile = it.value();
434 QImage tileImage = img.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
435 QDir tileDir( u
"%1/%2/%3"_s.arg( mOutputDir ).arg( tile.z ).arg( tile.x ) );
436 tileDir.mkpath( tileDir.absolutePath() );
440 y = tile2tms( y, tile.z );
442 tileImage.save( u
"%1/%2.%3"_s.arg( tileDir.absolutePath() ).arg( y ).arg( mTileFormat.toLower() ), mTileFormat.toStdString().c_str(), mJpgQuality );
446 mRendererJobs.remove( job );
449 mFeedback->setProgress( 100.0 * ( mProcessedTiles++ ) / mTotalTiles );
451 if ( mFeedback->isCanceled() )
453 while ( mRendererJobs.size() > 0 )
457 mRendererJobs.remove( j );
460 mRendererJobs.clear();
468 if ( mMetaTiles.size() > 0 )
472 else if ( mMetaTiles.size() == 0 && mRendererJobs.size() == 0 )
483QString QgsXyzTilesMbtilesAlgorithm::name()
const
485 return u
"tilesxyzmbtiles"_s;
488QString QgsXyzTilesMbtilesAlgorithm::displayName()
const
490 return QObject::tr(
"Generate XYZ tiles (MBTiles)" );
493QStringList QgsXyzTilesMbtilesAlgorithm::tags()
const
495 return QObject::tr(
"tiles,xyz,tms,mbtiles" ).split(
',' );
498QString QgsXyzTilesMbtilesAlgorithm::shortHelpString()
const
500 return QObject::tr(
"Generates XYZ tiles of map canvas content and saves them as an MBTiles file." );
503QgsXyzTilesMbtilesAlgorithm *QgsXyzTilesMbtilesAlgorithm::createInstance()
const
505 return new QgsXyzTilesMbtilesAlgorithm();
508void QgsXyzTilesMbtilesAlgorithm::initAlgorithm(
const QVariantMap & )
510 createCommonParameters();
516 const QString outputFile = parameterAsString( parameters, u
"OUTPUT_FILE"_s, context );
518 mMbtilesWriter = std::make_unique<QgsMbTiles>( outputFile );
519 if ( !mMbtilesWriter->create() )
523 mMbtilesWriter->setMetadataValue(
"format", mTileFormat.toLower() );
524 mMbtilesWriter->setMetadataValue(
"name", QFileInfo( outputFile ).baseName() );
525 mMbtilesWriter->setMetadataValue(
"description", QFileInfo( outputFile ).baseName() );
526 mMbtilesWriter->setMetadataValue(
"version", u
"1.1"_s );
527 mMbtilesWriter->setMetadataValue(
"type", u
"overlay"_s );
528 mMbtilesWriter->setMetadataValue(
"minzoom", QString::number( mMinZoom ) );
529 mMbtilesWriter->setMetadataValue(
"maxzoom", QString::number( mMaxZoom ) );
530 QString boundsStr = QString(
"%1,%2,%3,%4" ).arg( mWgs84Extent.xMinimum() ).arg( mWgs84Extent.yMinimum() ).arg( mWgs84Extent.xMaximum() ).arg( mWgs84Extent.yMaximum() );
531 mMbtilesWriter->setMetadataValue(
"bounds", boundsStr );
534 for (
int z = mMinZoom; z <= mMaxZoom; z++ )
539 mMetaTiles += getMetatiles( mWgs84Extent, z, mMetaTileSize );
540 feedback->
pushInfo( QObject::tr(
"%1 tiles will be created for zoom level %2" ).arg( mMetaTiles.size() - mTotalTiles ).arg( z ) );
541 mTotalTiles = mMetaTiles.size();
543 feedback->
pushInfo( QObject::tr(
"A total of %1 tiles will be created" ).arg( mTotalTiles ) );
545 checkLayersUsagePolicy( feedback );
547 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
549 layer->moveToThread( QThread::currentThread() );
558 qDeleteAll( mLayers );
562 results.insert( u
"OUTPUT_FILE"_s, outputFile );
568 MetaTile metaTile = mRendererJobs.value( job );
571 QMap<QPair<int, int>, Tile>::const_iterator it = metaTile.tiles.constBegin();
572 while ( it != metaTile.tiles.constEnd() )
574 QPair<int, int> tm = it.key();
575 Tile tile = it.value();
576 QImage tileImage = img.copy( mTileWidth * tm.first, mTileHeight * tm.second, mTileWidth, mTileHeight );
578 QBuffer buffer( &ba );
579 buffer.open( QIODevice::WriteOnly );
580 tileImage.save( &buffer, mTileFormat.toStdString().c_str(), mJpgQuality );
581 mMbtilesWriter->setTileData( tile.z, tile.x, tile2tms( tile.y, tile.z ), ba );
585 mRendererJobs.remove( job );
588 mFeedback->setProgress( 100.0 * ( mProcessedTiles++ ) / mTotalTiles );
590 if ( mFeedback->isCanceled() )
592 while ( mRendererJobs.size() > 0 )
596 mRendererJobs.remove( j );
599 mRendererJobs.clear();
607 if ( mMetaTiles.size() > 0 )
611 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.
Represents a coordinate reference system (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.
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.
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 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,...
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
QgsCoordinateReferenceSystem crs
Qgis::ScaleCalculationMethod scaleMethod
A rectangle specified with double values.
#define MAXIMUM_OPENSTREETMAP_TILES_FETCH