QGIS API Documentation  3.27.0-Master (11ef3e5184)
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"
27 #include "qgswindow3dengine.h"
28 
30  : QgsLayoutItem( layout )
31 {
32  assignFreeId();
33 
34  connect( this, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutItem3DMap::onSizePositionChanged );
35 }
36 
38 
39 
41 {
42  return new QgsLayoutItem3DMap( layout );
43 }
44 
46 {
48 }
49 
51 {
52  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItem3DMap.svg" ) );
53 }
54 
56 {
57  if ( !mLayout )
58  return;
59 
60  QList<QgsLayoutItem3DMap *> mapsList;
61  mLayout->layoutItems( mapsList );
62 
63  int maxId = -1;
64  bool used = false;
65  for ( QgsLayoutItem3DMap *map : std::as_const( mapsList ) )
66  {
67  if ( map == this )
68  continue;
69 
70  if ( map->mMapId == mMapId )
71  used = true;
72 
73  maxId = std::max( maxId, map->mMapId );
74  }
75  if ( used )
76  {
77  mMapId = maxId + 1;
78  mLayout->itemsModel()->updateItemDisplayName( this );
79  }
80  updateToolTip();
81 }
82 
84 {
85  if ( !QgsLayoutItem::id().isEmpty() )
86  {
87  return QgsLayoutItem::id();
88  }
89 
90  return tr( "3D Map %1" ).arg( mMapId );
91 }
92 
93 void QgsLayoutItem3DMap::updateToolTip()
94 {
95  setToolTip( displayName() );
96 }
97 
99 {
100  QgsRenderContext &ctx = context.renderContext();
101  QPainter *painter = ctx.painter();
102 
103  int w = static_cast<int>( std::ceil( rect().width() * ctx.scaleFactor() ) );
104  int h = static_cast<int>( std::ceil( rect().height() * ctx.scaleFactor() ) );
105  QRect r( 0, 0, w, h );
106 
107  painter->save();
108 
109  if ( !mSettings )
110  {
111  painter->drawText( r, Qt::AlignCenter, tr( "Scene not set" ) );
112  painter->restore();
113  return;
114  }
115 
116  if ( mSettings->backgroundColor() != backgroundColor() )
117  {
118  mSettings->setBackgroundColor( backgroundColor() );
119  mCapturedImage = QImage();
120  }
121 
122  if ( !mCapturedImage.isNull() )
123  {
124  painter->drawImage( r, mCapturedImage );
125  painter->restore();
126  return;
127  }
128 
129  // we do not have a cached image of the rendered scene - let's request one from the engine
130 
131  if ( mLayout->renderContext().isPreviewRender() )
132  {
133  painter->drawText( r, Qt::AlignCenter, tr( "Loading" ) );
134  painter->restore();
135  if ( mSettings->rendererUsage() != Qgis::RendererUsage::View )
136  {
137  mSettings->setRendererUsage( Qgis::RendererUsage::View );
138  mEngine.reset(); //we need to rebuild the scene to force the render again
139  }
140  }
141  else
142  {
143  if ( mSettings->rendererUsage() != Qgis::RendererUsage::Export )
144  {
145  mSettings->setRendererUsage( Qgis::RendererUsage::Export );
146  mEngine.reset(); //we need to rebuild the scene to force the render again
147  }
148  }
149 
150  QSizeF sizePixels = mLayout->renderContext().measurementConverter().convert( sizeWithUnits(), QgsUnitTypes::LayoutPixels ).toQSizeF();
151  QSize sizePixelsInt = QSize( static_cast<int>( std::ceil( sizePixels.width() ) ),
152  static_cast<int>( std::ceil( sizePixels.height() ) ) );
153 
154  if ( isTemporal() )
155  mSettings->setTemporalRange( temporalRange() );
156 
157  if ( !mEngine )
158  {
159  mEngine.reset( new QgsOffscreen3DEngine );
160  connect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
161 
162  mEngine->setSize( sizePixelsInt );
163  mScene = new Qgs3DMapScene( *mSettings, mEngine.get() );
164  connect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
165 
166  mEngine->setRootEntity( mScene );
167 
168  }
169 
170  if ( mEngine->size() != sizePixelsInt )
171  mEngine->setSize( sizePixelsInt );
172 
173  mScene->cameraController()->setCameraPose( mCameraPose );
174 
175  if ( mLayout->renderContext().isPreviewRender() )
176  {
177  onSceneStateChanged();
178  }
179  else
180  {
181  // we can't just request a capture and hope it will arrive at some point later.
182  // this is not a preview, we need the rendered scene now!
183  if ( mDrawing )
184  return;
185  mDrawing = true;
186  disconnect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
187  disconnect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
188 
189  Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
190  QImage img = Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
191  painter->drawImage( r, img );
192  painter->restore();
193 
194  connect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
195  connect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
196  mDrawing = false;
197  }
198 }
199 
200 void QgsLayoutItem3DMap::onImageCaptured( const QImage &img )
201 {
202  mCapturedImage = img;
203  update();
204 }
205 
206 void QgsLayoutItem3DMap::onSceneStateChanged()
207 {
208  if ( mCapturedImage.isNull() && mScene->sceneState() == Qgs3DMapScene::Ready )
209  {
210  mEngine->requestCaptureImage();
211  }
212 }
213 
214 void QgsLayoutItem3DMap::onSizePositionChanged()
215 {
216  mCapturedImage = QImage();
217  update();
218 }
219 
220 
221 bool QgsLayoutItem3DMap::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
222 {
223  if ( mSettings )
224  {
225  QDomElement elemSettings = mSettings->writeXml( document, context );
226  element.appendChild( elemSettings );
227  }
228 
229  QDomElement elemCameraPose = mCameraPose.writeXml( document );
230  element.appendChild( elemCameraPose );
231 
232  //temporal settings
233  QDomElement elemTemporal = document.createElement( QStringLiteral( "temporal-settings" ) );
234  elemTemporal.setAttribute( QStringLiteral( "isTemporal" ), isTemporal() ? 1 : 0 );
235  if ( isTemporal() )
236  {
237  elemTemporal.setAttribute( QStringLiteral( "temporalRangeBegin" ), temporalRange().begin().toString( Qt::ISODate ) );
238  elemTemporal.setAttribute( QStringLiteral( "temporalRangeEnd" ), temporalRange().end().toString( Qt::ISODate ) );
239  }
240  element.appendChild( elemTemporal );
241 
242  return true;
243 }
244 
245 bool QgsLayoutItem3DMap::readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context )
246 {
247  Q_UNUSED( document )
248  QDomElement elemSettings = element.firstChildElement( QStringLiteral( "qgis3d" ) );
249  if ( !elemSettings.isNull() )
250  {
251  mSettings.reset( new Qgs3DMapSettings );
252  mSettings->readXml( elemSettings, context );
253  if ( mLayout->project() )
254  {
255  mSettings->resolveReferences( *mLayout->project() );
256 
257  mSettings->setTransformContext( mLayout->project()->transformContext() );
258  mSettings->setPathResolver( mLayout->project()->pathResolver() );
259  mSettings->setMapThemeCollection( mLayout->project()->mapThemeCollection() );
260  }
261  }
262 
263  QDomElement elemCameraPose = element.firstChildElement( QStringLiteral( "camera-pose" ) );
264  if ( !elemCameraPose.isNull() )
265  mCameraPose.readXml( elemCameraPose );
266 
267  //temporal settings
268  QDomElement elemTemporal = element.firstChildElement( QStringLiteral( "temporal-settings" ) );
269  setIsTemporal( elemTemporal.attribute( QStringLiteral( "isTemporal" ) ).toInt() );
270  if ( isTemporal() )
271  {
272  QDateTime begin = QDateTime::fromString( elemTemporal.attribute( QStringLiteral( "temporalRangeBegin" ) ), Qt::ISODate );
273  QDateTime end = QDateTime::fromString( elemTemporal.attribute( QStringLiteral( "temporalRangeBegin" ) ), Qt::ISODate );
274  setTemporalRange( QgsDateTimeRange( begin, end ) );
275  }
276 
277  return true;
278 }
279 
281 {
282  assignFreeId();
283 }
284 
286 {
287  mSettings.reset( settings );
288 
289  mEngine.reset();
290  mScene = nullptr;
291 
292  mCapturedImage = QImage();
293  update();
294 }
295 
297 {
299 
300  mCapturedImage = QImage();
301 }
302 
304 {
305  if ( mCameraPose == pose )
306  return;
307 
308  mCameraPose = pose;
309  mCapturedImage = QImage();
310  update();
311 }
@ Export
Renderer used for printing or exporting to a file.
@ View
Renderer used for displaying on screen.
@ Ready
The scene is fully loaded/updated.
QgsCameraController * cameraController()
Returns camera controller.
Definition: qgs3dmapscene.h:78
void sceneStateChanged()
Emitted when the scene's state has changed.
SceneState sceneState() const
Returns the current state of the scene.
static QImage captureSceneImage(QgsAbstract3DEngine &engine, Qgs3DMapScene *scene)
Captures image of the current 3D scene of a 3D engine.
Definition: qgs3dutils.cpp:51
void imageCaptured(const QImage &image)
Emitted after a call to requestCaptureImage() to return the captured image.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
void setCameraPose(const QgsCameraPose &camPose)
Sets camera pose.
QDomElement writeXml(QDomDocument &doc) const
Writes configuration to a new DOM element and returns it.
void readXml(const QDomElement &elem)
Reads configuration from a DOM element previously written using writeXml()
void setCameraPose(const QgsCameraPose &pose)
Configures camera view.
QString displayName() const override
overridden to show "3D Map 1" type names
QgsLayoutItem3DMap(QgsLayout *layout)
Constructor for QgsLayoutItem3DMap, with the specified parent layout.
virtual int type() const override
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
QIcon icon() const override
Returns the item's icon.
static QgsLayoutItem3DMap * create(QgsLayout *layout)
Returns a new 3D map item for the specified layout.
void setMapSettings(Qgs3DMapSettings *settings)
Configures map scene.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
void assignFreeId()
Sets the map id() to a number not yet used in the layout.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:45
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:72
Base class for graphical items within a QgsLayout.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
QColor backgroundColor() const
Returns the background color for this item.
void sizePositionChanged()
Emitted when the item's size or position changes.
QString id() const
Returns the item's ID name.
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
const QgsLayout * layout() const
Returns the layout the object is attached to.
QPointer< QgsLayout > mLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:51
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
bool isTemporal() const
Returns true if the object's temporal range is enabled, and the object will be filtered when renderin...
void setIsTemporal(bool enabled)
Sets whether the temporal range is enabled (i.e.
void setTemporalRange(const QgsDateTimeRange &range)
Sets the temporal range for the object.
@ LayoutPixels
Pixels.
Definition: qgsunittypes.h:190