QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgsterraintexturegenerator_p.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsterraintexturegenerator_p.cpp
3 --------------------------------------
4 Date : July 2017
5 Copyright : (C) 2017 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
20#include <qgsmapsettings.h>
22#include <qgsproject.h>
23
24#include "qgs3dmapsettings.h"
25
26#include "qgseventtracing.h"
27
29
30QgsTerrainTextureGenerator::QgsTerrainTextureGenerator( const Qgs3DMapSettings &map )
31 : mMap( map )
32 , mLastJobId( 0 )
33 , mTextureSize( QSize( mMap.mapTileResolution(), mMap.mapTileResolution() ) )
34{
35}
36
37int QgsTerrainTextureGenerator::render( const QgsRectangle &extent, QgsChunkNodeId tileId, const QString &debugText )
38{
39 QgsMapSettings mapSettings( baseMapSettings() );
40 mapSettings.setExtent( extent );
41 QSize size = QSize( mTextureSize );
42 if ( mMap.terrainGenerator()->type() == QgsTerrainGenerator::Flat )
43 {
44 // The flat terrain generator might have non-square tiles, clipped at the scene's extent.
45 // We need to produce non-square textures for those cases.
46 const QgsRectangle clippedExtent = extent.intersect( mMap.extent() );
47 if ( !qgsDoubleNear( clippedExtent.width(), clippedExtent.height() ) )
48 {
49 if ( clippedExtent.height() > clippedExtent.width() )
50 size.setWidth( std::round( size.width() * clippedExtent.width() / clippedExtent.height() ) );
51 else if ( clippedExtent.height() < clippedExtent.width() )
52 size.setHeight( std::round( size.height() * clippedExtent.height() / clippedExtent.width() ) );
53 }
54 mapSettings.setExtent( clippedExtent );
55 }
56 mapSettings.setOutputSize( size );
57
58 QgsEventTracing::addEvent( QgsEventTracing::AsyncBegin, QStringLiteral( "3D" ), QStringLiteral( "Texture" ), tileId.text() );
59
61 connect( job, &QgsMapRendererJob::finished, this, &QgsTerrainTextureGenerator::onRenderingFinished );
62
63 JobData jobData;
64 jobData.jobId = ++mLastJobId;
65 jobData.tileId = tileId;
66 jobData.job = job;
67 jobData.extent = extent;
68 jobData.debugText = debugText;
69
70 mJobs.insert( job, jobData ); //store job data just before launching the job
71 job->start();
72
73 // QgsDebugMsgLevel( QStringLiteral("added job: %1 .... in queue: %2").arg( jobData.jobId ).arg( jobs.count() ), 2);
74 return jobData.jobId;
75}
76
77void QgsTerrainTextureGenerator::cancelJob( int jobId )
78{
79 for ( const JobData &jd : std::as_const( mJobs ) )
80 {
81 if ( jd.jobId == jobId )
82 {
83 // QgsDebugMsgLevel( QStringLiteral("canceling job %1").arg( jobId ), 2 );
84 jd.job->cancelWithoutBlocking();
85 disconnect( jd.job, &QgsMapRendererJob::finished, this, &QgsTerrainTextureGenerator::onRenderingFinished );
86 jd.job->deleteLater();
87 mJobs.remove( jd.job );
88 return;
89 }
90 }
91 Q_ASSERT( false && "requested job ID does not exist!" );
92}
93
94void QgsTerrainTextureGenerator::waitForFinished()
95{
96 for ( auto it = mJobs.keyBegin(); it != mJobs.keyEnd(); it++ )
97 disconnect( *it, &QgsMapRendererJob::finished, this, &QgsTerrainTextureGenerator::onRenderingFinished );
98 QVector<QgsMapRendererSequentialJob *> toBeDeleted;
99 for ( auto it = mJobs.constBegin(); it != mJobs.constEnd(); it++ )
100 {
101 QgsMapRendererSequentialJob *mapJob = it.key();
102 mapJob->waitForFinished();
103 JobData jobData = it.value();
104 toBeDeleted.push_back( mapJob );
105
106 QImage img = mapJob->renderedImage();
107
108 if ( mMap.showTerrainTilesInfo() )
109 {
110 // extra tile information for debugging
111 QPainter p( &img );
112 p.setPen( Qt::white );
113 p.drawRect( 0, 0, img.width() - 1, img.height() - 1 );
114 p.drawText( img.rect(), jobData.debugText, QTextOption( Qt::AlignCenter ) );
115 p.end();
116 }
117
118 // pass QImage further
119 emit tileReady( jobData.jobId, img );
120 }
121
122 for ( QgsMapRendererSequentialJob *mapJob : toBeDeleted )
123 {
124 mJobs.remove( mapJob );
125 mapJob->deleteLater();
126 }
127}
128
129void QgsTerrainTextureGenerator::onRenderingFinished()
130{
131 QgsMapRendererSequentialJob *mapJob = static_cast<QgsMapRendererSequentialJob *>( sender() );
132
133 Q_ASSERT( mJobs.contains( mapJob ) );
134 JobData jobData = mJobs.value( mapJob );
135
136 QImage img = mapJob->renderedImage();
137
138 if ( mMap.showTerrainTilesInfo() )
139 {
140 // extra tile information for debugging
141 QPainter p( &img );
142 p.setPen( Qt::white );
143 p.drawRect( 0, 0, img.width() - 1, img.height() - 1 );
144 p.drawText( img.rect(), jobData.debugText, QTextOption( Qt::AlignCenter ) );
145 p.end();
146 }
147
148 mapJob->deleteLater();
149 mJobs.remove( mapJob );
150
151 // QgsDebugMsgLevel( QStringLiteral("finished job %1 ... in queue: %2").arg( jobData.jobId).arg( jobs.count() ), 2 );
152
153 QgsEventTracing::addEvent( QgsEventTracing::AsyncEnd, QStringLiteral( "3D" ), QStringLiteral( "Texture" ), jobData.tileId.text() );
154
155 // pass QImage further
156 emit tileReady( jobData.jobId, img );
157}
158
159QgsMapSettings QgsTerrainTextureGenerator::baseMapSettings()
160{
161 QgsMapSettings mapSettings;
162
163 mapSettings.setOutputSize( mTextureSize );
164 mapSettings.setDestinationCrs( mMap.crs() );
165 mapSettings.setBackgroundColor( mMap.backgroundColor() );
166 mapSettings.setFlag( Qgis::MapSettingsFlag::DrawLabeling, mMap.showLabels() );
168 mapSettings.setTransformContext( mMap.transformContext() );
169 mapSettings.setPathResolver( mMap.pathResolver() );
170 mapSettings.setRendererUsage( mMap.rendererUsage() );
171
172 QList<QgsMapLayer *> layers;
173 QgsMapThemeCollection *mapThemes = mMap.mapThemeCollection();
174 QString mapThemeName = mMap.terrainMapTheme();
175 if ( mapThemeName.isEmpty() || !mapThemes || !mapThemes->hasMapTheme( mapThemeName ) )
176 {
177 layers = mMap.layers();
178 }
179 else
180 {
181 layers = mapThemes->mapThemeVisibleLayers( mapThemeName );
182 mapSettings.setLayerStyleOverrides( mapThemes->mapThemeStyleOverrides( mapThemeName ) );
183 }
184 layers.erase( std::remove_if( layers.begin(),
185 layers.end(),
186 []( const QgsMapLayer * layer ) { return layer->renderer3D(); } ),
187 layers.end() );
188 mapSettings.setLayers( layers );
189
190 return mapSettings;
191}
192
@ DrawLabeling
Enable drawing of labels on top of the map.
@ Render3DMap
Render is for a 3D map.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
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 waitForFinished() override
Block until the job has finished.
The QgsMapSettings class contains configuration for rendering of the map.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
void setRendererUsage(Qgis::RendererUsage rendererUsage)
Sets the rendering usage.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the map of map layer style overrides (key: layer ID, value: style name) where a different style ...
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context, which stores various information regarding which datum transfo...
void setPathResolver(const QgsPathResolver &resolver)
Sets the path resolver for conversion between relative and absolute paths during rendering operations...
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.
Container class that allows storage of map themes consisting of visible map layers and layer styles.
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
QList< QgsMapLayer * > mapThemeVisibleLayers(const QString &name) const
Returns the list of layers that are visible for the specified map theme.
QMap< QString, QString > mapThemeStyleOverrides(const QString &name)
Gets layer style overrides (for QgsMapSettings) of the visible layers for given map theme.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:333
@ Flat
The whole terrain is flat area.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509