QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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"
26
28 : QgsLayoutItem( layout )
29{
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 : std::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
91void 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 if ( mSettings->rendererUsage() != Qgis::RendererUsage::View )
134 {
135 mSettings->setRendererUsage( Qgis::RendererUsage::View );
136 mEngine.reset(); //we need to rebuild the scene to force the render again
137 }
138 }
139 else
140 {
141 if ( mSettings->rendererUsage() != Qgis::RendererUsage::Export )
142 {
143 mSettings->setRendererUsage( Qgis::RendererUsage::Export );
144 mEngine.reset(); //we need to rebuild the scene to force the render again
145 }
146 }
147
148 QSizeF sizePixels = mLayout->renderContext().measurementConverter().convert( sizeWithUnits(), Qgis::LayoutUnit::Pixels ).toQSizeF();
149 QSize sizePixelsInt = QSize( static_cast<int>( std::ceil( sizePixels.width() ) ),
150 static_cast<int>( std::ceil( sizePixels.height() ) ) );
151
152 if ( isTemporal() )
153 mSettings->setTemporalRange( temporalRange() );
154
155 if ( !mEngine )
156 {
157 mEngine.reset( new QgsOffscreen3DEngine );
158 connect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
159
160 mEngine->setSize( sizePixelsInt );
161 mScene = new Qgs3DMapScene( *mSettings, mEngine.get() );
162 connect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
163
164 mEngine->setRootEntity( mScene );
165
166 }
167
168 if ( mEngine->size() != sizePixelsInt )
169 mEngine->setSize( sizePixelsInt );
170
171 mScene->cameraController()->setCameraPose( mCameraPose );
172
173 if ( mLayout->renderContext().isPreviewRender() )
174 {
175 onSceneStateChanged();
176 }
177 else
178 {
179 // we can't just request a capture and hope it will arrive at some point later.
180 // this is not a preview, we need the rendered scene now!
181 if ( mDrawing )
182 return;
183 mDrawing = true;
184 disconnect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
185 disconnect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
186
187 Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
188 QImage img = Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
189 painter->drawImage( r, img );
190 painter->restore();
191
192 connect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
193 connect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
194 mDrawing = false;
195 }
196}
197
198void QgsLayoutItem3DMap::onImageCaptured( const QImage &img )
199{
200 mCapturedImage = img;
201 update();
202}
203
204void QgsLayoutItem3DMap::onSceneStateChanged()
205{
206 if ( mCapturedImage.isNull() && mScene->sceneState() == Qgs3DMapScene::Ready )
207 {
208 mEngine->requestCaptureImage();
209 }
210}
211
212void QgsLayoutItem3DMap::onSizePositionChanged()
213{
214 mCapturedImage = QImage();
215 update();
216}
217
218
219bool QgsLayoutItem3DMap::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
220{
221 if ( mSettings )
222 {
223 QDomElement elemSettings = mSettings->writeXml( document, context );
224 element.appendChild( elemSettings );
225 }
226
227 QDomElement elemCameraPose = mCameraPose.writeXml( document );
228 element.appendChild( elemCameraPose );
229
230 //temporal settings
231 QDomElement elemTemporal = document.createElement( QStringLiteral( "temporal-settings" ) );
232 elemTemporal.setAttribute( QStringLiteral( "isTemporal" ), isTemporal() ? 1 : 0 );
233 if ( isTemporal() )
234 {
235 elemTemporal.setAttribute( QStringLiteral( "temporalRangeBegin" ), temporalRange().begin().toString( Qt::ISODate ) );
236 elemTemporal.setAttribute( QStringLiteral( "temporalRangeEnd" ), temporalRange().end().toString( Qt::ISODate ) );
237 }
238 element.appendChild( elemTemporal );
239
240 return true;
241}
242
243bool QgsLayoutItem3DMap::readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context )
244{
245 Q_UNUSED( document )
246 QDomElement elemSettings = element.firstChildElement( QStringLiteral( "qgis3d" ) );
247 if ( !elemSettings.isNull() )
248 {
249 mSettings.reset( new Qgs3DMapSettings );
250 mSettings->readXml( elemSettings, context );
251 if ( mLayout->project() )
252 {
253 mSettings->resolveReferences( *mLayout->project() );
254
255 mSettings->setTransformContext( mLayout->project()->transformContext() );
256 mSettings->setPathResolver( mLayout->project()->pathResolver() );
257 mSettings->setMapThemeCollection( mLayout->project()->mapThemeCollection() );
258 }
259 }
260
261 QDomElement elemCameraPose = element.firstChildElement( QStringLiteral( "camera-pose" ) );
262 if ( !elemCameraPose.isNull() )
263 mCameraPose.readXml( elemCameraPose );
264
265 //temporal settings
266 QDomElement elemTemporal = element.firstChildElement( QStringLiteral( "temporal-settings" ) );
267 setIsTemporal( elemTemporal.attribute( QStringLiteral( "isTemporal" ) ).toInt() );
268 if ( isTemporal() )
269 {
270 QDateTime begin = QDateTime::fromString( elemTemporal.attribute( QStringLiteral( "temporalRangeBegin" ) ), Qt::ISODate );
271 QDateTime end = QDateTime::fromString( elemTemporal.attribute( QStringLiteral( "temporalRangeBegin" ) ), Qt::ISODate );
272 setTemporalRange( QgsDateTimeRange( begin, end ) );
273 }
274
275 return true;
276}
277
279{
280 assignFreeId();
281}
282
284{
285 mSettings.reset( settings );
286
287 mEngine.reset();
288 mScene = nullptr;
289
290 mCapturedImage = QImage();
291 update();
292}
293
295{
297
298 mCapturedImage = QImage();
299}
300
302{
303 if ( mCameraPose == pose )
304 return;
305
306 mCameraPose = pose;
307 mCapturedImage = QImage();
308 update();
309}
@ Export
Renderer used for printing or exporting to a file.
@ View
Renderer used for displaying on screen.
QgsCameraController * cameraController() const
Returns camera controller.
Definition: qgs3dmapscene.h:81
@ 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.
static QImage captureSceneImage(QgsAbstract3DEngine &engine, Qgs3DMapScene *scene)
Captures image of the current 3D scene of a 3D engine.
Definition: qgs3dutils.cpp:62
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:43
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:70
Base class for graphical items within a QgsLayout.
QColor backgroundColor(bool useDataDefined=true) const
Returns the background color for this item.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
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:49
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.
QgsTemporalRange< QDateTime > QgsDateTimeRange
QgsRange which stores a range of date times.
Definition: qgsrange.h:742