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 
16 #include "qgsterraintexturegenerator_p.h"
17 
18 #include <qgsmaprenderercustompainterjob.h>
19 #include <qgsmaprenderersequentialjob.h>
20 #include <qgsmapsettings.h>
21 #include <qgsmapthemecollection.h>
22 #include <qgsproject.h>
23 
24 #include "qgs3dmapsettings.h"
25 
26 #include "qgseventtracing.h"
27 
28 ///@cond PRIVATE
29 
QgsTerrainTextureGenerator(const Qgs3DMapSettings & map)30 QgsTerrainTextureGenerator::QgsTerrainTextureGenerator( const Qgs3DMapSettings &map )
31   : mMap( map )
32   , mLastJobId( 0 )
33   , mTextureSize( QSize( mMap.mapTileResolution(), mMap.mapTileResolution() ) )
34 {
35 }
36 
render(const QgsRectangle & extent,QgsChunkNodeId tileId,const QString & debugText)37 int QgsTerrainTextureGenerator::render( const QgsRectangle &extent, QgsChunkNodeId tileId, const QString &debugText )
38 {
39   QgsMapSettings mapSettings( baseMapSettings() );
40   mapSettings.setExtent( extent );
41 
42   QgsEventTracing::addEvent( QgsEventTracing::AsyncBegin, QStringLiteral( "3D" ), QStringLiteral( "Texture" ), tileId.text() );
43 
44   QgsMapRendererSequentialJob *job = new QgsMapRendererSequentialJob( mapSettings );
45   connect( job, &QgsMapRendererJob::finished, this, &QgsTerrainTextureGenerator::onRenderingFinished );
46   job->start();
47 
48   JobData jobData;
49   jobData.jobId = ++mLastJobId;
50   jobData.tileId = tileId;
51   jobData.job = job;
52   jobData.extent = extent;
53   jobData.debugText = debugText;
54 
55   mJobs.insert( job, jobData );
56   //qDebug() << "added job: " << jobData.jobId << "  .... in queue: " << jobs.count();
57   return jobData.jobId;
58 }
59 
cancelJob(int jobId)60 void QgsTerrainTextureGenerator::cancelJob( int jobId )
61 {
62   Q_FOREACH ( const JobData &jd, mJobs )
63   {
64     if ( jd.jobId == jobId )
65     {
66       //qDebug() << "canceling job " << jobId;
67       jd.job->cancelWithoutBlocking();
68       disconnect( jd.job, &QgsMapRendererJob::finished, this, &QgsTerrainTextureGenerator::onRenderingFinished );
69       jd.job->deleteLater();
70       mJobs.remove( jd.job );
71       return;
72     }
73   }
74   Q_ASSERT( false && "requested job ID does not exist!" );
75 }
76 
waitForFinished()77 void QgsTerrainTextureGenerator::waitForFinished()
78 {
79   for ( QgsMapRendererSequentialJob *job : mJobs.keys() )
80     disconnect( job, &QgsMapRendererJob::finished, this, &QgsTerrainTextureGenerator::onRenderingFinished );
81   QVector<QgsMapRendererSequentialJob *> toBeDeleted;
82   for ( QgsMapRendererSequentialJob *mapJob : mJobs.keys() )
83   {
84     mapJob->waitForFinished();
85     JobData jobData = mJobs.value( mapJob );
86     toBeDeleted.push_back( mapJob );
87 
88     QImage img = mapJob->renderedImage();
89 
90     if ( mMap.showTerrainTilesInfo() )
91     {
92       // extra tile information for debugging
93       QPainter p( &img );
94       p.setPen( Qt::white );
95       p.drawRect( 0, 0, img.width() - 1, img.height() - 1 );
96       p.drawText( img.rect(), jobData.debugText, QTextOption( Qt::AlignCenter ) );
97       p.end();
98     }
99 
100     // pass QImage further
101     emit tileReady( jobData.jobId, img );
102   }
103 
104   for ( QgsMapRendererSequentialJob *mapJob : toBeDeleted )
105   {
106     mJobs.remove( mapJob );
107     mapJob->deleteLater();
108   }
109 }
110 
onRenderingFinished()111 void QgsTerrainTextureGenerator::onRenderingFinished()
112 {
113   QgsMapRendererSequentialJob *mapJob = static_cast<QgsMapRendererSequentialJob *>( sender() );
114 
115   Q_ASSERT( mJobs.contains( mapJob ) );
116   JobData jobData = mJobs.value( mapJob );
117 
118   QImage img = mapJob->renderedImage();
119 
120   if ( mMap.showTerrainTilesInfo() )
121   {
122     // extra tile information for debugging
123     QPainter p( &img );
124     p.setPen( Qt::white );
125     p.drawRect( 0, 0, img.width() - 1, img.height() - 1 );
126     p.drawText( img.rect(), jobData.debugText, QTextOption( Qt::AlignCenter ) );
127     p.end();
128   }
129 
130   mapJob->deleteLater();
131   mJobs.remove( mapJob );
132 
133   //qDebug() << "finished job " << jobData.jobId << "  ... in queue: " << jobs.count();
134 
135   QgsEventTracing::addEvent( QgsEventTracing::AsyncEnd, QStringLiteral( "3D" ), QStringLiteral( "Texture" ), jobData.tileId.text() );
136 
137   // pass QImage further
138   emit tileReady( jobData.jobId, img );
139 }
140 
baseMapSettings()141 QgsMapSettings QgsTerrainTextureGenerator::baseMapSettings()
142 {
143   QgsMapSettings mapSettings;
144 
145   mapSettings.setOutputSize( mTextureSize );
146   mapSettings.setDestinationCrs( mMap.crs() );
147   mapSettings.setBackgroundColor( mMap.backgroundColor() );
148   mapSettings.setFlag( QgsMapSettings::DrawLabeling, mMap.showLabels() );
149   mapSettings.setFlag( QgsMapSettings::Render3DMap );
150   mapSettings.setTransformContext( mMap.transformContext() );
151   mapSettings.setPathResolver( mMap.pathResolver() );
152 
153   QgsMapThemeCollection *mapThemes = mMap.mapThemeCollection();
154   QString mapThemeName = mMap.terrainMapTheme();
155   if ( mapThemeName.isEmpty() || !mapThemes || !mapThemes->hasMapTheme( mapThemeName ) )
156   {
157     mapSettings.setLayers( mMap.terrainLayers() );
158   }
159   else
160   {
161     mapSettings.setLayers( mapThemes->mapThemeVisibleLayers( mapThemeName ) );
162     mapSettings.setLayerStyleOverrides( mapThemes->mapThemeStyleOverrides( mapThemeName ) );
163   }
164 
165   return mapSettings;
166 }
167 
168 /// @endcond
169