QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgslayoutitem3dmap.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutitem3dmap.cpp
3  --------------------------------------
4  Date : August 2018
5  Copyright : (C) 2018 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 "qgslayoutitem3dmap.h"
17 
18 #include "qgs3dmapscene.h"
19 #include "qgs3dutils.h"
20 #include "qgscameracontroller.h"
21 #include "qgslayout.h"
22 #include "qgslayoutmodel.h"
23 #include "qgslayoutitemregistry.h"
24 #include "qgsoffscreen3dengine.h"
25 
26 
28  : QgsLayoutItem( layout )
29 {
30  assignFreeId();
31 
32  connect( this, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutItem3DMap::onSizePositionChanged );
33 }
34 
36 
37 
39 {
40  return new QgsLayoutItem3DMap( layout );
41 }
42 
44 {
46 }
47 
49 {
50  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItem3DMap.svg" ) );
51 }
52 
54 {
55  if ( !mLayout )
56  return;
57 
58  QList<QgsLayoutItem3DMap *> mapsList;
59  mLayout->layoutItems( mapsList );
60 
61  int maxId = -1;
62  bool used = false;
63  for ( QgsLayoutItem3DMap *map : qgis::as_const( mapsList ) )
64  {
65  if ( map == this )
66  continue;
67 
68  if ( map->mMapId == mMapId )
69  used = true;
70 
71  maxId = std::max( maxId, map->mMapId );
72  }
73  if ( used )
74  {
75  mMapId = maxId + 1;
76  mLayout->itemsModel()->updateItemDisplayName( this );
77  }
78  updateToolTip();
79 }
80 
82 {
83  if ( !QgsLayoutItem::id().isEmpty() )
84  {
85  return QgsLayoutItem::id();
86  }
87 
88  return tr( "3D Map %1" ).arg( mMapId );
89 }
90 
91 void QgsLayoutItem3DMap::updateToolTip()
92 {
93  setToolTip( displayName() );
94 }
95 
97 {
98  QgsRenderContext &ctx = context.renderContext();
99  QPainter *painter = ctx.painter();
100 
101  int w = static_cast<int>( std::ceil( rect().width() * ctx.scaleFactor() ) );
102  int h = static_cast<int>( std::ceil( rect().height() * ctx.scaleFactor() ) );
103  QRect r( 0, 0, w, h );
104 
105  painter->save();
106 
107  if ( !mSettings )
108  {
109  painter->drawText( r, Qt::AlignCenter, tr( "Scene not set" ) );
110  painter->restore();
111  return;
112  }
113 
114  if ( mSettings->backgroundColor() != backgroundColor() )
115  {
116  mSettings->setBackgroundColor( backgroundColor() );
117  mCapturedImage = QImage();
118  }
119 
120  if ( !mCapturedImage.isNull() )
121  {
122  painter->drawImage( r, mCapturedImage );
123  painter->restore();
124  return;
125  }
126 
127  // we do not have a cached image of the rendered scene - let's request one from the engine
128 
129  if ( mLayout->renderContext().isPreviewRender() )
130  {
131  painter->drawText( r, Qt::AlignCenter, tr( "Loading" ) );
132  painter->restore();
133  }
134 
135  QSizeF sizePixels = mLayout->renderContext().measurementConverter().convert( sizeWithUnits(), QgsUnitTypes::LayoutPixels ).toQSizeF();
136  QSize sizePixelsInt = QSize( static_cast<int>( std::ceil( sizePixels.width() ) ),
137  static_cast<int>( std::ceil( sizePixels.height() ) ) );
138 
139  if ( !mEngine )
140  {
141  mEngine.reset( new QgsOffscreen3DEngine );
142  connect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
143 
144  mEngine->setSize( sizePixelsInt );
145  mScene = new Qgs3DMapScene( *mSettings, mEngine.get() );
146  connect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
147 
148  mEngine->setRootEntity( mScene );
149  }
150 
151  if ( mEngine->size() != sizePixelsInt )
152  mEngine->setSize( sizePixelsInt );
153 
154  mScene->cameraController()->setCameraPose( mCameraPose );
155 
156  if ( mLayout->renderContext().isPreviewRender() )
157  {
158  onSceneStateChanged();
159  }
160  else
161  {
162  // we can't just request a capture and hope it will arrive at some point later.
163  // this is not a preview, we need the rendered scene now!
164  if ( mDrawing )
165  return;
166  mDrawing = true;
167  Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
168  QImage img = Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
169  painter->drawImage( r, img );
170  painter->restore();
171  mDrawing = false;
172  }
173 }
174 
175 void QgsLayoutItem3DMap::onImageCaptured( const QImage &img )
176 {
177  mCapturedImage = img;
178  update();
179 }
180 
181 void QgsLayoutItem3DMap::onSceneStateChanged()
182 {
183  if ( mCapturedImage.isNull() && mScene->sceneState() == Qgs3DMapScene::Ready )
184  {
185  mEngine->requestCaptureImage();
186  }
187 }
188 
189 void QgsLayoutItem3DMap::onSizePositionChanged()
190 {
191  mCapturedImage = QImage();
192  update();
193 }
194 
195 
196 bool QgsLayoutItem3DMap::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
197 {
198  if ( mSettings )
199  {
200  QDomElement elemSettings = mSettings->writeXml( document, context );
201  element.appendChild( elemSettings );
202  }
203 
204  QDomElement elemCameraPose = mCameraPose.writeXml( document );
205  element.appendChild( elemCameraPose );
206 
207  return true;
208 }
209 
210 bool QgsLayoutItem3DMap::readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context )
211 {
212  Q_UNUSED( document )
213  QDomElement elemSettings = element.firstChildElement( QStringLiteral( "qgis3d" ) );
214  if ( !elemSettings.isNull() )
215  {
216  mSettings.reset( new Qgs3DMapSettings );
217  mSettings->readXml( elemSettings, context );
218  if ( mLayout->project() )
219  {
220  mSettings->resolveReferences( *mLayout->project() );
221 
222  mSettings->setTransformContext( mLayout->project()->transformContext() );
223  mSettings->setPathResolver( mLayout->project()->pathResolver() );
224  mSettings->setMapThemeCollection( mLayout->project()->mapThemeCollection() );
225  }
226  }
227 
228  QDomElement elemCameraPose = element.firstChildElement( QStringLiteral( "camera-pose" ) );
229  if ( !elemCameraPose.isNull() )
230  mCameraPose.readXml( elemCameraPose );
231 
232  return true;
233 }
234 
236 {
237  assignFreeId();
238 }
239 
241 {
242  mSettings.reset( settings );
243 
244  mEngine.reset();
245  mScene = nullptr;
246 
247  mCapturedImage = QImage();
248  update();
249 }
250 
252 {
254 
255  mCapturedImage = QImage();
256 }
257 
259 {
260  if ( mCameraPose == pose )
261  return;
262 
263  mCameraPose = pose;
264  mCapturedImage = QImage();
265  update();
266 }
The class is used as a container of context for various read/write operations on other objects...
Base class for graphical items within a QgsLayout.
void setCameraPose(const QgsCameraPose &pose)
Configures camera view.
QgsLayoutSize sizeWithUnits() const
Returns the item&#39;s current size, including units.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void readXml(const QDomElement &elem)
Reads configuration from a DOM element previously written using writeXml()
3 Definition of the world
virtual int type() const override
3 Class that encapsulates camera pose in a 3D scene.
Definition: qgscamerapose.h:41
void setMapSettings(Qgs3DMapSettings *settings)
Configures map scene.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void sizePositionChanged()
Emitted when the item&#39;s size or position changes.
void assignFreeId()
Sets the map id() to a number not yet used in the layout.
const QgsLayout * layout() const
Returns the layout the object is attached to.
QDomElement writeXml(QDomDocument &doc) const
Writes configuration to a new DOM element and returns it.
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgslayoutitem.h:72
3 Off-screen 3D engine implementation.
SceneState sceneState() const
Returns the current state of the scene.
Definition: qgs3dmapscene.h:79
QColor backgroundColor() const
Returns the background color for this item.
QPointer< QgsLayout > mLayout
QString id() const
Returns the item&#39;s ID name.
The scene is fully loaded/updated.
Definition: qgs3dmapscene.h:74
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:44
static QImage captureSceneImage(QgsAbstract3DEngine &engine, Qgs3DMapScene *scene)
Captures image of the current 3D scene of a 3D engine.
Definition: qgs3dutils.cpp:42
QIcon icon() const override
Returns the item&#39;s icon.
Contains information about the context of a rendering operation.
QgsLayoutItem3DMap(QgsLayout *layout)
Constructor for QgsLayoutItem3DMap, with the specified parent layout.
QPainter * painter()
Returns the destination QPainter for the render operation.
3 Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children...
Definition: qgs3dmapscene.h:53
void imageCaptured(const QImage &image)
Emitted after a call to requestCaptureImage() to return the captured image.
3Implements support of 3D map views in print layouts
void sceneStateChanged()
Emitted when the scene&#39;s state has changed.
QString displayName() const override
overridden to show "3D Map 1" type names
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
void setCameraPose(const QgsCameraPose &camPose)
Sets camera pose.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
QgsCameraController * cameraController()
Returns camera controller.
Definition: qgs3dmapscene.h:61
void draw(QgsLayoutItemRenderContext &context) override
Draws the item&#39;s contents using the specified item render context.
static QgsLayoutItem3DMap * create(QgsLayout *layout)
Returns a new 3D map item for the specified layout.