QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgsquickmapcanvasmap.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsquickmapcanvasmap.cpp
3 --------------------------------------
4 Date : 10.12.2014
5 Copyright : (C) 2014 by Matthias Kuhn
6 Email : matthias (at) opengis.ch
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
17
18#include "qgis.h"
19#include "qgsannotationlayer.h"
21#include "qgsgrouplayer.h"
22#include "qgslabelingresults.h"
25#include "qgsmaprenderercache.h"
27#include "qgsmessagelog.h"
28#include "qgspallabeling.h"
29#include "qgsproject.h"
30#include "qgsquickmapsettings.h"
31#include "qgssymbollayerutils.h"
32#include "qgsvectorlayer.h"
33
34#include <QQuickWindow>
35#include <QSGSimpleTextureNode>
36#include <QScreen>
37#include <QString>
38
39#include "moc_qgsquickmapcanvasmap.cpp"
40
41using namespace Qt::StringLiterals;
42
44 : QQuickItem( parent )
45 , mMapSettings( std::make_unique<QgsQuickMapSettings>() )
46 , mCache( std::make_unique<QgsMapRendererCache>() )
47{
48 connect( this, &QQuickItem::windowChanged, this, &QgsQuickMapCanvasMap::onWindowChanged );
49 connect( &mRefreshTimer, &QTimer::timeout, this, [this] { refreshMap(); } );
50 connect( &mMapUpdateTimer, &QTimer::timeout, this, &QgsQuickMapCanvasMap::renderJobUpdated );
51
52 connect( mMapSettings.get(), &QgsQuickMapSettings::extentChanged, this, &QgsQuickMapCanvasMap::onExtentChanged );
53 connect( mMapSettings.get(), &QgsQuickMapSettings::layersChanged, this, &QgsQuickMapCanvasMap::onLayersChanged );
54 connect( mMapSettings.get(), &QgsQuickMapSettings::temporalStateChanged, this, &QgsQuickMapCanvasMap::onTemporalStateChanged );
55 connect( mMapSettings.get(), &QgsQuickMapSettings::zRangeChanged, this, &QgsQuickMapCanvasMap::onzRangeChanged );
56
59
60 mMapUpdateTimer.setSingleShot( false );
61 mMapUpdateTimer.setInterval( 250 );
62 mRefreshTimer.setSingleShot( true );
63 setTransformOrigin( QQuickItem::TopLeft );
64 setFlags( QQuickItem::ItemHasContents );
65}
66
68
70{
71 return mMapSettings.get();
72}
73
74void QgsQuickMapCanvasMap::zoom( QPointF center, qreal scale )
75{
76 QgsRectangle extent = mMapSettings->extent();
77 QgsPoint oldCenter( extent.center() );
78 QgsPoint mousePos( mMapSettings->screenToCoordinate( center ) );
79
80 QgsPointXY newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * scale ), mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * scale ) );
81
82 // same as zoomWithCenter (no coordinate transformations are needed)
83 extent.scale( scale, &newCenter );
84 mMapSettings->setExtent( extent );
85}
86
87void QgsQuickMapCanvasMap::pan( QPointF oldPos, QPointF newPos )
88{
89 QgsPoint start = mMapSettings->screenToCoordinate( oldPos.toPoint() );
90 QgsPoint end = mMapSettings->screenToCoordinate( newPos.toPoint() );
91
92 double dx = end.x() - start.x();
93 double dy = end.y() - start.y();
94
95 // modify the extent
96 QgsRectangle extent = mMapSettings->extent();
97
98 extent.setXMinimum( extent.xMinimum() + dx );
99 extent.setXMaximum( extent.xMaximum() + dx );
100 extent.setYMaximum( extent.yMaximum() + dy );
101 extent.setYMinimum( extent.yMinimum() + dy );
102
103 mMapSettings->setExtent( extent );
104}
105
106void QgsQuickMapCanvasMap::refreshMap()
107{
108 stopRendering(); // if any...
109
110 if ( mCacheInvalidations.testFlag( CacheInvalidationType::Temporal ) )
111 {
112 clearTemporalCache();
113 mCacheInvalidations &= ~( static_cast<int>( CacheInvalidationType::Temporal ) );
114 }
115 if ( mCacheInvalidations.testFlag( CacheInvalidationType::Elevation ) )
116 {
117 clearElevationCache();
118 mCacheInvalidations &= ~( static_cast<int>( CacheInvalidationType::Elevation ) );
119 }
120
121 QgsMapSettings mapSettings = mMapSettings->mapSettings();
122 if ( !mapSettings.hasValidSettings() )
123 return;
124
125 //build the expression context
126 QgsExpressionContext expressionContext;
127 expressionContext << QgsExpressionContextUtils::globalScope()
129
130 QgsProject *project = mMapSettings->project();
131 if ( project )
132 {
133 expressionContext << QgsExpressionContextUtils::projectScope( project );
134
135 mapSettings.setLabelingEngineSettings( project->labelingEngineSettings() );
136
137 // render main annotation layer above all other layers
138 QList<QgsMapLayer *> allLayers = mapSettings.layers();
139 allLayers.insert( 0, project->mainAnnotationLayer() );
140 mapSettings.setLayers( allLayers );
141 }
142
143 mapSettings.setExpressionContext( expressionContext );
144
145 // enables on-the-fly simplification of geometries to spend less time rendering
147 // with incremental rendering - enables updates of partially rendered layers (good for WMTS, XYZ layers)
148 mapSettings.setFlag( Qgis::MapSettingsFlag::RenderPartialOutput, mIncrementalRendering );
149
150 // create the renderer job
151 Q_ASSERT( !mJob );
152 mJob = new QgsMapRendererParallelJob( mapSettings );
153
154 if ( mIncrementalRendering )
155 mMapUpdateTimer.start();
156
157 connect( mJob, &QgsMapRendererJob::renderingLayersFinished, this, &QgsQuickMapCanvasMap::renderJobUpdated );
158 connect( mJob, &QgsMapRendererJob::finished, this, &QgsQuickMapCanvasMap::renderJobFinished );
159 mJob->setCache( mCache.get() );
160
161 mJob->start();
162
163 if ( !mSilentRefresh )
164 {
165 emit renderStarting();
166 }
167}
168
169void QgsQuickMapCanvasMap::renderJobUpdated()
170{
171 if ( !mJob )
172 return;
173
174 mImage = mJob->renderedImage();
175 mImageMapSettings = mJob->mapSettings();
176 mDirty = true;
177 // Temporarily freeze the canvas, we only need to reset the geometry but not trigger a repaint
178 bool freeze = mFreeze;
179 mFreeze = true;
180 updateTransform();
181 mFreeze = freeze;
182
183 update();
184}
185
186void QgsQuickMapCanvasMap::renderJobFinished()
187{
188 if ( !mJob )
189 return;
190
191 const QgsMapRendererJob::Errors errors = mJob->errors();
192 for ( const QgsMapRendererJob::Error &error : errors )
193 {
194 QgsMessageLog::logMessage( u"%1 :: %2"_s.arg( error.layerID, error.message ), tr( "Rendering" ) );
195 }
196
197 // take labeling results before emitting renderComplete, so labeling map tools
198 // connected to signal work with correct results
199 delete mLabelingResults;
200 mLabelingResults = mJob->takeLabelingResults();
201
202 mImage = mJob->renderedImage();
203 mImageMapSettings = mJob->mapSettings();
204
205 // now we are in a slot called from mJob - do not delete it immediately
206 // so the class is still valid when the execution returns to the class
207 mJob->deleteLater();
208 mJob = nullptr;
209 mDirty = true;
210 mMapUpdateTimer.stop();
211
212 // Temporarily freeze the canvas, we only need to reset the geometry but not trigger a repaint
213 bool freeze = mFreeze;
214 mFreeze = true;
215 updateTransform();
216 mFreeze = freeze;
217
218 update();
219 if ( !mSilentRefresh )
220 {
221 emit mapCanvasRefreshed();
222 }
223 else
224 {
225 mSilentRefresh = false;
226 }
227
228 if ( mDeferredRefreshPending )
229 {
230 mDeferredRefreshPending = false;
231 mSilentRefresh = true;
232 refresh();
233 }
234}
235
236void QgsQuickMapCanvasMap::layerRepaintRequested( bool deferred )
237{
238 if ( mMapSettings->outputSize().isNull() )
239 return; // the map image size has not been set yet
240
241 if ( !mFreeze )
242 {
243 if ( deferred )
244 {
245 if ( !mJob )
246 {
247 mSilentRefresh = true;
248 refresh();
249 }
250 else
251 {
252 mDeferredRefreshPending = true;
253 }
254 }
255 else
256 {
257 refresh();
258 }
259 }
260}
261
262void QgsQuickMapCanvasMap::onWindowChanged( QQuickWindow *window )
263{
264 if ( mWindow == window )
265 return;
266
267 if ( mWindow )
268 disconnect( mWindow, &QQuickWindow::screenChanged, this, &QgsQuickMapCanvasMap::onScreenChanged );
269
270 if ( window )
271 {
272 connect( window, &QQuickWindow::screenChanged, this, &QgsQuickMapCanvasMap::onScreenChanged );
273 onScreenChanged( window->screen() );
274 }
275
276 mWindow = window;
277}
278
279void QgsQuickMapCanvasMap::onScreenChanged( QScreen *screen )
280{
281 if ( screen )
282 {
283 if ( screen->devicePixelRatio() > 0 )
284 {
285 mMapSettings->setDevicePixelRatio( screen->devicePixelRatio() );
286 }
287 mMapSettings->setOutputDpi( screen->physicalDotsPerInch() );
288 }
289}
290
291void QgsQuickMapCanvasMap::onExtentChanged()
292{
293 updateTransform();
294
295 // And trigger a new rendering job
296 refresh();
297}
298
299void QgsQuickMapCanvasMap::onTemporalStateChanged()
300{
301 mCacheInvalidations |= CacheInvalidationType::Temporal;
302
303 // And trigger a new rendering job
304 refresh();
305}
306
307void QgsQuickMapCanvasMap::onzRangeChanged()
308{
309 mCacheInvalidations |= CacheInvalidationType::Elevation;
310
311 // And trigger a new rendering job
312 refresh();
313}
314
315void QgsQuickMapCanvasMap::updateTransform()
316{
317 QgsRectangle imageExtent = mImageMapSettings.visibleExtent();
318 QgsRectangle newExtent = mMapSettings->mapSettings().visibleExtent();
319 setScale( imageExtent.width() / newExtent.width() );
320
321 QgsPointXY pixelPt = mMapSettings->coordinateToScreen( QgsPoint( imageExtent.xMinimum(), imageExtent.yMaximum() ) );
322 setX( pixelPt.x() );
323 setY( pixelPt.y() );
324}
325
327{
328 return mMapUpdateTimer.interval();
329}
330
332{
333 if ( mMapUpdateTimer.interval() == mapUpdateInterval )
334 return;
335
336 mMapUpdateTimer.setInterval( mapUpdateInterval );
337
339}
340
342{
343 return mIncrementalRendering;
344}
345
347{
348 if ( incrementalRendering == mIncrementalRendering )
349 return;
350
351 mIncrementalRendering = incrementalRendering;
353}
354
356{
357 return mFreeze;
358}
359
361{
362 if ( freeze == mFreeze )
363 return;
364
365 mFreeze = freeze;
366
367 if ( mFreeze )
369 else
370 refresh();
371
372 emit freezeChanged();
373}
374
376{
377 return mJob;
378}
379
380QSGNode *QgsQuickMapCanvasMap::updatePaintNode( QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * )
381{
382 if ( mDirty )
383 {
384 delete oldNode;
385 oldNode = nullptr;
386 mDirty = false;
387 }
388
389 if ( mImage.isNull() )
390 {
391 return nullptr;
392 }
393
394 QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>( oldNode );
395 if ( !node )
396 {
397 node = new QSGSimpleTextureNode();
398 QSGTexture *texture = window()->createTextureFromImage( mImage );
399 node->setTexture( texture );
400 node->setOwnsTexture( true );
401 }
402
403 QRectF rect( boundingRect() );
404 QSizeF size = mImage.size();
405 if ( !size.isEmpty() )
406 size /= mMapSettings->devicePixelRatio();
407
408 // Check for resizes that change the w/h ratio
409 if ( !rect.isEmpty() && !size.isEmpty() && !qgsDoubleNear( rect.width() / rect.height(), ( size.width() ) / static_cast<double>( size.height() ), 3 ) )
410 {
411 if ( qgsDoubleNear( rect.height(), mImage.height() ) )
412 {
413 rect.setHeight( rect.width() / size.width() * size.height() );
414 }
415 else
416 {
417 rect.setWidth( rect.height() / size.height() * size.width() );
418 }
419 }
420
421 node->setRect( rect );
422
423 return node;
424}
425
426void QgsQuickMapCanvasMap::geometryChange( const QRectF &newGeometry, const QRectF &oldGeometry )
427{
428 QQuickItem::geometryChange( newGeometry, oldGeometry );
429 if ( newGeometry.size() != oldGeometry.size() )
430 {
431 mMapSettings->setOutputSize( newGeometry.size().toSize() );
432 refresh();
433 }
434}
435
436void QgsQuickMapCanvasMap::onLayersChanged()
437{
438 if ( mMapSettings->extent().isEmpty() )
439 zoomToFullExtent();
440
441 for ( const QMetaObject::Connection &conn : std::as_const( mLayerConnections ) )
442 {
443 disconnect( conn );
444 }
445 mLayerConnections.clear();
446
447 const QList<QgsMapLayer *> layers = mMapSettings->layers();
448 for ( QgsMapLayer *layer : layers )
449 {
450 mLayerConnections << connect( layer, &QgsMapLayer::repaintRequested, this, &QgsQuickMapCanvasMap::layerRepaintRequested );
451 }
452
453 refresh();
454}
455
456void QgsQuickMapCanvasMap::destroyJob( QgsMapRendererJob *job )
457{
458 job->cancel();
459 job->deleteLater();
460}
461
463{
464 if ( mJob )
465 {
466 disconnect( mJob, &QgsMapRendererJob::renderingLayersFinished, this, &QgsQuickMapCanvasMap::renderJobUpdated );
467 disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsQuickMapCanvasMap::renderJobFinished );
468
469 mJob->cancelWithoutBlocking();
470 mJob = nullptr;
471 }
472}
473
474void QgsQuickMapCanvasMap::zoomToFullExtent()
475{
476 QgsRectangle extent;
477 const QList<QgsMapLayer *> layers = mMapSettings->layers();
478 for ( QgsMapLayer *layer : layers )
479 {
480 if ( mMapSettings->destinationCrs() != layer->crs() )
481 {
482 QgsCoordinateTransform transform( layer->crs(), mMapSettings->destinationCrs(), mMapSettings->transformContext() );
483 try
484 {
485 extent.combineExtentWith( transform.transformBoundingBox( layer->extent() ) );
486 }
487 catch ( const QgsCsException &exp )
488 {
489 // Ignore extent if it can't be transformed
490 }
491 }
492 else
493 {
494 extent.combineExtentWith( layer->extent() );
495 }
496 }
497 mMapSettings->setExtent( extent );
498
499 refresh();
500}
501
503{
504 if ( mMapSettings->outputSize().isNull() )
505 return; // the map image size has not been set yet
506
507 if ( !mFreeze )
508 mRefreshTimer.start( 1 );
509}
510
512{
513 if ( mCache )
514 mCache->clear();
515}
516
517void QgsQuickMapCanvasMap::clearTemporalCache()
518{
519 if ( mCache )
520 {
521 bool invalidateLabels = false;
522 const QList<QgsMapLayer *> layerList = mMapSettings->mapSettings().layers();
523 for ( QgsMapLayer *layer : layerList )
524 {
525 bool alreadyInvalidatedThisLayer = false;
526 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
527 {
528 if ( vl->renderer() && QgsSymbolLayerUtils::rendererFrameRate( vl->renderer() ) > -1 )
529 {
530 // layer has an animated symbol assigned, so we have to redraw it regardless of whether
531 // or not it has temporal settings
532 mCache->invalidateCacheForLayer( layer );
533 alreadyInvalidatedThisLayer = true;
534 // we can't shortcut and "continue" here, as we still need to check whether the layer
535 // will cause label invalidation using the logic below
536 }
537 }
538
539 if ( layer->temporalProperties() && layer->temporalProperties()->isActive() )
540 {
541 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
542 {
543 if ( vl->labelsEnabled() || vl->diagramsEnabled() )
544 invalidateLabels = true;
545 }
546
547 if ( layer->temporalProperties()->flags() & QgsTemporalProperty::FlagDontInvalidateCachedRendersWhenRangeChanges )
548 continue;
549
550 if ( !alreadyInvalidatedThisLayer )
551 mCache->invalidateCacheForLayer( layer );
552 }
553 else if ( QgsGroupLayer *gl = qobject_cast<QgsGroupLayer *>( layer ) )
554 {
555 const QList<QgsMapLayer *> childLayerList = gl->childLayers();
556 for ( QgsMapLayer *childLayer : childLayerList )
557 {
558 if ( childLayer->temporalProperties() && childLayer->temporalProperties()->isActive() )
559 {
560 if ( childLayer->temporalProperties()->flags() & QgsTemporalProperty::FlagDontInvalidateCachedRendersWhenRangeChanges )
561 continue;
562
563 mCache->invalidateCacheForLayer( layer );
564 break;
565 }
566 }
567 }
568 }
569
570 if ( invalidateLabels )
571 {
572 mCache->clearCacheImage( u"_labels_"_s );
573 mCache->clearCacheImage( u"_preview_labels_"_s );
574 }
575 }
576}
577
578void QgsQuickMapCanvasMap::clearElevationCache()
579{
580 if ( mCache )
581 {
582 bool invalidateLabels = false;
583 const QList<QgsMapLayer *> layerList = mMapSettings->mapSettings().layers();
584 for ( QgsMapLayer *layer : layerList )
585 {
586 if ( layer->elevationProperties() && layer->elevationProperties()->hasElevation() )
587 {
588 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
589 {
590 if ( vl->labelsEnabled() || vl->diagramsEnabled() )
591 invalidateLabels = true;
592 }
593
595 continue;
596
597 mCache->invalidateCacheForLayer( layer );
598 }
599 else if ( QgsGroupLayer *gl = qobject_cast<QgsGroupLayer *>( layer ) )
600 {
601 const QList<QgsMapLayer *> childLayerList = gl->childLayers();
602 for ( QgsMapLayer *childLayer : childLayerList )
603 {
604 if ( childLayer->elevationProperties() && childLayer->elevationProperties()->hasElevation() )
605 {
606 if ( childLayer->elevationProperties()->flags() & QgsMapLayerElevationProperties::FlagDontInvalidateCachedRendersWhenRangeChanges )
607 continue;
608
609 mCache->invalidateCacheForLayer( layer );
610 break;
611 }
612 }
613 }
614 }
615
616 if ( invalidateLabels )
617 {
618 mCache->clearCacheImage( u"_labels_"_s );
619 mCache->clearCacheImage( u"_preview_labels_"_s );
620 }
621 }
622}
@ UseRenderingOptimization
Enable vector simplification and other rendering optimizations.
Definition qgis.h:2780
@ RenderPartialOutput
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
Definition qgis.h:2784
Handles coordinate transforms between two coordinate systems.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
@ FlagDontInvalidateCachedRendersWhenRangeChanges
Any cached rendering will not be invalidated when z range context is modified.
Base class for all map layer types.
Definition qgsmaplayer.h:83
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
Responsible for keeping a cache of rendered images resulting from a map rendering job.
Abstract base class for map rendering implementations.
void renderingLayersFinished()
Emitted when the layers are rendered.
void finished()
emitted when asynchronous rendering is finished (or canceled).
QList< QgsMapRendererJob::Error > Errors
virtual void cancel()=0
Stop the rendering job - does not return until the job has terminated.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double x
Definition qgspoint.h:56
double y
Definition qgspoint.h:57
QgsAnnotationLayer * mainAnnotationLayer()
Returns the main annotation layer associated with the project.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project's global labeling engine settings.
void freezeChanged()
When freeze property is set to true, the map canvas does not refresh.
bool isRendering
The isRendering property is set to true while a rendering job is pending for this map canvas map.
void mapCanvasRefreshed()
Signal is emitted when a canvas is refreshed.
void incrementalRenderingChanged()
When the incrementalRendering property is set to true, the automatic refresh of map canvas during ren...
int mapUpdateInterval
Interval in milliseconds after which the map canvas will be updated while a rendering job is ongoing.
void setMapUpdateInterval(int mapUpdateInterval)
Interval in milliseconds after which the map canvas will be updated while a rendering job is ongoing.
void pan(QPointF oldPos, QPointF newPos)
Set map setting's extent (pan the map) based on the difference of positions.
void renderStarting()
Signal is emitted when a rendering is starting.
void stopRendering()
Stop map rendering.
~QgsQuickMapCanvasMap() override
void zoom(QPointF center, qreal scale)
Set map setting's extent (zoom the map) on the center by given scale.
void setIncrementalRendering(bool incrementalRendering)
When the incrementalRendering property is set to true, the automatic refresh of map canvas during ren...
void clearCache()
Clears rendering cache.
void setFreeze(bool freeze)
When freeze property is set to true, the map canvas does not refresh.
QgsQuickMapSettings * mapSettings
The mapSettings property contains configuration for rendering of the map.
void refresh()
Refresh the map canvas.
QSGNode * updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) override
void mapUpdateIntervalChanged()
Interval in milliseconds after which the map canvas will be updated while a rendering job is ongoing.
bool incrementalRendering
When the incrementalRendering property is set to true, the automatic refresh of map canvas during ren...
bool freeze
When freeze property is set to true, the map canvas does not refresh.
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
QgsQuickMapCanvasMap(QQuickItem *parent=nullptr)
Create map canvas map.
void isRenderingChanged()
The isRendering property is set to true while a rendering job is pending for this map canvas map.
Encapsulates QgsMapSettings class to offer settings of configuration of map rendering via QML propert...
void extentChanged()
Geographical coordinates of the rectangle that should be rendered.
void layersChanged()
Set list of layers for map rendering.
void temporalStateChanged()
Emitted when the temporal state has changed.
void zRangeChanged()
Emitted when the Z range has changed.
A rectangle specified with double values.
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
double xMinimum
double yMinimum
double xMaximum
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
double yMaximum
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
QgsPointXY center
static double rendererFrameRate(const QgsFeatureRenderer *renderer)
Calculates the frame rate (in frames per second) at which the given renderer must be redrawn.
@ FlagDontInvalidateCachedRendersWhenRangeChanges
Any cached rendering will not be invalidated when temporal range context is modified.
Represents a vector layer which manages a vector based dataset.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900