QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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"
27#include "qgswindow3dengine.h"
28
30 : QgsLayoutItem( layout )
31{
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
93void 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
200void QgsLayoutItem3DMap::onImageCaptured( const QImage &img )
201{
202 mCapturedImage = img;
203 update();
204}
205
206void QgsLayoutItem3DMap::onSceneStateChanged()
207{
208 if ( mCapturedImage.isNull() && mScene->sceneState() == Qgs3DMapScene::Ready )
209 {
210 mEngine->requestCaptureImage();
211 }
212}
213
214void QgsLayoutItem3DMap::onSizePositionChanged()
215{
216 mCapturedImage = QImage();
217 update();
218}
219
220
221bool 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
245bool 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.
void sceneStateChanged()
Emitted when the scene's state has changed.
SceneState sceneState() const
Returns the current state of the scene.
QgsCameraController * cameraController()
Returns camera controller.
Definition: qgs3dmapscene.h:78
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