QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 }
QgsLayoutItem::id
QString id() const
Returns the item's ID name.
Definition: qgslayoutitem.h:359
QgsLayoutObject::layout
const QgsLayout * layout() const
Returns the layout the object is attached to.
Definition: qgslayoutobject.cpp:216
QgsLayoutItem3DMap::writePropertiesToElement
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
Definition: qgslayoutitem3dmap.cpp:221
QgsLayoutItem3DMap::setMapSettings
void setMapSettings(Qgs3DMapSettings *settings)
Configures map scene.
Definition: qgslayoutitem3dmap.cpp:285
QgsLayoutItem::sizePositionChanged
void sizePositionChanged()
Emitted when the item's size or position changes.
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:34
QgsUnitTypes::LayoutPixels
@ LayoutPixels
Pixels.
Definition: qgsunittypes.h:190
QgsTemporalRangeObject::isTemporal
bool isTemporal() const
Returns true if the object's temporal range is enabled, and the object will be filtered when renderin...
Definition: qgstemporalrangeobject.cpp:30
QgsTemporalRangeObject::setIsTemporal
void setIsTemporal(bool enabled)
Sets whether the temporal range is enabled (i.e.
Definition: qgstemporalrangeobject.cpp:25
QgsLayoutItemRenderContext
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:44
QgsTemporalRangeObject::temporalRange
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
Definition: qgstemporalrangeobject.cpp:43
Qgs3DUtils::captureSceneImage
static QImage captureSceneImage(QgsAbstract3DEngine &engine, Qgs3DMapScene *scene)
Captures image of the current 3D scene of a 3D engine.
Definition: qgs3dutils.cpp:51
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:59
QgsAbstract3DEngine::imageCaptured
void imageCaptured(const QImage &image)
Emitted after a call to requestCaptureImage() to return the captured image.
QgsRenderContext::scaleFactor
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Definition: qgsrendercontext.h:266
QgsLayoutItem3DMap
Implements support of 3D map views in print layouts.
Definition: qgslayoutitem3dmap.h:44
QgsLayoutItem::sizeWithUnits
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
Definition: qgslayoutitem.h:673
qgsshadowrenderingframegraph.h
Qgs3DMapScene::sceneState
SceneState sceneState() const
Returns the current state of the scene.
Definition: qgs3dmapscene.h:116
Qgs3DMapScene::sceneStateChanged
void sceneStateChanged()
Emitted when the scene's state has changed.
Qgis::RendererUsage::Export
@ Export
Renderer used for printing or exporting to a file.
QgsLayoutItem::backgroundColor
QColor backgroundColor() const
Returns the background color for this item.
Definition: qgslayoutitem.h:817
QgsCameraController::setCameraPose
void setCameraPose(const QgsCameraPose &camPose)
Sets camera pose.
Definition: qgscameracontroller.cpp:201
qgs3dutils.h
QgsLayoutItem3DMap::QgsLayoutItem3DMap
QgsLayoutItem3DMap(QgsLayout *layout)
Constructor for QgsLayoutItem3DMap, with the specified parent layout.
Definition: qgslayoutitem3dmap.cpp:29
Qgs3DMapScene::cameraController
QgsCameraController * cameraController()
Returns camera controller.
Definition: qgs3dmapscene.h:78
QgsLayoutItem3DMap::type
virtual int type() const override
Definition: qgslayoutitem3dmap.cpp:45
QgsLayoutItemRegistry::Layout3DMap
@ Layout3DMap
3D map item
Definition: qgslayoutitemregistry.h:366
Qgs3DMapSettings
Definition of the world.
Definition: qgs3dmapsettings.h:57
qgswindow3dengine.h
QgsLayoutItem
Base class for graphical items within a QgsLayout.
Definition: qgslayoutitem.h:112
qgscameracontroller.h
QgsLayoutItem3DMap::displayName
QString displayName() const override
overridden to show "3D Map 1" type names
Definition: qgslayoutitem3dmap.cpp:83
qgslayout.h
QgsLayoutItem3DMap::refresh
void refresh() override
Definition: qgslayoutitem3dmap.cpp:296
QgsCameraPose
Class that encapsulates camera pose in a 3D scene.
Definition: qgscamerapose.h:46
QgsLayoutItem3DMap::finalizeRestoreFromXml
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
Definition: qgslayoutitem3dmap.cpp:280
QgsLayoutItem::refresh
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
Definition: qgslayoutitem.cpp:1177
Qgs3DMapScene
Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children.
Definition: qgs3dmapscene.h:70
QgsCameraPose::readXml
void readXml(const QDomElement &elem)
Reads configuration from a DOM element previously written using writeXml()
Definition: qgscamerapose.cpp:34
QgsLayoutItemRenderContext::renderContext
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:72
Qgs3DMapScene::Ready
@ Ready
The scene is fully loaded/updated.
Definition: qgs3dmapscene.h:111
QgsLayoutItem3DMap::draw
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
Definition: qgslayoutitem3dmap.cpp:98
QgsLayoutObject::mLayout
QPointer< QgsLayout > mLayout
Definition: qgslayoutobject.h:363
QgsLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50
QgsCameraPose::writeXml
QDomElement writeXml(QDomDocument &doc) const
Writes configuration to a new DOM element and returns it.
Definition: qgscamerapose.cpp:22
QgsLayoutItem3DMap::create
static QgsLayoutItem3DMap * create(QgsLayout *layout)
Returns a new 3D map item for the specified layout.
Definition: qgslayoutitem3dmap.cpp:40
QgsLayoutItem3DMap::readPropertiesFromElement
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
Definition: qgslayoutitem3dmap.cpp:245
QgsTemporalRangeObject::setTemporalRange
void setTemporalRange(const QgsDateTimeRange &range)
Sets the temporal range for the object.
Definition: qgstemporalrangeobject.cpp:35
qgsoffscreen3dengine.h
QgsLayoutItem3DMap::setCameraPose
void setCameraPose(const QgsCameraPose &pose)
Configures camera view.
Definition: qgslayoutitem3dmap.cpp:303
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:112
QgsLayoutItem3DMap::assignFreeId
void assignFreeId()
Sets the map id() to a number not yet used in the layout.
Definition: qgslayoutitem3dmap.cpp:55
qgspostprocessingentity.h
Qgis::RendererUsage::View
@ View
Renderer used for displaying on screen.
QgsLayoutItem3DMap::icon
QIcon icon() const override
Returns the item's icon.
Definition: qgslayoutitem3dmap.cpp:50
qgslayoutitemregistry.h
QgsOffscreen3DEngine
Off-screen 3D engine implementation. It is useful for recording rendered 3D scenes of arbitrary size.
Definition: qgsoffscreen3dengine.h:66
qgslayoutmodel.h
QgsLayoutItem3DMap::~QgsLayoutItem3DMap
~QgsLayoutItem3DMap()
qgslayoutitem3dmap.h
qgs3dmapscene.h