QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 
146  mScene = new Qgs3DMapScene( *mSettings, mEngine.get() );
147  connect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
148 
149  mEngine->setRootEntity( mScene );
150  }
151 
152  if ( mEngine->size() != sizePixelsInt )
153  mEngine->setSize( sizePixelsInt );
154 
155  mScene->cameraController()->setCameraPose( mCameraPose );
156 
157  if ( mLayout->renderContext().isPreviewRender() )
158  {
159  onSceneStateChanged();
160  }
161  else
162  {
163  // we can't just request a capture and hope it will arrive at some point later.
164  // this is not a preview, we need the rendered scene now!
165  if ( mDrawing )
166  return;
167  mDrawing = true;
168  Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
169  QImage img = Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
170  painter->drawImage( r, img );
171  painter->restore();
172  mDrawing = false;
173  }
174 }
175 
176 void QgsLayoutItem3DMap::onImageCaptured( const QImage &img )
177 {
178  mCapturedImage = img;
179  update();
180 }
181 
182 void QgsLayoutItem3DMap::onSceneStateChanged()
183 {
184  if ( mCapturedImage.isNull() && mScene->sceneState() == Qgs3DMapScene::Ready )
185  {
186  mEngine->requestCaptureImage();
187  }
188 }
189 
190 void QgsLayoutItem3DMap::onSizePositionChanged()
191 {
192  mCapturedImage = QImage();
193  update();
194 }
195 
196 
197 bool QgsLayoutItem3DMap::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
198 {
199  if ( mSettings )
200  {
201  QDomElement elemSettings = mSettings->writeXml( document, context );
202  element.appendChild( elemSettings );
203  }
204 
205  QDomElement elemCameraPose = mCameraPose.writeXml( document );
206  element.appendChild( elemCameraPose );
207 
208  return true;
209 }
210 
211 bool QgsLayoutItem3DMap::readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context )
212 {
213  Q_UNUSED( document );
214  QDomElement elemSettings = element.firstChildElement( QStringLiteral( "qgis3d" ) );
215  if ( !elemSettings.isNull() )
216  {
217  mSettings.reset( new Qgs3DMapSettings );
218  mSettings->readXml( elemSettings, context );
219  if ( mLayout->project() )
220  mSettings->resolveReferences( *mLayout->project() );
221  }
222 
223  QDomElement elemCameraPose = element.firstChildElement( QStringLiteral( "camera-pose" ) );
224  if ( !elemCameraPose.isNull() )
225  mCameraPose.readXml( elemCameraPose );
226 
227  return true;
228 }
229 
231 {
232  assignFreeId();
233 }
234 
236 {
237  mSettings.reset( settings );
238 
239  mEngine.reset();
240  mScene = nullptr;
241 
242  mCapturedImage = QImage();
243  update();
244 }
245 
247 {
249 
250  mCapturedImage = QImage();
251 }
252 
254 {
255  if ( mCameraPose == pose )
256  return;
257 
258  mCameraPose = pose;
259  mCapturedImage = QImage();
260  update();
261 }
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.
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()
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
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.
QgsLayoutSize sizeWithUnits() const
Returns the item&#39;s current size, including units.
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.
SceneState sceneState() const
Returns the current state of the scene.
Definition: qgs3dmapscene.h:79
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgslayoutitem.h:71
3 Off-screen 3D engine implementation.
QPointer< QgsLayout > mLayout
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:43
static QImage captureSceneImage(QgsAbstract3DEngine &engine, Qgs3DMapScene *scene)
Captures image of the current 3D scene of a 3D engine.
Definition: qgs3dutils.cpp:31
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.
QColor backgroundColor() const
Returns the background color for this item.
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
QString id() const
Returns the item&#39;s ID name.
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.
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.