QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
Loading...
Searching...
No Matches
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#include "moc_qgslayoutitem3dmap.cpp"
18
19#include "qgs3dmapscene.h"
20#include "qgs3dutils.h"
21#include "qgscameracontroller.h"
22#include "qgslayout.h"
23#include "qgslayoutmodel.h"
27
29 : QgsLayoutItem( layout )
30{
32
33 connect( this, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutItem3DMap::onSizePositionChanged );
34}
35
37
38
43
48
50{
51 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItem3DMap.svg" ) );
52}
53
55{
56 if ( !mLayout )
57 return;
58
59 QList<QgsLayoutItem3DMap *> mapsList;
60 mLayout->layoutItems( mapsList );
61
62 int maxId = -1;
63 bool used = false;
64 for ( QgsLayoutItem3DMap *map : std::as_const( mapsList ) )
65 {
66 if ( map == this )
67 continue;
68
69 if ( map->mMapId == mMapId )
70 used = true;
71
72 maxId = std::max( maxId, map->mMapId );
73 }
74 if ( used )
75 {
76 mMapId = maxId + 1;
77 mLayout->itemsModel()->updateItemDisplayName( this );
78 }
79 updateToolTip();
80}
81
83{
84 if ( !QgsLayoutItem::id().isEmpty() )
85 {
86 return QgsLayoutItem::id();
87 }
88
89 return tr( "3D Map %1" ).arg( mMapId );
90}
91
92void QgsLayoutItem3DMap::updateToolTip()
93{
94 setToolTip( displayName() );
95}
96
98{
99 QgsRenderContext &ctx = context.renderContext();
100 QPainter *painter = ctx.painter();
101
102 int w = static_cast<int>( std::ceil( rect().width() * ctx.scaleFactor() ) );
103 int h = static_cast<int>( std::ceil( rect().height() * ctx.scaleFactor() ) );
104 QRect r( 0, 0, w, h );
105
106 painter->save();
107
108 if ( !mSettings )
109 {
110 painter->drawText( r, Qt::AlignCenter, tr( "Scene not set" ) );
111 painter->restore();
112 return;
113 }
114
115 if ( mSettings->backgroundColor() != backgroundColor() )
116 {
117 mSettings->setBackgroundColor( backgroundColor() );
118 mCapturedImage = QImage();
119 }
120
121 if ( !mCapturedImage.isNull() )
122 {
123 painter->drawImage( r, mCapturedImage );
124 painter->restore();
125 return;
126 }
127
128 // we do not have a cached image of the rendered scene - let's request one from the engine
129
130 if ( mLayout->renderContext().isPreviewRender() )
131 {
132 painter->drawText( r, Qt::AlignCenter, tr( "Loading" ) );
133 painter->restore();
134 if ( mSettings->rendererUsage() != Qgis::RendererUsage::View )
135 {
136 mSettings->setRendererUsage( Qgis::RendererUsage::View );
137 mEngine.reset(); //we need to rebuild the scene to force the render again
138 }
139 }
140 else
141 {
142 if ( mSettings->rendererUsage() != Qgis::RendererUsage::Export )
143 {
144 mSettings->setRendererUsage( Qgis::RendererUsage::Export );
145 mEngine.reset(); //we need to rebuild the scene to force the render again
146 }
147 }
148
149 QSizeF sizePixels = mLayout->renderContext().measurementConverter().convert( sizeWithUnits(), Qgis::LayoutUnit::Pixels ).toQSizeF();
150 QSize sizePixelsInt = QSize( static_cast<int>( std::ceil( sizePixels.width() ) ),
151 static_cast<int>( std::ceil( sizePixels.height() ) ) );
152
153 if ( isTemporal() )
154 mSettings->setTemporalRange( temporalRange() );
155
156 if ( !mEngine )
157 {
158 mEngine.reset( new QgsOffscreen3DEngine );
159 connect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
160
161 mEngine->setSize( sizePixelsInt );
162 mScene = new Qgs3DMapScene( *mSettings, mEngine.get() );
163 connect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
164
165 mEngine->setRootEntity( mScene );
166
167 }
168
169 if ( mEngine->size() != sizePixelsInt )
170 mEngine->setSize( sizePixelsInt );
171
172 mScene->cameraController()->setCameraPose( mCameraPose );
173
174 if ( mLayout->renderContext().isPreviewRender() )
175 {
176 onSceneStateChanged();
177 }
178 else
179 {
180 // we can't just request a capture and hope it will arrive at some point later.
181 // this is not a preview, we need the rendered scene now!
182 if ( mDrawing )
183 return;
184 mDrawing = true;
185 disconnect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
186 disconnect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
187
188 Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
189 QImage img = Qgs3DUtils::captureSceneImage( *mEngine.get(), mScene );
190 painter->drawImage( r, img );
191 painter->restore();
192
193 connect( mEngine.get(), &QgsAbstract3DEngine::imageCaptured, this, &QgsLayoutItem3DMap::onImageCaptured );
194 connect( mScene, &Qgs3DMapScene::sceneStateChanged, this, &QgsLayoutItem3DMap::onSceneStateChanged );
195 mDrawing = false;
196 }
197}
198
199void QgsLayoutItem3DMap::onImageCaptured( const QImage &img )
200{
201 mCapturedImage = img;
202 update();
203}
204
205void QgsLayoutItem3DMap::onSceneStateChanged()
206{
207 if ( mCapturedImage.isNull() && mScene->sceneState() == Qgs3DMapScene::Ready )
208 {
209 mEngine->requestCaptureImage();
210 }
211}
212
213void QgsLayoutItem3DMap::onSizePositionChanged()
214{
215 mCapturedImage = QImage();
216 update();
217}
218
219
220bool QgsLayoutItem3DMap::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
221{
222 if ( mSettings )
223 {
224 QDomElement elemSettings = mSettings->writeXml( document, context );
225 element.appendChild( elemSettings );
226 }
227
228 QDomElement elemCameraPose = mCameraPose.writeXml( document );
229 element.appendChild( elemCameraPose );
230
231 //temporal settings
232 QDomElement elemTemporal = document.createElement( QStringLiteral( "temporal-settings" ) );
233 elemTemporal.setAttribute( QStringLiteral( "isTemporal" ), isTemporal() ? 1 : 0 );
234 if ( isTemporal() )
235 {
236 elemTemporal.setAttribute( QStringLiteral( "temporalRangeBegin" ), temporalRange().begin().toString( Qt::ISODate ) );
237 elemTemporal.setAttribute( QStringLiteral( "temporalRangeEnd" ), temporalRange().end().toString( Qt::ISODate ) );
238 }
239 element.appendChild( elemTemporal );
240
241 return true;
242}
243
244bool QgsLayoutItem3DMap::readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context )
245{
246 Q_UNUSED( document )
247 QDomElement elemSettings = element.firstChildElement( QStringLiteral( "qgis3d" ) );
248 if ( !elemSettings.isNull() )
249 {
250 mSettings.reset( new Qgs3DMapSettings );
251 mSettings->readXml( elemSettings, context );
252 if ( mLayout->project() )
253 {
254 mSettings->resolveReferences( *mLayout->project() );
255
256 mSettings->setTransformContext( mLayout->project()->transformContext() );
257 mSettings->setPathResolver( mLayout->project()->pathResolver() );
258 mSettings->setMapThemeCollection( mLayout->project()->mapThemeCollection() );
259 }
260 }
261
262 QDomElement elemCameraPose = element.firstChildElement( QStringLiteral( "camera-pose" ) );
263 if ( !elemCameraPose.isNull() )
264 mCameraPose.readXml( elemCameraPose );
265
266 //temporal settings
267 QDomElement elemTemporal = element.firstChildElement( QStringLiteral( "temporal-settings" ) );
268 setIsTemporal( elemTemporal.attribute( QStringLiteral( "isTemporal" ) ).toInt() );
269 if ( isTemporal() )
270 {
271 QDateTime begin = QDateTime::fromString( elemTemporal.attribute( QStringLiteral( "temporalRangeBegin" ) ), Qt::ISODate );
272 QDateTime end = QDateTime::fromString( elemTemporal.attribute( QStringLiteral( "temporalRangeBegin" ) ), Qt::ISODate );
273 setTemporalRange( QgsDateTimeRange( begin, end ) );
274 }
275
276 return true;
277}
278
283
285{
286 mSettings.reset( settings );
287
288 mEngine.reset();
289 mScene = nullptr;
290
291 mCapturedImage = QImage();
292 update();
293}
294
296{
298
299 mCapturedImage = QImage();
300}
301
303{
304 if ( mCameraPose == pose )
305 return;
306
307 mCameraPose = pose;
308 mCapturedImage = QImage();
309 update();
310}
@ Export
Renderer used for printing or exporting to a file.
@ View
Renderer used for displaying on screen.
QgsCameraController * cameraController() const
Returns camera controller.
@ 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.
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.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
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.
T begin() const
Returns the beginning of the range.
Definition qgsrange.h:444
T end() const
Returns the upper bound of the range.
Definition qgsrange.h:451
QgsTemporalRange< QDateTime > QgsDateTimeRange
QgsRange which stores a range of date times.
Definition qgsrange.h:742