QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 ( isTemporal() )
140  mSettings->setTemporalRange( temporalRange() );
141 
142  if ( !mEngine )
143  {
144  mEngine.reset( new QgsOffscreen3DEngine );
145  connect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
146 
147  mEngine->setSize( sizePixelsInt );
148  mScene = new Qgs3DMapScene( *mSettings, mEngine.get() );
149  connect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
150 
151  mEngine->setRootEntity( mScene );
152  }
153 
154  if ( mEngine->size() != sizePixelsInt )
155  mEngine->setSize( sizePixelsInt );
156 
157  mScene->cameraController()->setCameraPose( mCameraPose );
158 
159  if ( mLayout->renderContext().isPreviewRender() )
160  {
161  onSceneStateChanged();
162  }
163  else
164  {
165  // we can't just request a capture and hope it will arrive at some point later.
166  // this is not a preview, we need the rendered scene now!
167  if ( mDrawing )
168  return;
169  mDrawing = true;
170  Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
171  QImage img = Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
172  painter->drawImage( r, img );
173  painter->restore();
174  mDrawing = false;
175  }
176 }
177 
178 void QgsLayoutItem3DMap::onImageCaptured( const QImage &img )
179 {
180  mCapturedImage = img;
181  update();
182 }
183 
184 void QgsLayoutItem3DMap::onSceneStateChanged()
185 {
186  if ( mCapturedImage.isNull() && mScene->sceneState() == Qgs3DMapScene::Ready )
187  {
188  mEngine->requestCaptureImage();
189  }
190 }
191 
192 void QgsLayoutItem3DMap::onSizePositionChanged()
193 {
194  mCapturedImage = QImage();
195  update();
196 }
197 
198 
199 bool QgsLayoutItem3DMap::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
200 {
201  if ( mSettings )
202  {
203  QDomElement elemSettings = mSettings->writeXml( document, context );
204  element.appendChild( elemSettings );
205  }
206 
207  QDomElement elemCameraPose = mCameraPose.writeXml( document );
208  element.appendChild( elemCameraPose );
209 
210  //temporal settings
211  QDomElement elemTemporal = document.createElement( QStringLiteral( "temporal-settings" ) );
212  elemTemporal.setAttribute( QStringLiteral( "isTemporal" ), isTemporal() ? 1 : 0 );
213  if ( isTemporal() )
214  {
215  elemTemporal.setAttribute( QStringLiteral( "temporalRangeBegin" ), temporalRange().begin().toString( Qt::ISODate ) );
216  elemTemporal.setAttribute( QStringLiteral( "temporalRangeEnd" ), temporalRange().end().toString( Qt::ISODate ) );
217  }
218  element.appendChild( elemTemporal );
219 
220  return true;
221 }
222 
223 bool QgsLayoutItem3DMap::readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context )
224 {
225  Q_UNUSED( document )
226  QDomElement elemSettings = element.firstChildElement( QStringLiteral( "qgis3d" ) );
227  if ( !elemSettings.isNull() )
228  {
229  mSettings.reset( new Qgs3DMapSettings );
230  mSettings->readXml( elemSettings, context );
231  if ( mLayout->project() )
232  {
233  mSettings->resolveReferences( *mLayout->project() );
234 
235  mSettings->setTransformContext( mLayout->project()->transformContext() );
236  mSettings->setPathResolver( mLayout->project()->pathResolver() );
237  mSettings->setMapThemeCollection( mLayout->project()->mapThemeCollection() );
238  }
239  }
240 
241  QDomElement elemCameraPose = element.firstChildElement( QStringLiteral( "camera-pose" ) );
242  if ( !elemCameraPose.isNull() )
243  mCameraPose.readXml( elemCameraPose );
244 
245  //temporal settings
246  QDomElement elemTemporal = element.firstChildElement( QStringLiteral( "temporal-settings" ) );
247  setIsTemporal( elemTemporal.attribute( QStringLiteral( "isTemporal" ) ).toInt() );
248  if ( isTemporal() )
249  {
250  QDateTime begin = QDateTime::fromString( elemTemporal.attribute( QStringLiteral( "temporalRangeBegin" ) ), Qt::ISODate );
251  QDateTime end = QDateTime::fromString( elemTemporal.attribute( QStringLiteral( "temporalRangeBegin" ) ), Qt::ISODate );
252  setTemporalRange( QgsDateTimeRange( begin, end ) );
253  }
254 
255  return true;
256 }
257 
259 {
260  assignFreeId();
261 }
262 
264 {
265  mSettings.reset( settings );
266 
267  mEngine.reset();
268  mScene = nullptr;
269 
270  mCapturedImage = QImage();
271  update();
272 }
273 
275 {
277 
278  mCapturedImage = QImage();
279 }
280 
282 {
283  if ( mCameraPose == pose )
284  return;
285 
286  mCameraPose = pose;
287  mCapturedImage = QImage();
288  update();
289 }
QgsLayoutItem::id
QString id() const
Returns the item's ID name.
Definition: qgslayoutitem.h:354
QgsLayoutObject::layout
const QgsLayout * layout() const
Returns the layout the object is attached to.
Definition: qgslayoutobject.cpp:118
QgsLayoutItem3DMap::writePropertiesToElement
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
Definition: qgslayoutitem3dmap.cpp:199
QgsLayoutItem3DMap::setMapSettings
void setMapSettings(Qgs3DMapSettings *settings)
Configures map scene.
Definition: qgslayoutitem3dmap.cpp:263
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:605
QgsLayoutItem::sizePositionChanged
void sizePositionChanged()
Emitted when the item's size or position changes.
QgsReadWriteContext
Definition: qgsreadwritecontext.h:34
QgsUnitTypes::LayoutPixels
@ LayoutPixels
Pixels.
Definition: qgsunittypes.h:189
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
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:42
QgsRenderContext
Definition: qgsrendercontext.h:57
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:317
QgsLayoutItem3DMap
Definition: qgslayoutitem3dmap.h:44
QgsLayoutItem::sizeWithUnits
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
Definition: qgslayoutitem.h:668
Qgs3DMapScene::sceneState
SceneState sceneState() const
Returns the current state of the scene.
Definition: qgs3dmapscene.h:92
Qgs3DMapScene::sceneStateChanged
void sceneStateChanged()
Emitted when the scene's state has changed.
QgsLayoutItem::backgroundColor
QColor backgroundColor() const
Returns the background color for this item.
Definition: qgslayoutitem.h:812
QgsCameraController::setCameraPose
void setCameraPose(const QgsCameraPose &camPose)
Sets camera pose.
Definition: qgscameracontroller.cpp:210
qgs3dutils.h
QgsLayoutItem3DMap::QgsLayoutItem3DMap
QgsLayoutItem3DMap(QgsLayout *layout)
Constructor for QgsLayoutItem3DMap, with the specified parent layout.
Definition: qgslayoutitem3dmap.cpp:27
Qgs3DMapScene::cameraController
QgsCameraController * cameraController()
Returns camera controller.
Definition: qgs3dmapscene.h:68
QgsLayoutItem3DMap::type
virtual int type() const override
Definition: qgslayoutitem3dmap.cpp:43
QgsLayoutItemRegistry::Layout3DMap
@ Layout3DMap
3D map item
Definition: qgslayoutitemregistry.h:336
Qgs3DMapSettings
Definition: qgs3dmapsettings.h:51
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:81
qgslayout.h
QgsLayoutItem3DMap::refresh
void refresh() override
Definition: qgslayoutitem3dmap.cpp:274
QgsCameraPose
Definition: qgscamerapose.h:44
QgsLayoutItem3DMap::finalizeRestoreFromXml
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
Definition: qgslayoutitem3dmap.cpp:258
QgsLayoutItem::refresh
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
Definition: qgslayoutitem.cpp:1172
Qgs3DMapScene
Definition: qgs3dmapscene.h:60
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:87
QgsLayoutItem3DMap::draw
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
Definition: qgslayoutitem3dmap.cpp:96
QgsLayoutObject::mLayout
QPointer< QgsLayout > mLayout
Definition: qgslayoutobject.h:335
QgsLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
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:38
QgsLayoutItem3DMap::readPropertiesFromElement
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
Definition: qgslayoutitem3dmap.cpp:223
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:281
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:174
QgsLayoutItem3DMap::assignFreeId
void assignFreeId()
Sets the map id() to a number not yet used in the layout.
Definition: qgslayoutitem3dmap.cpp:53
QgsLayoutItem3DMap::icon
QIcon icon() const override
Returns the item's icon.
Definition: qgslayoutitem3dmap.cpp:48
qgslayoutitemregistry.h
QgsOffscreen3DEngine
Definition: qgsoffscreen3dengine.h:64
qgslayoutmodel.h
QgsLayoutItem3DMap::~QgsLayoutItem3DMap
~QgsLayoutItem3DMap()
qgslayoutitem3dmap.h
qgs3dmapscene.h