QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgslayoutitemmap.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemmap.cpp
3 ---------------------
4 begin : July 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8/***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17#include "qgslayoutitemmap.h"
18#include "qgslayout.h"
21#include "qgslayoututils.h"
22#include "qgslayoutmodel.h"
25#include "qgsannotation.h"
26#include "qgsmapsettingsutils.h"
27#include "qgslayertree.h"
28#include "qgsmaplayerref.h"
31#include "qgsvectorlayer.h"
33#include "qgsapplication.h"
36#include "qgsannotationlayer.h"
38#include "qgsprojoperation.h"
39#include "qgslabelingresults.h"
40#include "qgsvectortileutils.h"
41#include "qgsunittypes.h"
42
43#include <QPainter>
44#include <QStyleOptionGraphicsItem>
45#include <QTimer>
46
48 : QgsLayoutItem( layout )
49 , mAtlasClippingSettings( new QgsLayoutItemMapAtlasClippingSettings( this ) )
50 , mItemClippingSettings( new QgsLayoutItemMapItemClipPathSettings( this ) )
51{
52 mBackgroundUpdateTimer = new QTimer( this );
53 mBackgroundUpdateTimer->setSingleShot( true );
54 connect( mBackgroundUpdateTimer, &QTimer::timeout, this, &QgsLayoutItemMap::recreateCachedImageInBackground );
55
57
58 setCacheMode( QGraphicsItem::NoCache );
59
60 connect( this, &QgsLayoutItem::sizePositionChanged, this, [ = ]
61 {
62 shapeChanged();
63 } );
64
65 mGridStack = std::make_unique< QgsLayoutItemMapGridStack >( this );
66 mOverviewStack = std::make_unique< QgsLayoutItemMapOverviewStack >( this );
67
68 connect( mAtlasClippingSettings, &QgsLayoutItemMapAtlasClippingSettings::changed, this, [ = ]
69 {
70 refresh();
71 } );
72
73 connect( mItemClippingSettings, &QgsLayoutItemMapItemClipPathSettings::changed, this, [ = ]
74 {
75 refresh();
76 } );
77
79 {
82 if ( mCrs != crs )
83 {
84 setCrs( crs );
86 }
87 } );
88
89 if ( layout )
90 connectUpdateSlot();
91}
92
94{
95 if ( mPainterJob )
96 {
97 disconnect( mPainterJob.get(), &QgsMapRendererCustomPainterJob::finished, this, &QgsLayoutItemMap::painterJobFinished );
99 mPainterJob->cancel(); // blocks
100 mPainter->end();
101 }
102}
103
105{
107}
108
110{
111 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemMap.svg" ) );
112}
113
114QgsLayoutItem::Flags QgsLayoutItemMap::itemFlags() const
115{
117}
118
120{
121 if ( !mLayout )
122 return;
123
124 QList<QgsLayoutItemMap *> mapsList;
125 mLayout->layoutItems( mapsList );
126
127 int maxId = -1;
128 bool used = false;
129 for ( QgsLayoutItemMap *map : std::as_const( mapsList ) )
130 {
131 if ( map == this )
132 continue;
133
134 if ( map->mMapId == mMapId )
135 used = true;
136
137 maxId = std::max( maxId, map->mMapId );
138 }
139 if ( used )
140 {
141 mMapId = maxId + 1;
142 mLayout->itemsModel()->updateItemDisplayName( this );
143 }
144 updateToolTip();
145}
146
148{
149 if ( !QgsLayoutItem::id().isEmpty() )
150 {
151 return QgsLayoutItem::id();
152 }
153
154 return tr( "Map %1" ).arg( mMapId );
155}
156
158{
159 return new QgsLayoutItemMap( layout );
160}
161
163{
165
166 mCachedLayerStyleOverridesPresetName.clear();
167
169
170 updateAtlasFeature();
171}
172
174{
175 if ( rect().isEmpty() )
176 return 0;
177
178 QgsScaleCalculator calculator;
179 calculator.setMapUnits( crs().mapUnits() );
180 calculator.setDpi( 25.4 ); //Using mm
181 double widthInMm = mLayout->convertFromLayoutUnits( rect().width(), Qgis::LayoutUnit::Millimeters ).length();
182 return calculator.calculate( extent(), widthInMm );
183}
184
185void QgsLayoutItemMap::setScale( double scaleDenominator, bool forceUpdate )
186{
187 double currentScaleDenominator = scale();
188
189 if ( qgsDoubleNear( scaleDenominator, currentScaleDenominator ) || qgsDoubleNear( scaleDenominator, 0.0 ) )
190 {
191 return;
192 }
193
194 double scaleRatio = scaleDenominator / currentScaleDenominator;
195 mExtent.scale( scaleRatio );
196
197 if ( mAtlasDriven && mAtlasScalingMode == Fixed )
198 {
199 //if map is atlas controlled and set to fixed scaling mode, then scale changes should be treated as permanent
200 //and also apply to the map's original extent (see #9602)
201 //we can't use the scaleRatio calculated earlier, as the scale can vary depending on extent for geographic coordinate systems
202 QgsScaleCalculator calculator;
203 calculator.setMapUnits( crs().mapUnits() );
204 calculator.setDpi( 25.4 ); //QGraphicsView units are mm
205 scaleRatio = scaleDenominator / calculator.calculate( mExtent, rect().width() );
206 mExtent.scale( scaleRatio );
207 }
208
210 if ( forceUpdate )
211 {
212 emit changed();
213 update();
214 }
215 emit extentChanged();
216}
217
219{
220 if ( mExtent == extent )
221 {
222 return;
223 }
224 mExtent = extent;
225
226 //recalculate data defined scale and extents, since that may override extent
227 refreshMapExtents();
228
229 //adjust height
230 QRectF currentRect = rect();
231
232 double newHeight = currentRect.width() * mExtent.height() / mExtent.width();
233
234 attemptSetSceneRect( QRectF( pos().x(), pos().y(), currentRect.width(), newHeight ) );
235 update();
236}
237
239{
240 QgsRectangle newExtent = extent;
241 QgsRectangle currentExtent = mExtent;
242 //Make sure the width/height ratio is the same as the current layout map extent.
243 //This is to keep the map item frame size fixed
244 double currentWidthHeightRatio = 1.0;
245 if ( !currentExtent.isNull() )
246 currentWidthHeightRatio = currentExtent.width() / currentExtent.height();
247 else
248 currentWidthHeightRatio = rect().width() / rect().height();
249 double newWidthHeightRatio = newExtent.width() / newExtent.height();
250
251 if ( currentWidthHeightRatio < newWidthHeightRatio )
252 {
253 //enlarge height of new extent, ensuring the map center stays the same
254 double newHeight = newExtent.width() / currentWidthHeightRatio;
255 double deltaHeight = newHeight - newExtent.height();
256 newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
257 newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
258 }
259 else
260 {
261 //enlarge width of new extent, ensuring the map center stays the same
262 double newWidth = currentWidthHeightRatio * newExtent.height();
263 double deltaWidth = newWidth - newExtent.width();
264 newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
265 newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
266 }
267
268 if ( mExtent == newExtent )
269 {
270 return;
271 }
272 mExtent = newExtent;
273
274 //recalculate data defined scale and extents, since that may override extent
275 refreshMapExtents();
276
278 emit changed();
279 emit extentChanged();
280}
281
283{
284 return mExtent;
285}
286
287QPolygonF QgsLayoutItemMap::calculateVisibleExtentPolygon( bool includeClipping ) const
288{
289 QPolygonF poly;
290 mapPolygon( mExtent, poly );
291
292 if ( includeClipping && mItemClippingSettings->isActive() )
293 {
294 const QgsGeometry geom = mItemClippingSettings->clippedMapExtent();
295 if ( !geom.isEmpty() )
296 {
297 poly = poly.intersected( geom.asQPolygonF() );
298 }
299 }
300
301 return poly;
302}
303
305{
306 return calculateVisibleExtentPolygon( true );
307}
308
310{
311 if ( mCrs.isValid() )
312 return mCrs;
313 else if ( mLayout && mLayout->project() )
314 return mLayout->project()->crs();
316}
317
319{
320 if ( mCrs == crs )
321 return;
322
323 mCrs = crs;
324 emit crsChanged();
325}
326
327QList<QgsMapLayer *> QgsLayoutItemMap::layers() const
328{
329 return _qgis_listRefToRaw( mLayers );
330}
331
332void QgsLayoutItemMap::setLayers( const QList<QgsMapLayer *> &layers )
333{
334 mLayers = _qgis_listRawToRef( layers );
335}
336
337void QgsLayoutItemMap::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
338{
339 if ( overrides == mLayerStyleOverrides )
340 return;
341
342 mLayerStyleOverrides = overrides;
343 emit layerStyleOverridesChanged(); // associated legends may listen to this
344
345}
346
348{
349 mLayerStyleOverrides.clear();
350 for ( const QgsMapLayerRef &layerRef : std::as_const( mLayers ) )
351 {
352 if ( QgsMapLayer *layer = layerRef.get() )
353 {
354 QgsMapLayerStyle style;
355 style.readFromLayer( layer );
356 mLayerStyleOverrides.insert( layer->id(), style.xmlData() );
357 }
358 }
359}
360
362{
363 if ( mFollowVisibilityPreset == follow )
364 return;
365
366 mFollowVisibilityPreset = follow;
367
368 if ( !mFollowVisibilityPresetName.isEmpty() )
369 emit themeChanged( mFollowVisibilityPreset ? mFollowVisibilityPresetName : QString() );
370}
371
373{
374 if ( name == mFollowVisibilityPresetName )
375 return;
376
377 mFollowVisibilityPresetName = name;
378 if ( mFollowVisibilityPreset )
379 emit themeChanged( mFollowVisibilityPresetName );
380}
381
382void QgsLayoutItemMap::moveContent( double dx, double dy )
383{
384 mLastRenderedImageOffsetX -= dx;
385 mLastRenderedImageOffsetY -= dy;
386 if ( !mDrawing )
387 {
388 transformShift( dx, dy );
389 mExtent.setXMinimum( mExtent.xMinimum() + dx );
390 mExtent.setXMaximum( mExtent.xMaximum() + dx );
391 mExtent.setYMinimum( mExtent.yMinimum() + dy );
392 mExtent.setYMaximum( mExtent.yMaximum() + dy );
393
394 //in case data defined extents are set, these override the calculated values
395 refreshMapExtents();
396
398 emit changed();
399 emit extentChanged();
400 }
401}
402
403void QgsLayoutItemMap::zoomContent( double factor, QPointF point )
404{
405 if ( mDrawing )
406 {
407 return;
408 }
409
410 //find out map coordinates of position
411 double mapX = mExtent.xMinimum() + ( point.x() / rect().width() ) * ( mExtent.xMaximum() - mExtent.xMinimum() );
412 double mapY = mExtent.yMinimum() + ( 1 - ( point.y() / rect().height() ) ) * ( mExtent.yMaximum() - mExtent.yMinimum() );
413
414 //find out new center point
415 double centerX = ( mExtent.xMaximum() + mExtent.xMinimum() ) / 2;
416 double centerY = ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2;
417
418 centerX = mapX + ( centerX - mapX ) * ( 1.0 / factor );
419 centerY = mapY + ( centerY - mapY ) * ( 1.0 / factor );
420
421 double newIntervalX, newIntervalY;
422
423 if ( factor > 0 )
424 {
425 newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) / factor;
426 newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) / factor;
427 }
428 else //no need to zoom
429 {
430 return;
431 }
432
433 mExtent.setXMaximum( centerX + newIntervalX / 2 );
434 mExtent.setXMinimum( centerX - newIntervalX / 2 );
435 mExtent.setYMaximum( centerY + newIntervalY / 2 );
436 mExtent.setYMinimum( centerY - newIntervalY / 2 );
437
438 if ( mAtlasDriven && mAtlasScalingMode == Fixed )
439 {
440 //if map is atlas controlled and set to fixed scaling mode, then scale changes should be treated as permanent
441 //and also apply to the map's original extent (see #9602)
442 //we can't use the scaleRatio calculated earlier, as the scale can vary depending on extent for geographic coordinate systems
443 QgsScaleCalculator calculator;
444 calculator.setMapUnits( crs().mapUnits() );
445 calculator.setDpi( 25.4 ); //QGraphicsView units are mm
446 double scaleRatio = scale() / calculator.calculate( mExtent, rect().width() );
447 mExtent.scale( scaleRatio );
448 }
449
450 //recalculate data defined scale and extents, since that may override zoom
451 refreshMapExtents();
452
454 emit changed();
455 emit extentChanged();
456}
457
459{
460 const QList< QgsMapLayer * > layers = layersToRender();
461 for ( QgsMapLayer *layer : layers )
462 {
463 if ( layer->dataProvider() && layer->providerType() == QLatin1String( "wms" ) )
464 {
465 return true;
466 }
467 }
468 return false;
469}
470
472{
474 return true;
475
476 // we MUST force the whole layout to render as a raster if any map item
477 // uses blend modes, and we are not drawing on a solid opaque background
478 // because in this case the map item needs to be rendered as a raster, but
479 // it also needs to interact with items below it
481 return false;
482
483 if ( hasBackground() && qgsDoubleNear( backgroundColor().alphaF(), 1.0 ) )
484 return false;
485
486 return true;
487}
488
490{
492 return true;
493
494 //check easy things first
495
496 //overviews
497 if ( mOverviewStack->containsAdvancedEffects() )
498 {
499 return true;
500 }
501
502 //grids
503 if ( mGridStack->containsAdvancedEffects() )
504 {
505 return true;
506 }
507
510 return ( !QgsMapSettingsUtils::containsAdvancedEffects( ms ).isEmpty() );
511}
512
513void QgsLayoutItemMap::setMapRotation( double rotation )
514{
515 mMapRotation = rotation;
516 mEvaluatedMapRotation = mMapRotation;
518 emit mapRotationChanged( rotation );
519 emit changed();
520}
521
523{
524 return valueType == QgsLayoutObject::EvaluatedValue ? mEvaluatedMapRotation : mMapRotation;
525
526}
527
529{
530 mAtlasDriven = enabled;
531
532 if ( !enabled )
533 {
534 //if not enabling the atlas, we still need to refresh the map extents
535 //so that data defined extents and scale are recalculated
536 refreshMapExtents();
537 }
538}
539
541{
542 if ( valueType == QgsLayoutObject::EvaluatedValue )
543 {
544 //evaluate data defined atlas margin
545
546 //start with user specified margin
547 double margin = mAtlasMargin;
549
550 bool ok = false;
551 double ddMargin = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapAtlasMargin, context, 0.0, &ok );
552 if ( ok )
553 {
554 //divide by 100 to convert to 0 -> 1.0 range
555 margin = ddMargin / 100;
556 }
557 return margin;
558 }
559 else
560 {
561 return mAtlasMargin;
562 }
563}
564
566{
567 if ( mGridStack->size() < 1 )
568 {
569 QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( tr( "Grid %1" ).arg( 1 ), this );
570 mGridStack->addGrid( grid );
571 }
572 return mGridStack->grid( 0 );
573}
574
576{
577 if ( mOverviewStack->size() < 1 )
578 {
579 QgsLayoutItemMapOverview *overview = new QgsLayoutItemMapOverview( tr( "Overview %1" ).arg( 1 ), this );
580 mOverviewStack->addOverview( overview );
581 }
582 return mOverviewStack->overview( 0 );
583}
584
586{
587}
588
589bool QgsLayoutItemMap::writePropertiesToElement( QDomElement &mapElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
590{
591 if ( mKeepLayerSet )
592 {
593 mapElem.setAttribute( QStringLiteral( "keepLayerSet" ), QStringLiteral( "true" ) );
594 }
595 else
596 {
597 mapElem.setAttribute( QStringLiteral( "keepLayerSet" ), QStringLiteral( "false" ) );
598 }
599
600 if ( mDrawAnnotations )
601 {
602 mapElem.setAttribute( QStringLiteral( "drawCanvasItems" ), QStringLiteral( "true" ) );
603 }
604 else
605 {
606 mapElem.setAttribute( QStringLiteral( "drawCanvasItems" ), QStringLiteral( "false" ) );
607 }
608
609 //extent
610 QDomElement extentElem = doc.createElement( QStringLiteral( "Extent" ) );
611 extentElem.setAttribute( QStringLiteral( "xmin" ), qgsDoubleToString( mExtent.xMinimum() ) );
612 extentElem.setAttribute( QStringLiteral( "xmax" ), qgsDoubleToString( mExtent.xMaximum() ) );
613 extentElem.setAttribute( QStringLiteral( "ymin" ), qgsDoubleToString( mExtent.yMinimum() ) );
614 extentElem.setAttribute( QStringLiteral( "ymax" ), qgsDoubleToString( mExtent.yMaximum() ) );
615 mapElem.appendChild( extentElem );
616
617 if ( mCrs.isValid() )
618 {
619 QDomElement crsElem = doc.createElement( QStringLiteral( "crs" ) );
620 mCrs.writeXml( crsElem, doc );
621 mapElem.appendChild( crsElem );
622 }
623
624 // follow map theme
625 mapElem.setAttribute( QStringLiteral( "followPreset" ), mFollowVisibilityPreset ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
626 mapElem.setAttribute( QStringLiteral( "followPresetName" ), mFollowVisibilityPresetName );
627
628 //map rotation
629 mapElem.setAttribute( QStringLiteral( "mapRotation" ), QString::number( mMapRotation ) );
630
631 //layer set
632 QDomElement layerSetElem = doc.createElement( QStringLiteral( "LayerSet" ) );
633 for ( const QgsMapLayerRef &layerRef : mLayers )
634 {
635 if ( !layerRef )
636 continue;
637 QDomElement layerElem = doc.createElement( QStringLiteral( "Layer" ) );
638 QDomText layerIdText = doc.createTextNode( layerRef.layerId );
639 layerElem.appendChild( layerIdText );
640
641 layerElem.setAttribute( QStringLiteral( "name" ), layerRef.name );
642 layerElem.setAttribute( QStringLiteral( "source" ), layerRef.source );
643 layerElem.setAttribute( QStringLiteral( "provider" ), layerRef.provider );
644
645 layerSetElem.appendChild( layerElem );
646 }
647 mapElem.appendChild( layerSetElem );
648
649 // override styles
650 if ( mKeepLayerStyles )
651 {
652 QDomElement stylesElem = doc.createElement( QStringLiteral( "LayerStyles" ) );
653 for ( auto styleIt = mLayerStyleOverrides.constBegin(); styleIt != mLayerStyleOverrides.constEnd(); ++styleIt )
654 {
655 QDomElement styleElem = doc.createElement( QStringLiteral( "LayerStyle" ) );
656
657 QgsMapLayerRef ref( styleIt.key() );
658 ref.resolve( mLayout->project() );
659
660 styleElem.setAttribute( QStringLiteral( "layerid" ), ref.layerId );
661 styleElem.setAttribute( QStringLiteral( "name" ), ref.name );
662 styleElem.setAttribute( QStringLiteral( "source" ), ref.source );
663 styleElem.setAttribute( QStringLiteral( "provider" ), ref.provider );
664
665 QgsMapLayerStyle style( styleIt.value() );
666 style.writeXml( styleElem );
667 stylesElem.appendChild( styleElem );
668 }
669 mapElem.appendChild( stylesElem );
670 }
671
672 //grids
673 mGridStack->writeXml( mapElem, doc, context );
674
675 //overviews
676 mOverviewStack->writeXml( mapElem, doc, context );
677
678 //atlas
679 QDomElement atlasElem = doc.createElement( QStringLiteral( "AtlasMap" ) );
680 atlasElem.setAttribute( QStringLiteral( "atlasDriven" ), mAtlasDriven );
681 atlasElem.setAttribute( QStringLiteral( "scalingMode" ), mAtlasScalingMode );
682 atlasElem.setAttribute( QStringLiteral( "margin" ), qgsDoubleToString( mAtlasMargin ) );
683 mapElem.appendChild( atlasElem );
684
685 mapElem.setAttribute( QStringLiteral( "labelMargin" ), mLabelMargin.encodeMeasurement() );
686 mapElem.setAttribute( QStringLiteral( "mapFlags" ), static_cast< int>( mMapFlags ) );
687
688 QDomElement labelBlockingItemsElem = doc.createElement( QStringLiteral( "labelBlockingItems" ) );
689 for ( const auto &item : std::as_const( mBlockingLabelItems ) )
690 {
691 if ( !item )
692 continue;
693
694 QDomElement blockingItemElem = doc.createElement( QStringLiteral( "item" ) );
695 blockingItemElem.setAttribute( QStringLiteral( "uuid" ), item->uuid() );
696 labelBlockingItemsElem.appendChild( blockingItemElem );
697 }
698 mapElem.appendChild( labelBlockingItemsElem );
699
700 //temporal settings
701 mapElem.setAttribute( QStringLiteral( "isTemporal" ), isTemporal() ? 1 : 0 );
702 if ( isTemporal() )
703 {
704 mapElem.setAttribute( QStringLiteral( "temporalRangeBegin" ), temporalRange().begin().toString( Qt::ISODate ) );
705 mapElem.setAttribute( QStringLiteral( "temporalRangeEnd" ), temporalRange().end().toString( Qt::ISODate ) );
706 }
707
708 mAtlasClippingSettings->writeXml( mapElem, doc, context );
709 mItemClippingSettings->writeXml( mapElem, doc, context );
710
711 return true;
712}
713
714bool QgsLayoutItemMap::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
715{
716 mUpdatesEnabled = false;
717
718 //extent
719 QDomNodeList extentNodeList = itemElem.elementsByTagName( QStringLiteral( "Extent" ) );
720 if ( !extentNodeList.isEmpty() )
721 {
722 QDomElement extentElem = extentNodeList.at( 0 ).toElement();
723 double xmin, xmax, ymin, ymax;
724 xmin = extentElem.attribute( QStringLiteral( "xmin" ) ).toDouble();
725 xmax = extentElem.attribute( QStringLiteral( "xmax" ) ).toDouble();
726 ymin = extentElem.attribute( QStringLiteral( "ymin" ) ).toDouble();
727 ymax = extentElem.attribute( QStringLiteral( "ymax" ) ).toDouble();
728 setExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
729 }
730
731 QDomNodeList crsNodeList = itemElem.elementsByTagName( QStringLiteral( "crs" ) );
733 if ( !crsNodeList.isEmpty() )
734 {
735 QDomElement crsElem = crsNodeList.at( 0 ).toElement();
736 crs.readXml( crsElem );
737 }
738 setCrs( crs );
739
740 //map rotation
741 mMapRotation = itemElem.attribute( QStringLiteral( "mapRotation" ), QStringLiteral( "0" ) ).toDouble();
742 mEvaluatedMapRotation = mMapRotation;
743
744 // follow map theme
745 mFollowVisibilityPreset = itemElem.attribute( QStringLiteral( "followPreset" ) ).compare( QLatin1String( "true" ) ) == 0;
746 mFollowVisibilityPresetName = itemElem.attribute( QStringLiteral( "followPresetName" ) );
747
748 //mKeepLayerSet flag
749 QString keepLayerSetFlag = itemElem.attribute( QStringLiteral( "keepLayerSet" ) );
750 if ( keepLayerSetFlag.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
751 {
752 mKeepLayerSet = true;
753 }
754 else
755 {
756 mKeepLayerSet = false;
757 }
758
759 QString drawCanvasItemsFlag = itemElem.attribute( QStringLiteral( "drawCanvasItems" ), QStringLiteral( "true" ) );
760 if ( drawCanvasItemsFlag.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
761 {
762 mDrawAnnotations = true;
763 }
764 else
765 {
766 mDrawAnnotations = false;
767 }
768
769 mLayerStyleOverrides.clear();
770
771 //mLayers
772 mLayers.clear();
773 QDomNodeList layerSetNodeList = itemElem.elementsByTagName( QStringLiteral( "LayerSet" ) );
774 if ( !layerSetNodeList.isEmpty() )
775 {
776 QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
777 QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( QStringLiteral( "Layer" ) );
778 mLayers.reserve( layerIdNodeList.size() );
779 for ( int i = 0; i < layerIdNodeList.size(); ++i )
780 {
781 QDomElement layerElem = layerIdNodeList.at( i ).toElement();
782 QString layerId = layerElem.text();
783 QString layerName = layerElem.attribute( QStringLiteral( "name" ) );
784 QString layerSource = layerElem.attribute( QStringLiteral( "source" ) );
785 QString layerProvider = layerElem.attribute( QStringLiteral( "provider" ) );
786
787 QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
788 ref.resolveWeakly( mLayout->project() );
789 mLayers << ref;
790 }
791 }
792
793 // override styles
794 QDomNodeList layerStylesNodeList = itemElem.elementsByTagName( QStringLiteral( "LayerStyles" ) );
795 mKeepLayerStyles = !layerStylesNodeList.isEmpty();
796 if ( mKeepLayerStyles )
797 {
798 QDomElement layerStylesElem = layerStylesNodeList.at( 0 ).toElement();
799 QDomNodeList layerStyleNodeList = layerStylesElem.elementsByTagName( QStringLiteral( "LayerStyle" ) );
800 for ( int i = 0; i < layerStyleNodeList.size(); ++i )
801 {
802 const QDomElement &layerStyleElement = layerStyleNodeList.at( i ).toElement();
803 QString layerId = layerStyleElement.attribute( QStringLiteral( "layerid" ) );
804 QString layerName = layerStyleElement.attribute( QStringLiteral( "name" ) );
805 QString layerSource = layerStyleElement.attribute( QStringLiteral( "source" ) );
806 QString layerProvider = layerStyleElement.attribute( QStringLiteral( "provider" ) );
807 QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
808 ref.resolveWeakly( mLayout->project() );
809
810 QgsMapLayerStyle style;
811 style.readXml( layerStyleElement );
812 mLayerStyleOverrides.insert( ref.layerId, style.xmlData() );
813 }
814 }
815
816 mDrawing = false;
817 mNumCachedLayers = 0;
818 mCacheInvalidated = true;
819
820 //overviews
821 mOverviewStack->readXml( itemElem, doc, context );
822
823 //grids
824 mGridStack->readXml( itemElem, doc, context );
825
826 //atlas
827 QDomNodeList atlasNodeList = itemElem.elementsByTagName( QStringLiteral( "AtlasMap" ) );
828 if ( !atlasNodeList.isEmpty() )
829 {
830 QDomElement atlasElem = atlasNodeList.at( 0 ).toElement();
831 mAtlasDriven = ( atlasElem.attribute( QStringLiteral( "atlasDriven" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
832 if ( atlasElem.hasAttribute( QStringLiteral( "fixedScale" ) ) ) // deprecated XML
833 {
834 mAtlasScalingMode = ( atlasElem.attribute( QStringLiteral( "fixedScale" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) ) ? Fixed : Auto;
835 }
836 else if ( atlasElem.hasAttribute( QStringLiteral( "scalingMode" ) ) )
837 {
838 mAtlasScalingMode = static_cast<AtlasScalingMode>( atlasElem.attribute( QStringLiteral( "scalingMode" ) ).toInt() );
839 }
840 mAtlasMargin = atlasElem.attribute( QStringLiteral( "margin" ), QStringLiteral( "0.1" ) ).toDouble();
841 }
842
843 setLabelMargin( QgsLayoutMeasurement::decodeMeasurement( itemElem.attribute( QStringLiteral( "labelMargin" ), QStringLiteral( "0" ) ) ) );
844
845 mMapFlags = static_cast< MapItemFlags>( itemElem.attribute( QStringLiteral( "mapFlags" ), nullptr ).toInt() );
846
847 // label blocking items
848 mBlockingLabelItems.clear();
849 mBlockingLabelItemUuids.clear();
850 QDomNodeList labelBlockingNodeList = itemElem.elementsByTagName( QStringLiteral( "labelBlockingItems" ) );
851 if ( !labelBlockingNodeList.isEmpty() )
852 {
853 QDomElement blockingItems = labelBlockingNodeList.at( 0 ).toElement();
854 QDomNodeList labelBlockingNodeList = blockingItems.childNodes();
855 for ( int i = 0; i < labelBlockingNodeList.size(); ++i )
856 {
857 const QDomElement &itemBlockingElement = labelBlockingNodeList.at( i ).toElement();
858 const QString itemUuid = itemBlockingElement.attribute( QStringLiteral( "uuid" ) );
859 mBlockingLabelItemUuids << itemUuid;
860 }
861 }
862
863 mAtlasClippingSettings->readXml( itemElem, doc, context );
864 mItemClippingSettings->readXml( itemElem, doc, context );
865
867
868 //temporal settings
869 setIsTemporal( itemElem.attribute( QStringLiteral( "isTemporal" ) ).toInt() );
870 if ( isTemporal() )
871 {
872 const QDateTime begin = QDateTime::fromString( itemElem.attribute( QStringLiteral( "temporalRangeBegin" ) ), Qt::ISODate );
873 const QDateTime end = QDateTime::fromString( itemElem.attribute( QStringLiteral( "temporalRangeEnd" ) ), Qt::ISODate );
874 setTemporalRange( QgsDateTimeRange( begin, end, true, begin == end ) );
875 }
876
877 mUpdatesEnabled = true;
878 return true;
879}
880
881QPainterPath QgsLayoutItemMap::framePath() const
882{
883 if ( mItemClippingSettings->isActive() )
884 {
885 const QgsGeometry g = mItemClippingSettings->clipPathInMapItemCoordinates();
886 if ( !g.isNull() )
887 return g.constGet()->asQPainterPath();
888 }
890}
891
892void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem *style, QWidget * )
893{
894 if ( !mLayout || !painter || !painter->device() || !mUpdatesEnabled )
895 {
896 return;
897 }
898 if ( !shouldDrawItem() )
899 {
900 return;
901 }
902
903 QRectF thisPaintRect = rect();
904 if ( qgsDoubleNear( thisPaintRect.width(), 0.0 ) || qgsDoubleNear( thisPaintRect.height(), 0 ) )
905 return;
906
907 //TODO - try to reduce the amount of duplicate code here!
908
909 if ( mLayout->renderContext().isPreviewRender() )
910 {
911 QgsScopedQPainterState painterState( painter );
912 painter->setClipRect( thisPaintRect );
913 if ( !mCacheFinalImage || mCacheFinalImage->isNull() )
914 {
915 // No initial render available - so draw some preview text alerting user
916 painter->setBrush( QBrush( QColor( 125, 125, 125, 125 ) ) );
917 painter->drawRect( thisPaintRect );
918 painter->setBrush( Qt::NoBrush );
919 QFont messageFont;
920 messageFont.setPointSize( 12 );
921 painter->setFont( messageFont );
922 painter->setPen( QColor( 255, 255, 255, 255 ) );
923 painter->drawText( thisPaintRect, Qt::AlignCenter | Qt::AlignHCenter, tr( "Rendering map" ) );
924 if ( mPainterJob && mCacheInvalidated && !mDrawingPreview )
925 {
926 // current job was invalidated - start a new one
927 mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( style, painter );
928 mBackgroundUpdateTimer->start( 1 );
929 }
930 else if ( !mPainterJob && !mDrawingPreview )
931 {
932 // this is the map's very first paint - trigger a cache update
933 mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( style, painter );
934 mBackgroundUpdateTimer->start( 1 );
935 }
936 }
937 else
938 {
939 if ( mCacheInvalidated && !mDrawingPreview )
940 {
941 // cache was invalidated - trigger a background update
942 mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( style, painter );
943 mBackgroundUpdateTimer->start( 1 );
944 }
945
946 //Background color is already included in cached image, so no need to draw
947
948 double imagePixelWidth = mCacheFinalImage->width(); //how many pixels of the image are for the map extent?
949 double scale = rect().width() / imagePixelWidth;
950
951 QgsScopedQPainterState rotatedPainterState( painter );
952
953 painter->translate( mLastRenderedImageOffsetX + mXOffset, mLastRenderedImageOffsetY + mYOffset );
954 painter->scale( scale, scale );
955 painter->drawImage( 0, 0, *mCacheFinalImage );
956
957 //restore rotation
958 }
959
960 painter->setClipRect( thisPaintRect, Qt::NoClip );
961
962 mOverviewStack->drawItems( painter, false );
963 mGridStack->drawItems( painter );
964 drawAnnotations( painter );
965 drawMapFrame( painter );
966 }
967 else
968 {
969 if ( mDrawing )
970 return;
971
972 mDrawing = true;
973 QPaintDevice *paintDevice = painter->device();
974 if ( !paintDevice )
975 return;
976
977 QgsRectangle cExtent = extent();
978 QSizeF size( cExtent.width() * mapUnitsToLayoutUnits(), cExtent.height() * mapUnitsToLayoutUnits() );
979
980 if ( mLayout && mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagLosslessImageRendering )
981 painter->setRenderHint( QPainter::LosslessImageRendering, true );
982
983 if ( containsAdvancedEffects() && ( !mLayout || !( mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagForceVectorOutput ) ) )
984 {
985 // rasterize
986 double destinationDpi = QgsLayoutUtils::scaleFactorFromItemStyle( style, painter ) * 25.4;
987 double layoutUnitsInInches = mLayout ? mLayout->convertFromLayoutUnits( 1, Qgis::LayoutUnit::Inches ).length() : 1;
988 int widthInPixels = static_cast< int >( std::round( boundingRect().width() * layoutUnitsInInches * destinationDpi ) );
989 int heightInPixels = static_cast< int >( std::round( boundingRect().height() * layoutUnitsInInches * destinationDpi ) );
990 QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
991
992 image.fill( Qt::transparent );
993 image.setDotsPerMeterX( static_cast< int >( std::round( 1000 * destinationDpi / 25.4 ) ) );
994 image.setDotsPerMeterY( static_cast< int >( std::round( 1000 * destinationDpi / 25.4 ) ) );
995 double dotsPerMM = destinationDpi / 25.4;
996 QPainter p( &image );
997
998 QPointF tl = -boundingRect().topLeft();
999 QRect imagePaintRect( static_cast< int >( std::round( tl.x() * dotsPerMM ) ),
1000 static_cast< int >( std::round( tl.y() * dotsPerMM ) ),
1001 static_cast< int >( std::round( thisPaintRect.width() * dotsPerMM ) ),
1002 static_cast< int >( std::round( thisPaintRect.height() * dotsPerMM ) ) );
1003 p.setClipRect( imagePaintRect );
1004
1005 p.translate( imagePaintRect.topLeft() );
1006
1007 // Fill with background color - must be drawn onto the flattened image
1008 // so that layers with opacity or blend modes can correctly interact with it
1009 if ( shouldDrawPart( Background ) )
1010 {
1011 p.scale( dotsPerMM, dotsPerMM );
1012 drawMapBackground( &p );
1013 p.scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
1014 }
1015
1016 drawMap( &p, cExtent, imagePaintRect.size(), image.logicalDpiX() );
1017
1018 // important - all other items, overviews, grids etc must be rendered to the
1019 // flattened image, in case these have blend modes must need to interact
1020 // with the map
1021 p.scale( dotsPerMM, dotsPerMM );
1022
1023 if ( shouldDrawPart( OverviewMapExtent ) )
1024 {
1025 mOverviewStack->drawItems( &p, false );
1026 }
1027 if ( shouldDrawPart( Grid ) )
1028 {
1029 mGridStack->drawItems( &p );
1030 }
1031 drawAnnotations( &p );
1032
1033 QgsScopedQPainterState painterState( painter );
1034 painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
1035 painter->drawImage( QPointF( -tl.x()* dotsPerMM, -tl.y() * dotsPerMM ), image );
1036 painter->scale( dotsPerMM, dotsPerMM );
1037 }
1038 else
1039 {
1040 // Fill with background color
1041 if ( shouldDrawPart( Background ) )
1042 {
1043 drawMapBackground( painter );
1044 }
1045
1046 QgsScopedQPainterState painterState( painter );
1047 painter->setClipRect( thisPaintRect );
1048
1049 if ( shouldDrawPart( Layer ) && !qgsDoubleNear( size.width(), 0.0 ) && !qgsDoubleNear( size.height(), 0.0 ) )
1050 {
1051 QgsScopedQPainterState stagedPainterState( painter );
1052 painter->translate( mXOffset, mYOffset );
1053
1054 double dotsPerMM = paintDevice->logicalDpiX() / 25.4;
1055 size *= dotsPerMM; // output size will be in dots (pixels)
1056 painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
1057
1058 if ( mCurrentExportPart != NotLayered )
1059 {
1060 if ( !mStagedRendererJob )
1061 {
1062 createStagedRenderJob( cExtent, size, paintDevice->logicalDpiX() );
1063 }
1064
1065 mStagedRendererJob->renderCurrentPart( painter );
1066 }
1067 else
1068 {
1069 drawMap( painter, cExtent, size, paintDevice->logicalDpiX() );
1070 }
1071 }
1072
1073 painter->setClipRect( thisPaintRect, Qt::NoClip );
1074
1075 if ( shouldDrawPart( OverviewMapExtent ) )
1076 {
1077 mOverviewStack->drawItems( painter, false );
1078 }
1079 if ( shouldDrawPart( Grid ) )
1080 {
1081 mGridStack->drawItems( painter );
1082 }
1083 drawAnnotations( painter );
1084 }
1085
1086 if ( shouldDrawPart( Frame ) )
1087 {
1088 drawMapFrame( painter );
1089 }
1090
1091 mDrawing = false;
1092 }
1093}
1094
1096{
1097 const int layerCount = layersToRender().length();
1098 return ( hasBackground() ? 1 : 0 )
1099 + ( layerCount + ( layerCount ? 1 : 0 ) ) // +1 for label layer, if labels present
1100 + ( mGridStack->hasEnabledItems() ? 1 : 0 )
1101 + ( mOverviewStack->hasEnabledItems() ? 1 : 0 )
1102 + ( frameEnabled() ? 1 : 0 );
1103}
1104
1106{
1107 mCurrentExportPart = Start;
1108 // only follow export themes if the map isn't set to follow a fixed theme
1109 mExportThemes = !mFollowVisibilityPreset ? mLayout->renderContext().exportThemes() : QStringList();
1110 mExportThemeIt = mExportThemes.begin();
1111}
1112
1114{
1115 mCurrentExportPart = NotLayered;
1116 mExportThemes.clear();
1117 mExportThemeIt = mExportThemes.begin();
1118}
1119
1121{
1122 switch ( mCurrentExportPart )
1123 {
1124 case Start:
1125 if ( hasBackground() )
1126 {
1127 mCurrentExportPart = Background;
1128 return true;
1129 }
1131
1132 case Background:
1133 mCurrentExportPart = Layer;
1134 return true;
1135
1136 case Layer:
1137 if ( mStagedRendererJob )
1138 {
1139 if ( mStagedRendererJob->nextPart() )
1140 return true;
1141 else
1142 {
1143 mExportLabelingResults.reset( mStagedRendererJob->takeLabelingResults() );
1144 mStagedRendererJob.reset(); // no more map layer parts
1145 }
1146 }
1147
1148 if ( mExportThemeIt != mExportThemes.end() && ++mExportThemeIt != mExportThemes.end() )
1149 {
1150 // move to next theme and continue exporting map layers
1151 return true;
1152 }
1153
1154 if ( mGridStack->hasEnabledItems() )
1155 {
1156 mCurrentExportPart = Grid;
1157 return true;
1158 }
1160
1161 case Grid:
1162 for ( int i = 0; i < mOverviewStack->size(); ++i )
1163 {
1164 QgsLayoutItemMapItem *item = mOverviewStack->item( i );
1166 {
1167 mCurrentExportPart = OverviewMapExtent;
1168 return true;
1169 }
1170 }
1172
1173 case OverviewMapExtent:
1174 if ( frameEnabled() )
1175 {
1176 mCurrentExportPart = Frame;
1177 return true;
1178 }
1179
1181
1182 case Frame:
1183 if ( isSelected() && !mLayout->renderContext().isPreviewRender() )
1184 {
1185 mCurrentExportPart = SelectionBoxes;
1186 return true;
1187 }
1189
1190 case SelectionBoxes:
1191 mCurrentExportPart = End;
1192 return false;
1193
1194 case End:
1195 return false;
1196
1197 case NotLayered:
1198 return false;
1199 }
1200 return false;
1201}
1202
1204{
1205 return ItemContainsSubLayers;
1206}
1207
1209{
1210 ExportLayerDetail detail;
1211
1212 switch ( mCurrentExportPart )
1213 {
1214 case Start:
1215 break;
1216
1217 case Background:
1218 detail.name = tr( "%1: Background" ).arg( displayName() );
1219 return detail;
1220
1221 case Layer:
1222 if ( !mExportThemes.empty() && mExportThemeIt != mExportThemes.end() )
1223 detail.mapTheme = *mExportThemeIt;
1224
1225 if ( mStagedRendererJob )
1226 {
1227 switch ( mStagedRendererJob->currentStage() )
1228 {
1230 {
1231 detail.mapLayerId = mStagedRendererJob->currentLayerId();
1232 detail.compositionMode = mStagedRendererJob->currentLayerCompositionMode();
1233 detail.opacity = mStagedRendererJob->currentLayerOpacity();
1234 if ( const QgsMapLayer *layer = mLayout->project()->mapLayer( detail.mapLayerId ) )
1235 {
1236 if ( !detail.mapTheme.isEmpty() )
1237 detail.name = QStringLiteral( "%1 (%2): %3" ).arg( displayName(), detail.mapTheme, layer->name() );
1238 else
1239 detail.name = QStringLiteral( "%1: %2" ).arg( displayName(), layer->name() );
1240 }
1241 else if ( mLayout->project()->mainAnnotationLayer()->id() == detail.mapLayerId )
1242 {
1243 // master annotation layer
1244 if ( !detail.mapTheme.isEmpty() )
1245 detail.name = QStringLiteral( "%1 (%2): %3" ).arg( displayName(), detail.mapTheme, tr( "Annotations" ) );
1246 else
1247 detail.name = QStringLiteral( "%1: %2" ).arg( displayName(), tr( "Annotations" ) );
1248 }
1249 else
1250 {
1251 // might be an item based layer
1252 const QList<QgsLayoutItemMapOverview *> res = mOverviewStack->asList();
1253 for ( QgsLayoutItemMapOverview *item : res )
1254 {
1255 if ( !item || !item->enabled() || item->stackingPosition() == QgsLayoutItemMapItem::StackAboveMapLabels )
1256 continue;
1257
1258 if ( item->mapLayer() && detail.mapLayerId == item->mapLayer()->id() )
1259 {
1260 if ( !detail.mapTheme.isEmpty() )
1261 detail.name = QStringLiteral( "%1 (%2): %3" ).arg( displayName(), detail.mapTheme, item->mapLayer()->name() );
1262 else
1263 detail.name = QStringLiteral( "%1: %2" ).arg( displayName(), item->mapLayer()->name() );
1264 break;
1265 }
1266 }
1267 }
1268 return detail;
1269 }
1270
1272 detail.mapLayerId = mStagedRendererJob->currentLayerId();
1273 if ( const QgsMapLayer *layer = mLayout->project()->mapLayer( detail.mapLayerId ) )
1274 {
1275 if ( !detail.mapTheme.isEmpty() )
1276 detail.name = QStringLiteral( "%1 (%2): %3 (Labels)" ).arg( displayName(), detail.mapTheme, layer->name() );
1277 else
1278 detail.name = tr( "%1: %2 (Labels)" ).arg( displayName(), layer->name() );
1279 }
1280 else
1281 {
1282 if ( !detail.mapTheme.isEmpty() )
1283 detail.name = tr( "%1 (%2): Labels" ).arg( displayName(), detail.mapTheme );
1284 else
1285 detail.name = tr( "%1: Labels" ).arg( displayName() );
1286 }
1287 return detail;
1288
1290 break;
1291 }
1292 }
1293 else
1294 {
1295 // we must be on the first layer, not having had a chance to create the render job yet
1296 const QList< QgsMapLayer * > layers = layersToRender();
1297 if ( !layers.isEmpty() )
1298 {
1299 const QgsMapLayer *layer = layers.constLast();
1300 if ( !detail.mapTheme.isEmpty() )
1301 detail.name = QStringLiteral( "%1 (%2): %3" ).arg( displayName(), detail.mapTheme, layer->name() );
1302 else
1303 detail.name = QStringLiteral( "%1: %2" ).arg( displayName(), layer->name() );
1304 detail.mapLayerId = layer->id();
1305 }
1306 }
1307 break;
1308
1309 case Grid:
1310 detail.name = tr( "%1: Grids" ).arg( displayName() );
1311 return detail;
1312
1313 case OverviewMapExtent:
1314 detail.name = tr( "%1: Overviews" ).arg( displayName() );
1315 return detail;
1316
1317 case Frame:
1318 detail.name = tr( "%1: Frame" ).arg( displayName() );
1319 return detail;
1320
1321 case SelectionBoxes:
1322 case End:
1323 case NotLayered:
1324 break;
1325 }
1326
1327 return detail;
1328}
1329
1331{
1334}
1335
1336void QgsLayoutItemMap::drawMap( QPainter *painter, const QgsRectangle &extent, QSizeF size, double dpi )
1337{
1338 if ( !painter )
1339 {
1340 return;
1341 }
1342 if ( qgsDoubleNear( size.width(), 0.0 ) || qgsDoubleNear( size.height(), 0.0 ) )
1343 {
1344 //don't attempt to draw if size is invalid
1345 return;
1346 }
1347
1348 // render
1349 QgsMapSettings ms( mapSettings( extent, size, dpi, true ) );
1350 if ( shouldDrawPart( OverviewMapExtent ) )
1351 {
1352 ms.setLayers( mOverviewStack->modifyMapLayerList( ms.layers() ) );
1353 }
1354
1355 QgsMapRendererCustomPainterJob job( ms, painter );
1356#ifdef HAVE_SERVER_PYTHON_PLUGINS
1357 job.setFeatureFilterProvider( mLayout->renderContext().featureFilterProvider() );
1358#endif
1359
1360 // Render the map in this thread. This is done because of problems
1361 // with printing to printer on Windows (printing to PDF is fine though).
1362 // Raster images were not displayed - see #10599
1363 job.renderSynchronously();
1364
1365 mExportLabelingResults.reset( job.takeLabelingResults() );
1366
1367 mRenderingErrors = job.errors();
1368}
1369
1370void QgsLayoutItemMap::recreateCachedImageInBackground()
1371{
1372 if ( mPainterJob )
1373 {
1374 disconnect( mPainterJob.get(), &QgsMapRendererCustomPainterJob::finished, this, &QgsLayoutItemMap::painterJobFinished );
1375 QgsMapRendererCustomPainterJob *oldJob = mPainterJob.release();
1376 QPainter *oldPainter = mPainter.release();
1377 QImage *oldImage = mCacheRenderingImage.release();
1378 connect( oldJob, &QgsMapRendererCustomPainterJob::finished, this, [oldPainter, oldJob, oldImage]
1379 {
1380 oldJob->deleteLater();
1381 delete oldPainter;
1382 delete oldImage;
1383 } );
1384 oldJob->cancelWithoutBlocking();
1385 }
1386 else
1387 {
1388 mCacheRenderingImage.reset( nullptr );
1390 }
1391
1392 Q_ASSERT( !mPainterJob );
1393 Q_ASSERT( !mPainter );
1394 Q_ASSERT( !mCacheRenderingImage );
1395
1396 QgsRectangle ext = extent();
1397 double widthLayoutUnits = ext.width() * mapUnitsToLayoutUnits();
1398 double heightLayoutUnits = ext.height() * mapUnitsToLayoutUnits();
1399
1400 int w = static_cast< int >( std::round( widthLayoutUnits * mPreviewScaleFactor ) );
1401 int h = static_cast< int >( std::round( heightLayoutUnits * mPreviewScaleFactor ) );
1402
1403 // limit size of image for better performance
1404 if ( w > 5000 || h > 5000 )
1405 {
1406 if ( w > h )
1407 {
1408 w = 5000;
1409 h = static_cast< int>( std::round( w * heightLayoutUnits / widthLayoutUnits ) );
1410 }
1411 else
1412 {
1413 h = 5000;
1414 w = static_cast< int >( std::round( h * widthLayoutUnits / heightLayoutUnits ) );
1415 }
1416 }
1417
1418 if ( w <= 0 || h <= 0 )
1419 return;
1420
1421 mCacheRenderingImage.reset( new QImage( w, h, QImage::Format_ARGB32 ) );
1422
1423 // set DPI of the image
1424 mCacheRenderingImage->setDotsPerMeterX( static_cast< int >( std::round( 1000 * w / widthLayoutUnits ) ) );
1425 mCacheRenderingImage->setDotsPerMeterY( static_cast< int >( std::round( 1000 * h / heightLayoutUnits ) ) );
1426
1427 //start with empty fill to avoid artifacts
1428 mCacheRenderingImage->fill( QColor( 255, 255, 255, 0 ).rgba() );
1429 if ( hasBackground() )
1430 {
1431 //Initially fill image with specified background color. This ensures that layers with blend modes will
1432 //preview correctly
1433 if ( mItemClippingSettings->isActive() )
1434 {
1435 QPainter p( mCacheRenderingImage.get() );
1436 const QPainterPath path = framePath();
1437 p.setPen( Qt::NoPen );
1438 p.setBrush( backgroundColor() );
1439 p.scale( mCacheRenderingImage->width() / widthLayoutUnits, mCacheRenderingImage->height() / heightLayoutUnits );
1440 p.drawPath( path );
1441 p.end();
1442 }
1443 else
1444 {
1445 mCacheRenderingImage->fill( backgroundColor().rgba() );
1446 }
1447 }
1448
1449 mCacheInvalidated = false;
1450 mPainter.reset( new QPainter( mCacheRenderingImage.get() ) );
1451 QgsMapSettings settings( mapSettings( ext, QSizeF( w, h ), mCacheRenderingImage->logicalDpiX(), true ) );
1452
1453 if ( shouldDrawPart( OverviewMapExtent ) )
1454 {
1455 settings.setLayers( mOverviewStack->modifyMapLayerList( settings.layers() ) );
1456 }
1457
1458 mPainterJob.reset( new QgsMapRendererCustomPainterJob( settings, mPainter.get() ) );
1459 connect( mPainterJob.get(), &QgsMapRendererCustomPainterJob::finished, this, &QgsLayoutItemMap::painterJobFinished );
1460 mPainterJob->start();
1461
1462 // from now on we can accept refresh requests again
1463 // this must be reset only after the job has been started, because
1464 // some providers (yes, it's you WCS and AMS!) during preparation
1465 // do network requests and start an internal event loop, which may
1466 // end up calling refresh() and would schedule another refresh,
1467 // deleting the one we have just started.
1468
1469 // ^^ that comment was directly copied from a similar fix in QgsMapCanvas. And
1470 // with little surprise, both those providers are still badly behaved and causing
1471 // annoying bugs for us to deal with...
1472 mDrawingPreview = false;
1473}
1474
1475QgsLayoutItemMap::MapItemFlags QgsLayoutItemMap::mapFlags() const
1476{
1477 return mMapFlags;
1478}
1479
1480void QgsLayoutItemMap::setMapFlags( QgsLayoutItemMap::MapItemFlags mapFlags )
1481{
1482 mMapFlags = mapFlags;
1483}
1484
1485QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF size, double dpi, bool includeLayerSettings ) const
1486{
1487 QgsExpressionContext expressionContext = createExpressionContext();
1488 QgsCoordinateReferenceSystem renderCrs = crs();
1489
1490 QgsMapSettings jobMapSettings;
1491 jobMapSettings.setDestinationCrs( renderCrs );
1492 jobMapSettings.setExtent( extent );
1493 jobMapSettings.setOutputSize( size.toSize() );
1494 jobMapSettings.setOutputDpi( dpi );
1495 if ( layout()->renderContext().isPreviewRender() )
1496 jobMapSettings.setDpiTarget( layout()->renderContext().dpi() );
1497 jobMapSettings.setBackgroundColor( Qt::transparent );
1498 jobMapSettings.setRotation( mEvaluatedMapRotation );
1499 if ( mLayout )
1500 {
1501 jobMapSettings.setEllipsoid( mLayout->project()->ellipsoid() );
1502 jobMapSettings.setElevationShadingRenderer( mLayout->project()->elevationShadingRenderer() );
1503 }
1504
1505 if ( includeLayerSettings )
1506 {
1507 //set layers to render
1508 QList<QgsMapLayer *> layers = layersToRender( &expressionContext );
1509
1510 if ( !mLayout->project()->mainAnnotationLayer()->isEmpty() )
1511 {
1512 // render main annotation layer above all other layers
1513 layers.insert( 0, mLayout->project()->mainAnnotationLayer() );
1514 }
1515
1516 jobMapSettings.setLayers( layers );
1517 jobMapSettings.setLayerStyleOverrides( layerStyleOverridesToRender( expressionContext ) );
1518 }
1519
1520 if ( !mLayout->renderContext().isPreviewRender() )
1521 {
1522 //if outputting layout, we disable optimisations like layer simplification by default, UNLESS the context specifically tells us to use them
1523 jobMapSettings.setFlag( Qgis::MapSettingsFlag::UseRenderingOptimization, mLayout->renderContext().simplifyMethod().simplifyHints() != QgsVectorSimplifyMethod::NoSimplification );
1524 jobMapSettings.setSimplifyMethod( mLayout->renderContext().simplifyMethod() );
1526 }
1527 else
1528 {
1529 // preview render - always use optimization
1531 // in a preview render we disable vector masking, as that is considerably slower vs raster masking
1532 jobMapSettings.setFlag( Qgis::MapSettingsFlag::ForceRasterMasks, true );
1534 }
1535
1536 jobMapSettings.setExpressionContext( expressionContext );
1537
1538 // layout-specific overrides of flags
1539 jobMapSettings.setFlag( Qgis::MapSettingsFlag::ForceVectorOutput, true ); // force vector output (no caching of marker images etc.)
1543 jobMapSettings.setFlag( Qgis::MapSettingsFlag::DrawEditingInfo, false );
1544 jobMapSettings.setSelectionColor( mLayout->renderContext().selectionColor() );
1548 jobMapSettings.setTransformContext( mLayout->project()->transformContext() );
1549 jobMapSettings.setPathResolver( mLayout->project()->pathResolver() );
1550
1551 QgsLabelingEngineSettings labelSettings = mLayout->project()->labelingEngineSettings();
1552
1553 // override project "show partial labels" setting with this map's setting
1557 jobMapSettings.setLabelingEngineSettings( labelSettings );
1558
1559 // override the default text render format inherited from the labeling engine settings using the layout's render context setting
1560 jobMapSettings.setTextRenderFormat( mLayout->renderContext().textRenderFormat() );
1561
1562 QgsGeometry labelBoundary;
1563 if ( mEvaluatedLabelMargin.length() > 0 )
1564 {
1565 QPolygonF visiblePoly = jobMapSettings.visiblePolygon();
1566 visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
1567 const double layoutLabelMargin = mLayout->convertToLayoutUnits( mEvaluatedLabelMargin );
1568 const double layoutLabelMarginInMapUnits = layoutLabelMargin / rect().width() * jobMapSettings.extent().width();
1569 QgsGeometry mapBoundaryGeom = QgsGeometry::fromQPolygonF( visiblePoly );
1570 mapBoundaryGeom = mapBoundaryGeom.buffer( -layoutLabelMarginInMapUnits, 0 );
1571 labelBoundary = mapBoundaryGeom;
1572 }
1573
1574 if ( !mBlockingLabelItems.isEmpty() )
1575 {
1576 jobMapSettings.setLabelBlockingRegions( createLabelBlockingRegions( jobMapSettings ) );
1577 }
1578
1579 for ( QgsRenderedFeatureHandlerInterface *handler : std::as_const( mRenderedFeatureHandlers ) )
1580 {
1581 jobMapSettings.addRenderedFeatureHandler( handler );
1582 }
1583
1584 if ( isTemporal() )
1585 jobMapSettings.setTemporalRange( temporalRange() );
1586
1587 if ( mAtlasClippingSettings->enabled() && mLayout->reportContext().feature().isValid() )
1588 {
1589 QgsGeometry clipGeom( mLayout->reportContext().currentGeometry( jobMapSettings.destinationCrs() ) );
1590 QgsMapClippingRegion region( clipGeom );
1591 region.setFeatureClip( mAtlasClippingSettings->featureClippingType() );
1592 region.setRestrictedLayers( mAtlasClippingSettings->layersToClip() );
1593 region.setRestrictToLayers( mAtlasClippingSettings->restrictToLayers() );
1594 jobMapSettings.addClippingRegion( region );
1595
1596 if ( mAtlasClippingSettings->forceLabelsInsideFeature() )
1597 {
1598 if ( !labelBoundary.isEmpty() )
1599 {
1600 labelBoundary = clipGeom.intersection( labelBoundary );
1601 }
1602 else
1603 {
1604 labelBoundary = clipGeom;
1605 }
1606 }
1607 }
1608
1609 if ( mItemClippingSettings->isActive() )
1610 {
1611 const QgsGeometry clipGeom = mItemClippingSettings->clippedMapExtent();
1612 if ( !clipGeom.isEmpty() )
1613 {
1614 jobMapSettings.addClippingRegion( mItemClippingSettings->toMapClippingRegion() );
1615
1616 if ( mItemClippingSettings->forceLabelsInsideClipPath() )
1617 {
1618 const double layoutLabelMargin = mLayout->convertToLayoutUnits( mEvaluatedLabelMargin );
1619 const double layoutLabelMarginInMapUnits = layoutLabelMargin / rect().width() * jobMapSettings.extent().width();
1620 QgsGeometry mapBoundaryGeom = clipGeom;
1621 mapBoundaryGeom = mapBoundaryGeom.buffer( -layoutLabelMarginInMapUnits, 0 );
1622 if ( !labelBoundary.isEmpty() )
1623 {
1624 labelBoundary = mapBoundaryGeom.intersection( labelBoundary );
1625 }
1626 else
1627 {
1628 labelBoundary = mapBoundaryGeom;
1629 }
1630 }
1631 }
1632 }
1633
1634 if ( !labelBoundary.isNull() )
1635 jobMapSettings.setLabelBoundaryGeometry( labelBoundary );
1636
1637 return jobMapSettings;
1638}
1639
1641{
1642 assignFreeId();
1643
1644 mBlockingLabelItems.clear();
1645 for ( const QString &uuid : std::as_const( mBlockingLabelItemUuids ) )
1646 {
1647 QgsLayoutItem *item = mLayout->itemByUuid( uuid, true );
1648 if ( item )
1649 {
1650 addLabelBlockingItem( item );
1651 }
1652 }
1653
1654 mOverviewStack->finalizeRestoreFromXml();
1655 mGridStack->finalizeRestoreFromXml();
1656 mItemClippingSettings->finalizeRestoreFromXml();
1657}
1658
1659void QgsLayoutItemMap::setMoveContentPreviewOffset( double xOffset, double yOffset )
1660{
1661 mXOffset = xOffset;
1662 mYOffset = yOffset;
1663}
1664
1666{
1667 return mCurrentRectangle;
1668}
1669
1671{
1673
1674 //Can't utilize QgsExpressionContextUtils::mapSettingsScope as we don't always
1675 //have a QgsMapSettings object available when the context is required, so we manually
1676 //add the same variables here
1677 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Map Settings" ) );
1678
1679 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_id" ), id(), true ) );
1680 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_rotation" ), mMapRotation, true ) );
1681 const double mapScale = scale();
1682 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_scale" ), mapScale, true ) );
1683
1684 scope->setVariable( QStringLiteral( "zoom_level" ), QgsVectorTileUtils::scaleToZoomLevel( mapScale, 0, 99999 ), true );
1685 scope->setVariable( QStringLiteral( "vector_tile_zoom" ), QgsVectorTileUtils::scaleToZoom( mapScale ), true );
1686
1687 QgsRectangle currentExtent( extent() );
1688 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_extent" ), QVariant::fromValue( QgsGeometry::fromRect( currentExtent ) ), true ) );
1689 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_extent_width" ), currentExtent.width(), true ) );
1690 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_extent_height" ), currentExtent.height(), true ) );
1691 QgsGeometry centerPoint = QgsGeometry::fromPointXY( currentExtent.center() );
1692 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_extent_center" ), QVariant::fromValue( centerPoint ), true ) );
1693
1695 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs" ), mapCrs.authid(), true ) );
1696 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_definition" ), mapCrs.toProj(), true ) );
1697 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_description" ), mapCrs.description(), true ) );
1698 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_units" ), QgsUnitTypes::toString( mapCrs.mapUnits() ), true ) );
1699 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_acronym" ), mapCrs.projectionAcronym(), true ) );
1700 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_projection" ), mapCrs.operation().description(), true ) );
1701 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_ellipsoid" ), mapCrs.ellipsoidAcronym(), true ) );
1702 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_proj4" ), mapCrs.toProj(), true ) );
1703 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_wkt" ), mapCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), true ) );
1704
1705 QVariantList layersIds;
1706 QVariantList layers;
1707 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_layer_ids" ), layersIds, true ) );
1708 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_layers" ), layers, true ) );
1709
1710 context.appendScope( scope );
1711
1712 // The scope map_layer_ids and map_layers variables have been added to the context, only now we can
1713 // call layersToRender (just in case layersToRender relies on evaluating an expression which uses
1714 // other variables contained within the map settings scope
1715 const QList<QgsMapLayer *> layersInMap = layersToRender( &context );
1716
1717 layersIds.reserve( layersInMap.count() );
1718 layers.reserve( layersInMap.count() );
1719 for ( QgsMapLayer *layer : layersInMap )
1720 {
1721 layersIds << layer->id();
1722 layers << QVariant::fromValue<QgsWeakMapLayerPointer>( QgsWeakMapLayerPointer( layer ) );
1723 }
1724 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_layer_ids" ), layersIds, true ) );
1725 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_layers" ), layers, true ) );
1726
1727 scope->addFunction( QStringLiteral( "is_layer_visible" ), new QgsExpressionContextUtils::GetLayerVisibility( layersInMap, scale() ) );
1728
1729 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_start_time" ), isTemporal() ? temporalRange().begin() : QVariant(), true ) );
1730 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_end_time" ), isTemporal() ? temporalRange().end() : QVariant(), true ) );
1731 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_interval" ), isTemporal() ? QgsInterval( temporalRange().end() - temporalRange().begin() ) : QVariant(), true ) );
1732
1733#if 0 // not relevant here! (but left so as to respect all the dangerous warnings in QgsExpressionContextUtils::mapSettingsScope)
1734 if ( mapSettings.frameRate() >= 0 )
1735 scope->setVariable( QStringLiteral( "frame_rate" ), mapSettings.frameRate(), true );
1736 if ( mapSettings.currentFrame() >= 0 )
1737 scope->setVariable( QStringLiteral( "frame_number" ), mapSettings.currentFrame(), true );
1738#endif
1739
1740 return context;
1741}
1742
1744{
1745 double extentWidth = extent().width();
1746 if ( extentWidth <= 0 )
1747 {
1748 return 1;
1749 }
1750 return rect().width() / extentWidth;
1751}
1752
1754{
1755 double dx = mXOffset;
1756 double dy = mYOffset;
1757 transformShift( dx, dy );
1758 QPolygonF poly = calculateVisibleExtentPolygon( false );
1759 poly.translate( -dx, -dy );
1760 return poly;
1761}
1762
1764{
1765 if ( !mBlockingLabelItems.contains( item ) )
1766 mBlockingLabelItems.append( item );
1767
1768 connect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutItemMap::invalidateCache, Qt::UniqueConnection );
1769}
1770
1772{
1773 mBlockingLabelItems.removeAll( item );
1774 if ( item )
1776}
1777
1779{
1780 return mBlockingLabelItems.contains( item );
1781}
1782
1784{
1785 return mPreviewLabelingResults.get();
1786}
1787
1789{
1790 // NOTE: if visitEnter returns false it means "don't visit the item", not "abort all further visitations"
1792 return true;
1793
1794 if ( mOverviewStack )
1795 {
1796 for ( int i = 0; i < mOverviewStack->size(); ++i )
1797 {
1798 if ( mOverviewStack->item( i )->accept( visitor ) )
1799 return false;
1800 }
1801 }
1802
1803 if ( mGridStack )
1804 {
1805 for ( int i = 0; i < mGridStack->size(); ++i )
1806 {
1807 if ( mGridStack->item( i )->accept( visitor ) )
1808 return false;
1809 }
1810 }
1811
1813 return false;
1814
1815 return true;
1816}
1817
1819{
1820 mRenderedFeatureHandlers.append( handler );
1821}
1822
1824{
1825 mRenderedFeatureHandlers.removeAll( handler );
1826}
1827
1828QPointF QgsLayoutItemMap::mapToItemCoords( QPointF mapCoords ) const
1829{
1830 QPolygonF mapPoly = transformedMapPolygon();
1831 if ( mapPoly.empty() )
1832 {
1833 return QPointF( 0, 0 );
1834 }
1835
1836 QgsRectangle tExtent = transformedExtent();
1837 QgsPointXY rotationPoint( ( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
1838 double dx = mapCoords.x() - rotationPoint.x();
1839 double dy = mapCoords.y() - rotationPoint.y();
1840 QgsLayoutUtils::rotate( -mEvaluatedMapRotation, dx, dy );
1841 QgsPointXY backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
1842
1843 QgsRectangle unrotatedExtent = transformedExtent();
1844 double xItem = rect().width() * ( backRotatedCoords.x() - unrotatedExtent.xMinimum() ) / unrotatedExtent.width();
1845 double yItem = rect().height() * ( 1 - ( backRotatedCoords.y() - unrotatedExtent.yMinimum() ) / unrotatedExtent.height() );
1846 return QPointF( xItem, yItem );
1847}
1848
1850{
1852 QgsRectangle newExtent = mExtent;
1853 if ( qgsDoubleNear( mEvaluatedMapRotation, 0.0 ) )
1854 {
1855 extent = newExtent;
1856 }
1857 else
1858 {
1859 QPolygonF poly;
1860 mapPolygon( newExtent, poly );
1861 QRectF bRect = poly.boundingRect();
1862 extent.setXMinimum( bRect.left() );
1863 extent.setXMaximum( bRect.right() );
1864 extent.setYMinimum( bRect.top() );
1865 extent.setYMaximum( bRect.bottom() );
1866 }
1867 return extent;
1868}
1869
1871{
1872 if ( mDrawing )
1873 return;
1874
1875 mCacheInvalidated = true;
1876 update();
1877}
1878
1880{
1881 QRectF rectangle = rect();
1882 double frameExtension = frameEnabled() ? pen().widthF() / 2.0 : 0.0;
1883
1884 double topExtension = 0.0;
1885 double rightExtension = 0.0;
1886 double bottomExtension = 0.0;
1887 double leftExtension = 0.0;
1888
1889 if ( mGridStack )
1890 mGridStack->calculateMaxGridExtension( topExtension, rightExtension, bottomExtension, leftExtension );
1891
1892 topExtension = std::max( topExtension, frameExtension );
1893 rightExtension = std::max( rightExtension, frameExtension );
1894 bottomExtension = std::max( bottomExtension, frameExtension );
1895 leftExtension = std::max( leftExtension, frameExtension );
1896
1897 rectangle.setLeft( rectangle.left() - leftExtension );
1898 rectangle.setRight( rectangle.right() + rightExtension );
1899 rectangle.setTop( rectangle.top() - topExtension );
1900 rectangle.setBottom( rectangle.bottom() + bottomExtension );
1901 if ( rectangle != mCurrentRectangle )
1902 {
1903 prepareGeometryChange();
1904 mCurrentRectangle = rectangle;
1905 }
1906}
1907
1909{
1911 if ( property == QgsLayoutObject::MapCrs || property == QgsLayoutObject::AllProperties )
1912 {
1913 bool ok;
1914 const QString crsVar = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapCrs, context, QString(), &ok );
1915 if ( ok && QgsCoordinateReferenceSystem( crsVar ).isValid() )
1916 {
1917 const QgsCoordinateReferenceSystem newCrs( crsVar );
1918 if ( newCrs.isValid() )
1919 {
1920 setCrs( newCrs );
1921 }
1922 }
1923 }
1924 //updates data defined properties and redraws item to match
1925 if ( property == QgsLayoutObject::MapRotation || property == QgsLayoutObject::MapScale ||
1926 property == QgsLayoutObject::MapXMin || property == QgsLayoutObject::MapYMin ||
1927 property == QgsLayoutObject::MapXMax || property == QgsLayoutObject::MapYMax ||
1928 property == QgsLayoutObject::MapAtlasMargin ||
1929 property == QgsLayoutObject::AllProperties )
1930 {
1931 QgsRectangle beforeExtent = mExtent;
1932 refreshMapExtents( &context );
1933 emit changed();
1934 if ( mExtent != beforeExtent )
1935 {
1936 emit extentChanged();
1937 }
1938 }
1939 if ( property == QgsLayoutObject::MapLabelMargin || property == QgsLayoutObject::AllProperties )
1940 {
1941 refreshLabelMargin( false );
1942 }
1943 if ( property == QgsLayoutObject::MapStylePreset || property == QgsLayoutObject::AllProperties )
1944 {
1945 const QString previousTheme = mLastEvaluatedThemeName.isEmpty() ? mFollowVisibilityPresetName : mLastEvaluatedThemeName;
1946 mLastEvaluatedThemeName = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapStylePreset, context, mFollowVisibilityPresetName );
1947 if ( mLastEvaluatedThemeName != previousTheme )
1948 emit themeChanged( mLastEvaluatedThemeName );
1949 }
1950
1951 if ( isTemporal() && ( property == QgsLayoutObject::StartDateTime || property == QgsLayoutObject::EndDateTime || property == QgsLayoutObject::AllProperties ) )
1952 {
1953 QDateTime begin = temporalRange().begin();
1954 QDateTime end = temporalRange().end();
1955
1956 if ( property == QgsLayoutObject::StartDateTime || property == QgsLayoutObject::AllProperties )
1958 if ( property == QgsLayoutObject::EndDateTime || property == QgsLayoutObject::AllProperties )
1960
1961 setTemporalRange( QgsDateTimeRange( begin, end, true, begin == end ) );
1962 }
1963
1964 //force redraw
1965 mCacheInvalidated = true;
1966
1968}
1969
1970void QgsLayoutItemMap::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
1971{
1972 if ( !mLayers.isEmpty() || mLayerStyleOverrides.isEmpty() )
1973 {
1974 for ( QgsMapLayer *layer : layers )
1975 {
1976 mLayerStyleOverrides.remove( layer->id() );
1977 }
1978 _qgis_removeLayers( mLayers, layers );
1979 }
1980}
1981
1982void QgsLayoutItemMap::painterJobFinished()
1983{
1984 mPainter->end();
1985 mPreviewLabelingResults.reset( mPainterJob->takeLabelingResults() );
1986 mPainterJob.reset( nullptr );
1987 mPainter.reset( nullptr );
1988 mCacheFinalImage = std::move( mCacheRenderingImage );
1989 mLastRenderedImageOffsetX = 0;
1990 mLastRenderedImageOffsetY = 0;
1992 update();
1993 emit previewRefreshed();
1994}
1995
1996void QgsLayoutItemMap::shapeChanged()
1997{
1998 // keep center as center
1999 QgsPointXY oldCenter = mExtent.center();
2000
2001 double w = rect().width();
2002 double h = rect().height();
2003
2004 // keep same width as before
2005 double newWidth = mExtent.width();
2006 // but scale height to match item's aspect ratio
2007 double newHeight = newWidth * h / w;
2008
2009 mExtent = QgsRectangle::fromCenterAndSize( oldCenter, newWidth, newHeight );
2010
2011 //recalculate data defined scale and extents
2012 refreshMapExtents();
2015 emit changed();
2016 emit extentChanged();
2017}
2018
2019void QgsLayoutItemMap::mapThemeChanged( const QString &theme )
2020{
2021 if ( theme == mCachedLayerStyleOverridesPresetName )
2022 mCachedLayerStyleOverridesPresetName.clear(); // force cache regeneration at next redraw
2023}
2024
2025void QgsLayoutItemMap::currentMapThemeRenamed( const QString &theme, const QString &newTheme )
2026{
2027 if ( theme == mFollowVisibilityPresetName )
2028 {
2029 mFollowVisibilityPresetName = newTheme;
2030 }
2031}
2032
2033void QgsLayoutItemMap::connectUpdateSlot()
2034{
2035 //connect signal from layer registry to update in case of new or deleted layers
2036 QgsProject *project = mLayout->project();
2037 if ( project )
2038 {
2039 // handles updating the stored layer state BEFORE the layers are removed
2040 connect( project, static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *>& layers ) > ( &QgsProject::layersWillBeRemoved ),
2041 this, &QgsLayoutItemMap::layersAboutToBeRemoved );
2042 // redraws the map AFTER layers are removed
2043 connect( project->layerTreeRoot(), &QgsLayerTree::layerOrderChanged, this, [ = ]
2044 {
2045 if ( layers().isEmpty() )
2046 {
2047 //using project layers, and layer order has changed
2048 invalidateCache();
2049 }
2050 } );
2051
2052 connect( project, &QgsProject::crsChanged, this, [ = ]
2053 {
2054 if ( !mCrs.isValid() )
2055 {
2056 //using project CRS, which just changed....
2057 invalidateCache();
2058 emit crsChanged();
2059 }
2060 } );
2061
2062 // If project colors change, we need to redraw the map, as layer symbols may rely on project colors
2063 connect( project, &QgsProject::projectColorsChanged, this, [ = ]
2064 {
2066 } );
2067
2068 connect( project->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsLayoutItemMap::mapThemeChanged );
2069 connect( project->mapThemeCollection(), &QgsMapThemeCollection::mapThemeRenamed, this, &QgsLayoutItemMap::currentMapThemeRenamed );
2070 }
2071 connect( mLayout, &QgsLayout::refreshed, this, &QgsLayoutItemMap::invalidateCache );
2072 connect( &mLayout->renderContext(), &QgsLayoutRenderContext::predefinedScalesChanged, this, [ = ]
2073 {
2074 if ( mAtlasScalingMode == Predefined )
2075 updateAtlasFeature();
2076 } );
2077}
2078
2080{
2081 QPolygonF thisExtent = calculateVisibleExtentPolygon( false );
2082 QTransform mapTransform;
2083 QPolygonF thisRectPoly = QPolygonF( QRectF( 0, 0, rect().width(), rect().height() ) );
2084 //workaround QT Bug #21329
2085 thisRectPoly.pop_back();
2086 thisExtent.pop_back();
2087
2088 QPolygonF thisItemPolyInLayout = mapToScene( thisRectPoly );
2089
2090 //create transform from layout coordinates to map coordinates
2091 QTransform::quadToQuad( thisItemPolyInLayout, thisExtent, mapTransform );
2092 return mapTransform;
2093}
2094
2095QList<QgsLabelBlockingRegion> QgsLayoutItemMap::createLabelBlockingRegions( const QgsMapSettings & ) const
2096{
2097 const QTransform mapTransform = layoutToMapCoordsTransform();
2098 QList< QgsLabelBlockingRegion > blockers;
2099 blockers.reserve( mBlockingLabelItems.count() );
2100 for ( const auto &item : std::as_const( mBlockingLabelItems ) )
2101 {
2102 // invisible items don't block labels!
2103 if ( !item )
2104 continue;
2105
2106 // layout items may be temporarily hidden during layered exports
2107 if ( item->property( "wasVisible" ).isValid() )
2108 {
2109 if ( !item->property( "wasVisible" ).toBool() )
2110 continue;
2111 }
2112 else if ( !item->isVisible() )
2113 continue;
2114
2115 QPolygonF itemRectInMapCoordinates = mapTransform.map( item->mapToScene( item->rect() ) );
2116 itemRectInMapCoordinates.append( itemRectInMapCoordinates.at( 0 ) ); //close polygon
2117 QgsGeometry blockingRegion = QgsGeometry::fromQPolygonF( itemRectInMapCoordinates );
2118 blockers << QgsLabelBlockingRegion( blockingRegion );
2119 }
2120 return blockers;
2121}
2122
2124{
2125 return mLabelMargin;
2126}
2127
2129{
2130 mLabelMargin = margin;
2131 refreshLabelMargin( false );
2132}
2133
2134void QgsLayoutItemMap::updateToolTip()
2135{
2136 setToolTip( displayName() );
2137}
2138
2139QString QgsLayoutItemMap::themeToRender( const QgsExpressionContext &context ) const
2140{
2141 QString presetName;
2142
2143 if ( mFollowVisibilityPreset )
2144 {
2145 presetName = mFollowVisibilityPresetName;
2146 // preset name can be overridden by data-defined one
2147 presetName = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapStylePreset, context, presetName );
2148 }
2149 else if ( !mExportThemes.empty() && mExportThemeIt != mExportThemes.end() )
2150 presetName = *mExportThemeIt;
2151 return presetName;
2152}
2153
2154QList<QgsMapLayer *> QgsLayoutItemMap::layersToRender( const QgsExpressionContext *context ) const
2155{
2156 QgsExpressionContext scopedContext;
2157 if ( !context )
2158 scopedContext = createExpressionContext();
2159 const QgsExpressionContext *evalContext = context ? context : &scopedContext;
2160
2161 QList<QgsMapLayer *> renderLayers;
2162
2163 QString presetName = themeToRender( *evalContext );
2164 if ( !presetName.isEmpty() )
2165 {
2166 if ( mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
2167 renderLayers = mLayout->project()->mapThemeCollection()->mapThemeVisibleLayers( presetName );
2168 else // fallback to using map canvas layers
2169 renderLayers = mLayout->project()->mapThemeCollection()->masterVisibleLayers();
2170 }
2171 else if ( !layers().isEmpty() )
2172 {
2173 renderLayers = layers();
2174 }
2175 else
2176 {
2177 renderLayers = mLayout->project()->mapThemeCollection()->masterVisibleLayers();
2178 }
2179
2180 bool ok = false;
2181 QString ddLayers = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapLayers, *evalContext, QString(), &ok );
2182 if ( ok )
2183 {
2184 renderLayers.clear();
2185
2186 const QStringList layerNames = ddLayers.split( '|' );
2187 //need to convert layer names to layer ids
2188 for ( const QString &name : layerNames )
2189 {
2190 const QList< QgsMapLayer * > matchingLayers = mLayout->project()->mapLayersByName( name );
2191 for ( QgsMapLayer *layer : matchingLayers )
2192 {
2193 renderLayers << layer;
2194 }
2195 }
2196 }
2197
2198 //remove atlas coverage layer if required
2199 if ( mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagHideCoverageLayer )
2200 {
2201 //hiding coverage layer
2202 int removeAt = renderLayers.indexOf( mLayout->reportContext().layer() );
2203 if ( removeAt != -1 )
2204 {
2205 renderLayers.removeAt( removeAt );
2206 }
2207 }
2208
2209 // remove any invalid layers
2210 renderLayers.erase( std::remove_if( renderLayers.begin(), renderLayers.end(), []( QgsMapLayer * layer )
2211 {
2212 return !layer || !layer->isValid();
2213 } ), renderLayers.end() );
2214
2215 return renderLayers;
2216}
2217
2218QMap<QString, QString> QgsLayoutItemMap::layerStyleOverridesToRender( const QgsExpressionContext &context ) const
2219{
2220 QString presetName = themeToRender( context );
2221 if ( !presetName.isEmpty() )
2222 {
2223 if ( mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
2224 {
2225 if ( presetName != mCachedLayerStyleOverridesPresetName )
2226 {
2227 // have to regenerate cache of style overrides
2228 mCachedPresetLayerStyleOverrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( presetName );
2229 mCachedLayerStyleOverridesPresetName = presetName;
2230 }
2231
2232 return mCachedPresetLayerStyleOverrides;
2233 }
2234 else
2235 return QMap<QString, QString>();
2236 }
2237 else if ( mFollowVisibilityPreset )
2238 {
2239 QString presetName = mFollowVisibilityPresetName;
2240 // data defined preset name?
2241 presetName = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapStylePreset, context, presetName );
2242 if ( mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
2243 {
2244 if ( presetName.isEmpty() || presetName != mCachedLayerStyleOverridesPresetName )
2245 {
2246 // have to regenerate cache of style overrides
2247 mCachedPresetLayerStyleOverrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( presetName );
2248 mCachedLayerStyleOverridesPresetName = presetName;
2249 }
2250
2251 return mCachedPresetLayerStyleOverrides;
2252 }
2253 else
2254 return QMap<QString, QString>();
2255 }
2256 else if ( mKeepLayerStyles )
2257 {
2258 return mLayerStyleOverrides;
2259 }
2260 else
2261 {
2262 return QMap<QString, QString>();
2263 }
2264}
2265
2266QgsRectangle QgsLayoutItemMap::transformedExtent() const
2267{
2268 double dx = mXOffset;
2269 double dy = mYOffset;
2270 transformShift( dx, dy );
2271 return QgsRectangle( mExtent.xMinimum() - dx, mExtent.yMinimum() - dy, mExtent.xMaximum() - dx, mExtent.yMaximum() - dy );
2272}
2273
2274void QgsLayoutItemMap::mapPolygon( const QgsRectangle &extent, QPolygonF &poly ) const
2275{
2276 poly.clear();
2277 if ( qgsDoubleNear( mEvaluatedMapRotation, 0.0 ) )
2278 {
2279 poly << QPointF( extent.xMinimum(), extent.yMaximum() );
2280 poly << QPointF( extent.xMaximum(), extent.yMaximum() );
2281 poly << QPointF( extent.xMaximum(), extent.yMinimum() );
2282 poly << QPointF( extent.xMinimum(), extent.yMinimum() );
2283 //ensure polygon is closed by readding first point
2284 poly << QPointF( poly.at( 0 ) );
2285 return;
2286 }
2287
2288 //there is rotation
2289 QgsPointXY rotationPoint( ( extent.xMaximum() + extent.xMinimum() ) / 2.0, ( extent.yMaximum() + extent.yMinimum() ) / 2.0 );
2290 double dx, dy; //x-, y- shift from rotation point to corner point
2291
2292 //top left point
2293 dx = rotationPoint.x() - extent.xMinimum();
2294 dy = rotationPoint.y() - extent.yMaximum();
2295 QgsLayoutUtils::rotate( mEvaluatedMapRotation, dx, dy );
2296 poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2297
2298 //top right point
2299 dx = rotationPoint.x() - extent.xMaximum();
2300 dy = rotationPoint.y() - extent.yMaximum();
2301 QgsLayoutUtils::rotate( mEvaluatedMapRotation, dx, dy );
2302 poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2303
2304 //bottom right point
2305 dx = rotationPoint.x() - extent.xMaximum();
2306 dy = rotationPoint.y() - extent.yMinimum();
2307 QgsLayoutUtils::rotate( mEvaluatedMapRotation, dx, dy );
2308 poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2309
2310 //bottom left point
2311 dx = rotationPoint.x() - extent.xMinimum();
2312 dy = rotationPoint.y() - extent.yMinimum();
2313 QgsLayoutUtils::rotate( mEvaluatedMapRotation, dx, dy );
2314 poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2315
2316 //ensure polygon is closed by readding first point
2317 poly << QPointF( poly.at( 0 ) );
2318}
2319
2320void QgsLayoutItemMap::transformShift( double &xShift, double &yShift ) const
2321{
2322 double mmToMapUnits = 1.0 / mapUnitsToLayoutUnits();
2323 double dxScaled = xShift * mmToMapUnits;
2324 double dyScaled = - yShift * mmToMapUnits;
2325
2326 QgsLayoutUtils::rotate( mEvaluatedMapRotation, dxScaled, dyScaled );
2327
2328 xShift = dxScaled;
2329 yShift = dyScaled;
2330}
2331
2332void QgsLayoutItemMap::drawAnnotations( QPainter *painter )
2333{
2334 if ( !mLayout || !mLayout->project() || !mDrawAnnotations )
2335 {
2336 return;
2337 }
2338
2339 const QList< QgsAnnotation * > annotations = mLayout->project()->annotationManager()->annotations();
2340 if ( annotations.isEmpty() )
2341 return;
2342
2344 rc.setForceVectorOutput( true );
2346 QList< QgsMapLayer * > layers = layersToRender( &rc.expressionContext() );
2347
2348 for ( QgsAnnotation *annotation : annotations )
2349 {
2350 if ( !annotation || !annotation->isVisible() )
2351 {
2352 continue;
2353 }
2354 if ( annotation->mapLayer() && !layers.contains( annotation->mapLayer() ) )
2355 continue;
2356
2357 drawAnnotation( annotation, rc );
2358 }
2359}
2360
2361void QgsLayoutItemMap::drawAnnotation( const QgsAnnotation *annotation, QgsRenderContext &context )
2362{
2363 if ( !annotation || !annotation->isVisible() || !context.painter() || !context.painter()->device() )
2364 {
2365 return;
2366 }
2367
2368 QgsScopedQPainterState painterState( context.painter() );
2370
2371 double itemX, itemY;
2372 if ( annotation->hasFixedMapPosition() )
2373 {
2374 QPointF mapPos = layoutMapPosForItem( annotation );
2375 itemX = mapPos.x();
2376 itemY = mapPos.y();
2377 }
2378 else
2379 {
2380 itemX = annotation->relativePosition().x() * rect().width();
2381 itemY = annotation->relativePosition().y() * rect().height();
2382 }
2383 context.painter()->translate( itemX, itemY );
2384
2385 //setup painter scaling to dots so that symbology is drawn to scale
2386 double dotsPerMM = context.painter()->device()->logicalDpiX() / 25.4;
2387 context.painter()->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
2388
2389 annotation->render( context );
2390}
2391
2392QPointF QgsLayoutItemMap::layoutMapPosForItem( const QgsAnnotation *annotation ) const
2393{
2394 if ( !annotation )
2395 return QPointF( 0, 0 );
2396
2397 double mapX = 0.0;
2398 double mapY = 0.0;
2399
2400 mapX = annotation->mapPosition().x();
2401 mapY = annotation->mapPosition().y();
2402 QgsCoordinateReferenceSystem annotationCrs = annotation->mapPositionCrs();
2403
2404 if ( annotationCrs != crs() )
2405 {
2406 //need to reproject
2407 QgsCoordinateTransform t( annotationCrs, crs(), mLayout->project() );
2408 double z = 0.0;
2409 try
2410 {
2411 t.transformInPlace( mapX, mapY, z );
2412 }
2413 catch ( const QgsCsException & )
2414 {
2415 }
2416 }
2417
2418 return mapToItemCoords( QPointF( mapX, mapY ) );
2419}
2420
2421void QgsLayoutItemMap::drawMapFrame( QPainter *p )
2422{
2423 if ( frameEnabled() && p )
2424 {
2427
2429 }
2430}
2431
2432void QgsLayoutItemMap::drawMapBackground( QPainter *p )
2433{
2434 if ( hasBackground() && p )
2435 {
2438
2440 }
2441}
2442
2443bool QgsLayoutItemMap::shouldDrawPart( QgsLayoutItemMap::PartType part ) const
2444{
2445 if ( mCurrentExportPart == NotLayered )
2446 {
2447 //all parts of the map are visible
2448 return true;
2449 }
2450
2451 switch ( part )
2452 {
2453 case NotLayered:
2454 return true;
2455
2456 case Start:
2457 return false;
2458
2459 case Background:
2460 return mCurrentExportPart == Background && hasBackground();
2461
2462 case Layer:
2463 return mCurrentExportPart == Layer;
2464
2465 case Grid:
2466 return mCurrentExportPart == Grid && mGridStack->hasEnabledItems();
2467
2468 case OverviewMapExtent:
2469 return mCurrentExportPart == OverviewMapExtent && mOverviewStack->hasEnabledItems();
2470
2471 case Frame:
2472 return mCurrentExportPart == Frame && frameEnabled();
2473
2474 case SelectionBoxes:
2475 return mCurrentExportPart == SelectionBoxes && isSelected();
2476
2477 case End:
2478 return false;
2479 }
2480
2481 return false;
2482}
2483
2484void QgsLayoutItemMap::refreshMapExtents( const QgsExpressionContext *context )
2485{
2486 QgsExpressionContext scopedContext;
2487 if ( !context )
2488 scopedContext = createExpressionContext();
2489
2490 bool ok = false;
2491 const QgsExpressionContext *evalContext = context ? context : &scopedContext;
2492
2493
2494 //data defined map extents set?
2495 QgsRectangle newExtent = extent();
2496 bool useDdXMin = false;
2497 bool useDdXMax = false;
2498 bool useDdYMin = false;
2499 bool useDdYMax = false;
2500 double minXD = 0;
2501 double minYD = 0;
2502 double maxXD = 0;
2503 double maxYD = 0;
2504
2505 minXD = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapXMin, *evalContext, 0.0, &ok );
2506 if ( ok )
2507 {
2508 useDdXMin = true;
2509 newExtent.setXMinimum( minXD );
2510 }
2511 minYD = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapYMin, *evalContext, 0.0, &ok );
2512 if ( ok )
2513 {
2514 useDdYMin = true;
2515 newExtent.setYMinimum( minYD );
2516 }
2517 maxXD = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapXMax, *evalContext, 0.0, &ok );
2518 if ( ok )
2519 {
2520 useDdXMax = true;
2521 newExtent.setXMaximum( maxXD );
2522 }
2523 maxYD = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapYMax, *evalContext, 0.0, &ok );
2524 if ( ok )
2525 {
2526 useDdYMax = true;
2527 newExtent.setYMaximum( maxYD );
2528 }
2529
2530 if ( newExtent != mExtent )
2531 {
2532 //calculate new extents to fit data defined extents
2533
2534 //Make sure the width/height ratio is the same as in current map extent.
2535 //This is to keep the map item frame and the page layout fixed
2536 double currentWidthHeightRatio = mExtent.width() / mExtent.height();
2537 double newWidthHeightRatio = newExtent.width() / newExtent.height();
2538
2539 if ( currentWidthHeightRatio < newWidthHeightRatio )
2540 {
2541 //enlarge height of new extent, ensuring the map center stays the same
2542 double newHeight = newExtent.width() / currentWidthHeightRatio;
2543 double deltaHeight = newHeight - newExtent.height();
2544 newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
2545 newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
2546 }
2547 else
2548 {
2549 //enlarge width of new extent, ensuring the map center stays the same
2550 double newWidth = currentWidthHeightRatio * newExtent.height();
2551 double deltaWidth = newWidth - newExtent.width();
2552 newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
2553 newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
2554 }
2555
2556 mExtent = newExtent;
2557 }
2558
2559 //now refresh scale, as this potentially overrides extents
2560
2561 //data defined map scale set?
2562 double scaleD = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapScale, *evalContext, 0.0, &ok );
2563 if ( ok )
2564 {
2565 setScale( scaleD, false );
2566 newExtent = mExtent;
2567 }
2568
2569 if ( useDdXMax || useDdXMin || useDdYMax || useDdYMin )
2570 {
2571 //if only one of min/max was set for either x or y, then make sure our extent is locked on that value
2572 //as we can do this without altering the scale
2573 if ( useDdXMin && !useDdXMax )
2574 {
2575 double xMax = mExtent.xMaximum() - ( mExtent.xMinimum() - minXD );
2576 newExtent.setXMinimum( minXD );
2577 newExtent.setXMaximum( xMax );
2578 }
2579 else if ( !useDdXMin && useDdXMax )
2580 {
2581 double xMin = mExtent.xMinimum() - ( mExtent.xMaximum() - maxXD );
2582 newExtent.setXMinimum( xMin );
2583 newExtent.setXMaximum( maxXD );
2584 }
2585 if ( useDdYMin && !useDdYMax )
2586 {
2587 double yMax = mExtent.yMaximum() - ( mExtent.yMinimum() - minYD );
2588 newExtent.setYMinimum( minYD );
2589 newExtent.setYMaximum( yMax );
2590 }
2591 else if ( !useDdYMin && useDdYMax )
2592 {
2593 double yMin = mExtent.yMinimum() - ( mExtent.yMaximum() - maxYD );
2594 newExtent.setYMinimum( yMin );
2595 newExtent.setYMaximum( maxYD );
2596 }
2597
2598 if ( newExtent != mExtent )
2599 {
2600 mExtent = newExtent;
2601 }
2602 }
2603
2604 //lastly, map rotation overrides all
2605 double mapRotation = mMapRotation;
2606
2607 //data defined map rotation set?
2609
2610 if ( !qgsDoubleNear( mEvaluatedMapRotation, mapRotation ) )
2611 {
2612 mEvaluatedMapRotation = mapRotation;
2614 }
2615}
2616
2617void QgsLayoutItemMap::refreshLabelMargin( bool updateItem )
2618{
2619 //data defined label margin set?
2621 mEvaluatedLabelMargin.setLength( labelMargin );
2622 mEvaluatedLabelMargin.setUnits( mLabelMargin.units() );
2623
2624 if ( updateItem )
2625 {
2626 update();
2627 }
2628}
2629
2630void QgsLayoutItemMap::updateAtlasFeature()
2631{
2632 if ( !atlasDriven() || !mLayout->reportContext().layer() )
2633 return; // nothing to do
2634
2635 QgsRectangle bounds = computeAtlasRectangle();
2636 if ( bounds.isNull() )
2637 return;
2638
2639 double xa1 = bounds.xMinimum();
2640 double xa2 = bounds.xMaximum();
2641 double ya1 = bounds.yMinimum();
2642 double ya2 = bounds.yMaximum();
2643 QgsRectangle newExtent = bounds;
2644 QgsRectangle originalExtent = mExtent;
2645
2646 //sanity check - only allow fixed scale mode for point layers
2647 bool isPointLayer = QgsWkbTypes::geometryType( mLayout->reportContext().layer()->wkbType() ) == Qgis::GeometryType::Point;
2648
2649 if ( mAtlasScalingMode == Fixed || mAtlasScalingMode == Predefined || isPointLayer )
2650 {
2651 QgsScaleCalculator calc;
2652 calc.setMapUnits( crs().mapUnits() );
2653 calc.setDpi( 25.4 );
2654 double originalScale = calc.calculate( originalExtent, rect().width() );
2655 double geomCenterX = ( xa1 + xa2 ) / 2.0;
2656 double geomCenterY = ( ya1 + ya2 ) / 2.0;
2657 QVector<qreal> scales;
2659 if ( !mLayout->reportContext().predefinedScales().empty() ) // remove when deprecated method is removed
2660 scales = mLayout->reportContext().predefinedScales();
2661 else
2662 scales = mLayout->renderContext().predefinedScales();
2664 if ( mAtlasScalingMode == Fixed || scales.isEmpty() || ( isPointLayer && mAtlasScalingMode != Predefined ) )
2665 {
2666 // only translate, keep the original scale (i.e. width x height)
2667 double xMin = geomCenterX - originalExtent.width() / 2.0;
2668 double yMin = geomCenterY - originalExtent.height() / 2.0;
2669 newExtent = QgsRectangle( xMin,
2670 yMin,
2671 xMin + originalExtent.width(),
2672 yMin + originalExtent.height() );
2673
2674 //scale newExtent to match original scale of map
2675 //this is required for geographic coordinate systems, where the scale varies by extent
2676 double newScale = calc.calculate( newExtent, rect().width() );
2677 newExtent.scale( originalScale / newScale );
2678 }
2679 else if ( mAtlasScalingMode == Predefined )
2680 {
2681 // choose one of the predefined scales
2682 double newWidth = originalExtent.width();
2683 double newHeight = originalExtent.height();
2684 for ( int i = 0; i < scales.size(); i++ )
2685 {
2686 double ratio = scales[i] / originalScale;
2687 newWidth = originalExtent.width() * ratio;
2688 newHeight = originalExtent.height() * ratio;
2689
2690 // compute new extent, centered on feature
2691 double xMin = geomCenterX - newWidth / 2.0;
2692 double yMin = geomCenterY - newHeight / 2.0;
2693 newExtent = QgsRectangle( xMin,
2694 yMin,
2695 xMin + newWidth,
2696 yMin + newHeight );
2697
2698 //scale newExtent to match desired map scale
2699 //this is required for geographic coordinate systems, where the scale varies by extent
2700 double newScale = calc.calculate( newExtent, rect().width() );
2701 newExtent.scale( scales[i] / newScale );
2702
2703 if ( ( newExtent.width() >= bounds.width() ) && ( newExtent.height() >= bounds.height() ) )
2704 {
2705 // this is the smallest extent that embeds the feature, stop here
2706 break;
2707 }
2708 }
2709 }
2710 }
2711 else if ( mAtlasScalingMode == Auto )
2712 {
2713 // auto scale
2714
2715 double geomRatio = bounds.width() / bounds.height();
2716 double mapRatio = originalExtent.width() / originalExtent.height();
2717
2718 // geometry height is too big
2719 if ( geomRatio < mapRatio )
2720 {
2721 // extent the bbox's width
2722 double adjWidth = ( mapRatio * bounds.height() - bounds.width() ) / 2.0;
2723 xa1 -= adjWidth;
2724 xa2 += adjWidth;
2725 }
2726 // geometry width is too big
2727 else if ( geomRatio > mapRatio )
2728 {
2729 // extent the bbox's height
2730 double adjHeight = ( bounds.width() / mapRatio - bounds.height() ) / 2.0;
2731 ya1 -= adjHeight;
2732 ya2 += adjHeight;
2733 }
2734 newExtent = QgsRectangle( xa1, ya1, xa2, ya2 );
2735
2736 const double evaluatedAtlasMargin = atlasMargin();
2737 if ( evaluatedAtlasMargin > 0.0 )
2738 {
2739 newExtent.scale( 1 + evaluatedAtlasMargin );
2740 }
2741 }
2742
2743 // set the new extent (and render)
2744 setExtent( newExtent );
2745 emit preparedForAtlas();
2746}
2747
2748QgsRectangle QgsLayoutItemMap::computeAtlasRectangle()
2749{
2750 // QgsGeometry::boundingBox is expressed in the geometry"s native CRS
2751 // We have to transform the geometry to the destination CRS and ask for the bounding box
2752 // Note: we cannot directly take the transformation of the bounding box, since transformations are not linear
2753 QgsGeometry g = mLayout->reportContext().currentGeometry( crs() );
2754 // Rotating the geometry, so the bounding box is correct wrt map rotation
2755 if ( mEvaluatedMapRotation != 0.0 )
2756 {
2757 QgsPointXY prevCenter = g.boundingBox().center();
2758 g.rotate( mEvaluatedMapRotation, g.boundingBox().center() );
2759 // Rotation center will be still the bounding box center of an unrotated geometry.
2760 // Which means, if the center of bbox moves after rotation, the viewport will
2761 // also be offset, and part of the geometry will fall out of bounds.
2762 // Here we compensate for that roughly: by extending the rotated bounds
2763 // so that its center is the same as the original.
2764 QgsRectangle bounds = g.boundingBox();
2765 double dx = std::max( std::abs( prevCenter.x() - bounds.xMinimum() ),
2766 std::abs( prevCenter.x() - bounds.xMaximum() ) );
2767 double dy = std::max( std::abs( prevCenter.y() - bounds.yMinimum() ),
2768 std::abs( prevCenter.y() - bounds.yMaximum() ) );
2769 QgsPointXY center = g.boundingBox().center();
2770 return QgsRectangle( center.x() - dx, center.y() - dy,
2771 center.x() + dx, center.y() + dy );
2772 }
2773 else
2774 {
2775 return g.boundingBox();
2776 }
2777}
2778
2779void QgsLayoutItemMap::createStagedRenderJob( const QgsRectangle &extent, const QSizeF size, double dpi )
2780{
2781 QgsMapSettings settings = mapSettings( extent, size, dpi, true );
2782 settings.setLayers( mOverviewStack->modifyMapLayerList( settings.layers() ) );
2783
2784 mStagedRendererJob = std::make_unique< QgsMapRendererStagedRenderJob >( settings,
2787 : QgsMapRendererStagedRenderJob::Flags() );
2788 mStagedRendererJob->start();
2789}
2790
2791
2792
2793//
2794// QgsLayoutItemMapAtlasClippingSettings
2795//
2796
2798 : QObject( map )
2799 , mMap( map )
2800{
2801 if ( mMap->layout() && mMap->layout()->project() )
2802 {
2803 connect( mMap->layout()->project(), static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *>& layers ) > ( &QgsProject::layersWillBeRemoved ),
2804 this, &QgsLayoutItemMapAtlasClippingSettings::layersAboutToBeRemoved );
2805 }
2806}
2807
2809{
2810 return mClipToAtlasFeature;
2811}
2812
2814{
2815 if ( enabled == mClipToAtlasFeature )
2816 return;
2817
2818 mClipToAtlasFeature = enabled;
2819 emit changed();
2820}
2821
2823{
2824 return mFeatureClippingType;
2825}
2826
2828{
2829 if ( mFeatureClippingType == type )
2830 return;
2831
2832 mFeatureClippingType = type;
2833 emit changed();
2834}
2835
2837{
2838 return mForceLabelsInsideFeature;
2839}
2840
2842{
2843 if ( forceInside == mForceLabelsInsideFeature )
2844 return;
2845
2846 mForceLabelsInsideFeature = forceInside;
2847 emit changed();
2848}
2849
2851{
2852 return mRestrictToLayers;
2853}
2854
2856{
2857 if ( mRestrictToLayers == enabled )
2858 return;
2859
2860 mRestrictToLayers = enabled;
2861 emit changed();
2862}
2863
2865{
2866 return _qgis_listRefToRaw( mLayersToClip );
2867}
2868
2869void QgsLayoutItemMapAtlasClippingSettings::setLayersToClip( const QList< QgsMapLayer * > &layersToClip )
2870{
2871 mLayersToClip = _qgis_listRawToRef( layersToClip );
2872 emit changed();
2873}
2874
2875bool QgsLayoutItemMapAtlasClippingSettings::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
2876{
2877 QDomElement settingsElem = document.createElement( QStringLiteral( "atlasClippingSettings" ) );
2878 settingsElem.setAttribute( QStringLiteral( "enabled" ), mClipToAtlasFeature ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
2879 settingsElem.setAttribute( QStringLiteral( "forceLabelsInside" ), mForceLabelsInsideFeature ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
2880 settingsElem.setAttribute( QStringLiteral( "clippingType" ), QString::number( static_cast<int>( mFeatureClippingType ) ) );
2881 settingsElem.setAttribute( QStringLiteral( "restrictLayers" ), mRestrictToLayers ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
2882
2883 //layer set
2884 QDomElement layerSetElem = document.createElement( QStringLiteral( "layersToClip" ) );
2885 for ( const QgsMapLayerRef &layerRef : mLayersToClip )
2886 {
2887 if ( !layerRef )
2888 continue;
2889 QDomElement layerElem = document.createElement( QStringLiteral( "Layer" ) );
2890 QDomText layerIdText = document.createTextNode( layerRef.layerId );
2891 layerElem.appendChild( layerIdText );
2892
2893 layerElem.setAttribute( QStringLiteral( "name" ), layerRef.name );
2894 layerElem.setAttribute( QStringLiteral( "source" ), layerRef.source );
2895 layerElem.setAttribute( QStringLiteral( "provider" ), layerRef.provider );
2896
2897 layerSetElem.appendChild( layerElem );
2898 }
2899 settingsElem.appendChild( layerSetElem );
2900
2901 element.appendChild( settingsElem );
2902 return true;
2903}
2904
2905bool QgsLayoutItemMapAtlasClippingSettings::readXml( const QDomElement &element, const QDomDocument &, const QgsReadWriteContext & )
2906{
2907 const QDomElement settingsElem = element.firstChildElement( QStringLiteral( "atlasClippingSettings" ) );
2908
2909 mClipToAtlasFeature = settingsElem.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt();
2910 mForceLabelsInsideFeature = settingsElem.attribute( QStringLiteral( "forceLabelsInside" ), QStringLiteral( "0" ) ).toInt();
2911 mFeatureClippingType = static_cast< QgsMapClippingRegion::FeatureClippingType >( settingsElem.attribute( QStringLiteral( "clippingType" ), QStringLiteral( "0" ) ).toInt() );
2912 mRestrictToLayers = settingsElem.attribute( QStringLiteral( "restrictLayers" ), QStringLiteral( "0" ) ).toInt();
2913
2914 mLayersToClip.clear();
2915 QDomNodeList layerSetNodeList = settingsElem.elementsByTagName( QStringLiteral( "layersToClip" ) );
2916 if ( !layerSetNodeList.isEmpty() )
2917 {
2918 QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
2919 QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( QStringLiteral( "Layer" ) );
2920 mLayersToClip.reserve( layerIdNodeList.size() );
2921 for ( int i = 0; i < layerIdNodeList.size(); ++i )
2922 {
2923 QDomElement layerElem = layerIdNodeList.at( i ).toElement();
2924 QString layerId = layerElem.text();
2925 QString layerName = layerElem.attribute( QStringLiteral( "name" ) );
2926 QString layerSource = layerElem.attribute( QStringLiteral( "source" ) );
2927 QString layerProvider = layerElem.attribute( QStringLiteral( "provider" ) );
2928
2929 QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
2930 if ( mMap->layout() && mMap->layout()->project() )
2931 ref.resolveWeakly( mMap->layout()->project() );
2932 mLayersToClip << ref;
2933 }
2934 }
2935
2936 return true;
2937}
2938
2939void QgsLayoutItemMapAtlasClippingSettings::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
2940{
2941 if ( !mLayersToClip.isEmpty() )
2942 {
2943 _qgis_removeLayers( mLayersToClip, layers );
2944 }
2945}
2946
2947//
2948// QgsLayoutItemMapItemClipPathSettings
2949//
2951 : QObject( map )
2952 , mMap( map )
2953{
2954}
2955
2957{
2958 return mEnabled && mClipPathSource;
2959}
2960
2962{
2963 return mEnabled;
2964}
2965
2967{
2968 if ( enabled == mEnabled )
2969 return;
2970
2971 mEnabled = enabled;
2972
2973 if ( mClipPathSource )
2974 {
2975 // may need to refresh the clip source in order to get it to render/not render depending on enabled state
2976 mClipPathSource->refresh();
2977 }
2978 emit changed();
2979}
2980
2982{
2983 if ( isActive() )
2984 {
2985 QgsGeometry clipGeom( mClipPathSource->clipPath() );
2986 clipGeom.transform( mMap->layoutToMapCoordsTransform() );
2987 return clipGeom;
2988 }
2989 return QgsGeometry();
2990}
2991
2993{
2994 if ( isActive() )
2995 {
2996 QgsGeometry clipGeom( mClipPathSource->clipPath() );
2997 clipGeom.transform( mMap->sceneTransform().inverted() );
2998 return clipGeom;
2999 }
3000 return QgsGeometry();
3001}
3002
3004{
3006 region.setFeatureClip( mFeatureClippingType );
3007 return region;
3008}
3009
3011{
3012 if ( mClipPathSource == item )
3013 return;
3014
3015 if ( mClipPathSource )
3016 {
3017 disconnect( mClipPathSource, &QgsLayoutItem::clipPathChanged, mMap, &QgsLayoutItemMap::refresh );
3018 disconnect( mClipPathSource, &QgsLayoutItem::rotationChanged, mMap, &QgsLayoutItemMap::refresh );
3019 disconnect( mClipPathSource, &QgsLayoutItem::clipPathChanged, mMap, &QgsLayoutItemMap::extentChanged );
3020 disconnect( mClipPathSource, &QgsLayoutItem::rotationChanged, mMap, &QgsLayoutItemMap::extentChanged );
3021 }
3022
3023 QgsLayoutItem *oldItem = mClipPathSource;
3024 mClipPathSource = item;
3025
3026 if ( mClipPathSource )
3027 {
3028 // if item size or rotation changes, we need to redraw this map
3029 connect( mClipPathSource, &QgsLayoutItem::clipPathChanged, mMap, &QgsLayoutItemMap::refresh );
3030 connect( mClipPathSource, &QgsLayoutItem::rotationChanged, mMap, &QgsLayoutItemMap::refresh );
3031 // and if clip item size or rotation changes, then effectively we've changed the visible extent of the map
3032 connect( mClipPathSource, &QgsLayoutItem::clipPathChanged, mMap, &QgsLayoutItemMap::extentChanged );
3033 connect( mClipPathSource, &QgsLayoutItem::rotationChanged, mMap, &QgsLayoutItemMap::extentChanged );
3034 // trigger a redraw of the clip source, so that it becomes invisible
3035 mClipPathSource->refresh();
3036 }
3037
3038 if ( oldItem )
3039 {
3040 // may need to refresh the previous item in order to get it to render
3041 oldItem->refresh();
3042 }
3043
3044 emit changed();
3045}
3046
3048{
3049 return mClipPathSource;
3050}
3051
3053{
3054 return mFeatureClippingType;
3055}
3056
3058{
3059 if ( mFeatureClippingType == type )
3060 return;
3061
3062 mFeatureClippingType = type;
3063 emit changed();
3064}
3065
3067{
3068 return mForceLabelsInsideClipPath;
3069}
3070
3072{
3073 if ( forceInside == mForceLabelsInsideClipPath )
3074 return;
3075
3076 mForceLabelsInsideClipPath = forceInside;
3077 emit changed();
3078}
3079
3080bool QgsLayoutItemMapItemClipPathSettings::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
3081{
3082 QDomElement settingsElem = document.createElement( QStringLiteral( "itemClippingSettings" ) );
3083 settingsElem.setAttribute( QStringLiteral( "enabled" ), mEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
3084 settingsElem.setAttribute( QStringLiteral( "forceLabelsInside" ), mForceLabelsInsideClipPath ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
3085 settingsElem.setAttribute( QStringLiteral( "clippingType" ), QString::number( static_cast<int>( mFeatureClippingType ) ) );
3086 if ( mClipPathSource )
3087 settingsElem.setAttribute( QStringLiteral( "clipSource" ), mClipPathSource->uuid() );
3088 else
3089 settingsElem.setAttribute( QStringLiteral( "clipSource" ), QString() );
3090
3091 element.appendChild( settingsElem );
3092 return true;
3093}
3094
3095bool QgsLayoutItemMapItemClipPathSettings::readXml( const QDomElement &element, const QDomDocument &, const QgsReadWriteContext & )
3096{
3097 const QDomElement settingsElem = element.firstChildElement( QStringLiteral( "itemClippingSettings" ) );
3098
3099 mEnabled = settingsElem.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt();
3100 mForceLabelsInsideClipPath = settingsElem.attribute( QStringLiteral( "forceLabelsInside" ), QStringLiteral( "0" ) ).toInt();
3101 mFeatureClippingType = static_cast< QgsMapClippingRegion::FeatureClippingType >( settingsElem.attribute( QStringLiteral( "clippingType" ), QStringLiteral( "0" ) ).toInt() );
3102 mClipPathUuid = settingsElem.attribute( QStringLiteral( "clipSource" ) );
3103
3104 return true;
3105}
3106
3108{
3109 if ( !mClipPathUuid.isEmpty() )
3110 {
3111 if ( QgsLayoutItem *item = mMap->layout()->itemByUuid( mClipPathUuid, true ) )
3112 {
3113 setSourceItem( item );
3114 }
3115 }
3116}
@ CollectUnplacedLabels
Whether unplaced labels should be collected in the labeling results (regardless of whether they are b...
@ DrawUnplacedLabels
Whether to render unplaced labels as an indicator/warning for users.
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
@ Export
Renderer used for printing or exporting to a file.
@ View
Renderer used for displaying on screen.
@ DrawEditingInfo
Enable drawing of vertex markers for layers in editing mode.
@ UseRenderingOptimization
Enable vector simplification and other rendering optimizations.
@ ForceVectorOutput
Vector graphics should not be cached and drawn as raster images.
@ RenderPartialOutput
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
@ ForceRasterMasks
Force symbol masking to be applied using a raster method. This is considerably faster when compared t...
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ DrawSelection
Whether vector selections should be shown in the rendered map.
@ Antialiasing
Enable anti-aliasing for map rendering.
@ UseAdvancedEffects
Enable layer opacity and blending effects.
@ HighQualityImageTransforms
Enable high quality image transformations, which results in better appearance of scaled or rotated ra...
virtual QPainterPath asQPainterPath() const =0
Returns the geometry represented as a QPainterPath.
QDateTime valueAsDateTime(int key, const QgsExpressionContext &context, const QDateTime &defaultDateTime=QDateTime(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a datetime.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
Abstract base class for annotation items which are drawn over a map.
Definition: qgsannotation.h:54
bool hasFixedMapPosition
Definition: qgsannotation.h:72
QgsCoordinateReferenceSystem mapPositionCrs() const
Returns the CRS of the map position, or an invalid CRS if the annotation does not have a fixed map po...
QgsPointXY mapPosition
Definition: qgsannotation.h:73
void render(QgsRenderContext &context) const
Renders the annotation to a target render context.
bool isVisible() const
Returns true if the annotation is visible and should be rendered.
Definition: qgsannotation.h:95
QPointF relativePosition() const
Returns the relative position of the annotation, if it is not attached to a fixed map position.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsCoordinateReferenceSystemRegistry * coordinateReferenceSystemRegistry()
Returns the application's coordinate reference system (CRS) registry, which handles known CRS definit...
void userCrsChanged(const QString &id)
Emitted whenever an existing user CRS definition is changed.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Q_GADGET Qgis::DistanceUnit mapUnits
QString toProj() const
Returns a Proj string representation of this CRS.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
void updateDefinition()
Updates the definition and parameters of the coordinate reference system to their latest values.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addFunction(const QString &name, QgsScopedExpressionFunction *function)
Adds a function to the scope.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
static QgsGeometry fromPointXY(const QgsPointXY &point) SIP_HOLDGIL
Creates a new geometry from a QgsPointXY object.
QPolygonF asQPolygonF() const SIP_HOLDGIL
Returns contents of the geometry as a QPolygonF.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
A representation of the interval between two datetime values.
Definition: qgsinterval.h:42
Label blocking region (in map coordinates and CRS).
Stores global configuration for labeling engine.
void setFlag(Qgis::LabelingFlag f, bool enabled=true)
Sets whether a particual flag is enabled.
Class that stores computed placement from labeling engine.
void layerOrderChanged()
Emitted when the layer order has changed.
Contains settings relating to clipping a layout map by the current atlas feature.
void setFeatureClippingType(QgsMapClippingRegion::FeatureClippingType type)
Sets the feature clipping type to apply when clipping to the current atlas feature.
bool restrictToLayers() const
Returns true if clipping should be restricted to a subset of layers.
QgsLayoutItemMapAtlasClippingSettings(QgsLayoutItemMap *map=nullptr)
Constructor for QgsLayoutItemMapAtlasClippingSettings, with the specified map parent.
bool readXml(const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context)
Sets the setting's state from a DOM document, where element is the DOM node corresponding to a 'Layou...
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Stores settings in a DOM element, where element is the DOM element corresponding to a 'LayoutMap' tag...
void setRestrictToLayers(bool enabled)
Sets whether clipping should be restricted to a subset of layers.
void setLayersToClip(const QList< QgsMapLayer * > &layers)
Sets the list of map layers to clip to the atlas feature.
QList< QgsMapLayer * > layersToClip() const
Returns the list of map layers to clip to the atlas feature.
void setEnabled(bool enabled)
Sets whether the map content should be clipped to the current atlas feature.
void changed()
Emitted when the atlas clipping settings are changed.
bool forceLabelsInsideFeature() const
Returns true if labels should only be placed inside the atlas feature geometry.
bool enabled() const
Returns true if the map content should be clipped to the current atlas feature.
void setForceLabelsInsideFeature(bool forceInside)
Sets whether labels should only be placed inside the atlas feature geometry.
QgsMapClippingRegion::FeatureClippingType featureClippingType() const
Returns the feature clipping type to apply when clipping to the current atlas feature.
An individual grid which is drawn above the map content in a QgsLayoutItemMap.
Contains settings relating to clipping a layout map by another layout item.
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Stores settings in a DOM element, where element is the DOM element corresponding to a 'LayoutMap' tag...
void setForceLabelsInsideClipPath(bool forceInside)
Sets whether labels should only be placed inside the clip path geometry.
void setSourceItem(QgsLayoutItem *item)
Sets the source item which will provide the clipping path for the map.
QgsLayoutItemMapItemClipPathSettings(QgsLayoutItemMap *map=nullptr)
Constructor for QgsLayoutItemMapItemClipPathSettings, with the specified map parent.
bool readXml(const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context)
Sets the setting's state from a DOM document, where element is the DOM node corresponding to a 'Layou...
QgsGeometry clipPathInMapItemCoordinates() const
Returns the clipping path geometry, in the map item's coordinate space.
QgsGeometry clippedMapExtent() const
Returns the geometry to use for clipping the parent map, in the map item's CRS.
QgsLayoutItem * sourceItem()
Returns the source item which will provide the clipping path for the map, or nullptr if no item is se...
void setEnabled(bool enabled)
Sets whether the map content should be clipped to the associated item.
bool forceLabelsInsideClipPath() const
Returns true if labels should only be placed inside the clip path geometry.
void finalizeRestoreFromXml()
To be called after all pending items have been restored from XML.
QgsMapClippingRegion::FeatureClippingType featureClippingType() const
Returns the feature clipping type to apply when clipping to the associated item.
bool enabled() const
Returns true if the map content should be clipped to the associated item.
QgsMapClippingRegion toMapClippingRegion() const
Returns the clip path as a map clipping region.
void changed()
Emitted when the item clipping settings are changed.
void setFeatureClippingType(QgsMapClippingRegion::FeatureClippingType type)
Sets the feature clipping type to apply when clipping to the associated item.
bool isActive() const
Returns true if the item clipping is enabled and set to a valid source item.
An item which is drawn inside a QgsLayoutItemMap, e.g., a grid or map overview.
@ StackAboveMapLabels
Render above all map layers and labels.
StackingPosition stackingPosition() const
Returns the item's stacking position, which specifies where the in the map's stack the item should be...
bool enabled() const
Returns whether the item will be drawn.
An individual overview which is drawn above the map content in a QgsLayoutItemMap,...
Layout graphical items for displaying a map.
void setFollowVisibilityPreset(bool follow)
Sets whether the map should follow a map theme.
bool nextExportPart() override
Moves to the next export part for a multi-layered export item, during a multi-layered export.
void removeRenderedFeatureHandler(QgsRenderedFeatureHandlerInterface *handler)
Removes a previously added rendered feature handler.
void extentChanged()
Emitted when the map's extent changes.
QPointF mapToItemCoords(QPointF mapCoords) const
Transforms map coordinates to item coordinates (considering rotation and move offset)
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
~QgsLayoutItemMap() override
QIcon icon() const override
Returns the item's icon.
void preparedForAtlas()
Emitted when the map has been prepared for atlas rendering, just before actual rendering.
void setFollowVisibilityPresetName(const QString &name)
Sets preset name for map rendering.
QTransform layoutToMapCoordsTransform() const
Creates a transform from layout coordinates to map coordinates.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
QgsMapSettings mapSettings(const QgsRectangle &extent, QSizeF size, double dpi, bool includeLayerSettings) const
Returns map settings that will be used for drawing of the map.
bool isLabelBlockingItem(QgsLayoutItem *item) const
Returns true if the specified item is a "label blocking item".
void storeCurrentLayerStyles()
Stores the current project layer styles into style overrides.
void setAtlasDriven(bool enabled)
Sets whether the map extent will follow the current atlas feature.
QgsLayoutMeasurement labelMargin() const
Returns the margin from the map edges in which no labels may be placed.
AtlasScalingMode
Scaling modes used for the serial rendering (atlas)
@ Predefined
A scale is chosen from the predefined scales.
@ Auto
The extent is adjusted so that each feature is fully visible.
@ Fixed
The current scale of the map is used for each feature of the atlas.
bool requiresRasterization() const override
Returns true if the item is drawn in such a way that forces the whole layout to be rasterized when ex...
Q_DECL_DEPRECATED int numberExportLayers() const override
Returns the number of layers that this item requires for exporting during layered exports (e....
void layerStyleOverridesChanged()
Emitted when layer style overrides are changed... a means to let associated legend items know they sh...
void updateBoundingRect()
Updates the bounding rect of this item. Call this function before doing any changes related to annota...
void moveContent(double dx, double dy) override
Moves the content of the item, by a specified dx and dy in layout units.
QgsLayoutItemMapGrid * grid()
Returns the map item's first grid.
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
int type() const override
void previewRefreshed()
Emitted whenever the item's map preview has been refreshed.
friend class QgsLayoutItemMapOverview
void setExtent(const QgsRectangle &extent)
Sets a new extent for the map.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation.
QgsRectangle requestedExtent() const
Calculates the extent to request and the yShift of the top-left point in case of rotation.
QgsLayoutItemMap(QgsLayout *layout)
Constructor for QgsLayoutItemMap, with the specified parent layout.
void setMapFlags(QgsLayoutItemMap::MapItemFlags flags)
Sets the map item's flags, which control how the map content is drawn.
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
void zoomContent(double factor, QPointF point) override
Zooms content of item.
QList< QgsMapLayer * > layersToRender(const QgsExpressionContext *context=nullptr) const
Returns a list of the layers which will be rendered within this map item, considering any locked laye...
void crsChanged()
Emitted when the map's coordinate reference system is changed.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the stored layers set.
QPolygonF transformedMapPolygon() const
Returns extent that considers rotation and shift with mOffsetX / mOffsetY.
static QgsLayoutItemMap * create(QgsLayout *layout)
Returns a new map item for the specified layout.
QRectF boundingRect() const override
QString displayName() const override
Gets item display name.
bool atlasDriven() const
Returns whether the map extent is set to follow the current atlas feature.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the stored overrides of styles for layers.
void stopLayeredExport() override
Stops a multi-layer export operation.
void addRenderedFeatureHandler(QgsRenderedFeatureHandlerInterface *handler)
Adds a rendered feature handler to use while rendering the map.
QPainterPath framePath() const override
Returns the path to use when drawing the item's frame or background.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void startLayeredExport() override
Starts a multi-layer export operation.
bool containsWmsLayer() const
Returns true if the map contains a WMS layer.
void setScale(double scale, bool forceUpdate=true)
Sets new map scale and changes only the map extent.
void refresh() override
double mapRotation(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the layout item, in degrees clockwise.
double mapUnitsToLayoutUnits() const
Returns the conversion factor from map units to layout units.
QgsLayoutItemMap::MapItemFlags mapFlags() const
Returns the map item's flags, which control how the map content is drawn.
void setLabelMargin(const QgsLayoutMeasurement &margin)
Sets the margin from the map edges in which no labels may be placed.
void themeChanged(const QString &theme)
Emitted when the map's associated theme is changed.
QgsLayoutItem::ExportLayerDetail exportLayerDetails() const override
Returns the details for the specified current export layer.
void zoomToExtent(const QgsRectangle &extent)
Zooms the map so that the specified extent is fully visible within the map item.
double scale() const
Returns the map scale.
@ ShowPartialLabels
Whether to draw labels which are partially outside of the map view.
@ ShowUnplacedLabels
Whether to render unplaced labels in the map view.
bool drawAnnotations() const
Returns whether annotations are drawn within the map.
void removeLabelBlockingItem(QgsLayoutItem *item)
Removes the specified layout item from the map's "label blocking items".
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the map's preset crs (coordinate reference system).
void invalidateCache() override
QgsRectangle extent() const
Returns the current map extent.
QgsLayoutItemMapOverview * overview()
Returns the map item's first overview.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
void setFrameStrokeWidth(QgsLayoutMeasurement width) override
Sets the frame stroke width.
bool containsAdvancedEffects() const override
Returns true if the item contains contents with blend modes or transparency effects which can only be...
friend class QgsLayoutItemMapGrid
QgsLayoutItem::Flags itemFlags() const override
Returns the item's flags, which indicate how the item behaves.
QList< QgsMapLayer * > layers() const
Returns the stored layer set.
void setMoveContentPreviewOffset(double dx, double dy) override
Sets temporary offset for the item, by a specified dx and dy in layout units.
void setMapRotation(double rotation)
Sets the rotation for the map - this does not affect the layout item shape, only the way the map is d...
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
double atlasMargin(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue)
Returns the margin size (percentage) used when the map is in atlas mode.
void addLabelBlockingItem(QgsLayoutItem *item)
Sets the specified layout item as a "label blocking item" for this map.
void assignFreeId()
Sets the map id() to a number not yet used in the layout.
ExportLayerBehavior exportLayerBehavior() const override
Returns the behavior of this item during exporting to layered exports (e.g.
QgsLabelingResults * previewLabelingResults() const
Returns the labeling results of the most recent preview map render.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:44
Base class for graphical items within a QgsLayout.
virtual void drawFrame(QgsRenderContext &context)
Draws the frame around the item.
virtual QPainterPath framePath() const
Returns the path to use when drawing the item's frame or background.
virtual void setFrameStrokeWidth(QgsLayoutMeasurement width)
Sets the frame stroke width.
void rotationChanged(double newRotation)
Emitted on item rotation change.
QColor backgroundColor() const
Returns the background color for this item.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual void drawBackground(QgsRenderContext &context)
Draws the background for the item.
virtual bool requiresRasterization() const
Returns true if the item is drawn in such a way that forces the whole layout to be rasterized when ex...
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties)
Refreshes a data defined property for the item by reevaluating the property's value and redrawing the...
@ FlagOverridesPaint
Item overrides the default layout item painting method.
virtual bool containsAdvancedEffects() const
Returns true if the item contains contents with blend modes or transparency effects which can only be...
void sizePositionChanged()
Emitted when the item's size or position changes.
virtual QString uuid() const
Returns the item identification string.
QString id() const
Returns the item's ID name.
bool frameEnabled() const
Returns true if the item includes a frame.
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
@ ItemContainsSubLayers
Item contains multiple sublayers which must be individually exported.
void clipPathChanged()
Emitted when the item's clipping path has changed.
bool hasBackground() const
Returns true if the item has a background.
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
void attemptSetSceneRect(const QRectF &rect, bool includesFrame=false)
Attempts to update the item's position and size to match the passed rect in layout coordinates.
void backgroundTaskCountChanged(int count)
Emitted whenever the number of background tasks an item is executing changes.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
void setLength(const double length)
Sets the length of the measurement.
static QgsLayoutMeasurement decodeMeasurement(const QString &string)
Decodes a measurement from a string.
QString encodeMeasurement() const
Encodes the layout measurement to a string.
Qgis::LayoutUnit units() const
Returns the units for the measurement.
void setUnits(const Qgis::LayoutUnit units)
Sets the units for the measurement.
double length() const
Returns the length of the measurement.
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
QPointer< QgsLayout > mLayout
DataDefinedProperty
Data defined properties for different item types.
@ MapYMin
Map extent y minimum.
@ MapStylePreset
Layer and style map theme.
@ MapXMax
Map extent x maximum.
@ StartDateTime
Temporal range's start DateTime.
@ AllProperties
All properties for item.
@ EndDateTime
Temporal range's end DateTime.
@ MapAtlasMargin
Map atlas margin.
@ MapYMax
Map extent y maximum.
@ MapXMin
Map extent x minimum.
@ MapScale
Map scale.
@ MapLabelMargin
Map label margin.
@ MapLayers
Map layer set.
@ MapRotation
Map rotation.
PropertyValueType
Specifies whether the value returned by a function should be the original, user set value,...
@ EvaluatedValue
Return the current evaluated value for the property.
void predefinedScalesChanged()
Emitted when the list of predefined scales changes.
@ FlagRenderLabelsByMapLayer
When rendering map items to multi-layered exports, render labels belonging to different layers into s...
@ FlagUseAdvancedEffects
Enable advanced effects such as blend modes.
@ FlagDrawSelection
Draw selection.
@ FlagLosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ FlagAntialiasing
Use antialiasing when drawing items.
@ FlagForceVectorOutput
Force output in vector format where possible, even if items require rasterization to keep their corre...
@ FlagHideCoverageLayer
Hide coverage layer in outputs.
@ FlagDisableTiledRasterLayerRenders
If set, then raster layers will not be drawn as separate tiles. This may improve the appearance in ex...
static QgsRenderContext createRenderContextForMap(QgsLayoutItemMap *map, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout map and painter destination.
static void rotate(double angle, double &x, double &y)
Rotates a point / vector around the origin.
static Q_DECL_DEPRECATED double scaleFactorFromItemStyle(const QStyleOptionGraphicsItem *style)
Extracts the scale factor from an item style.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50
QgsLayoutItem * itemByUuid(const QString &uuid, bool includeTemplateUuids=false) const
Returns the layout item with matching uuid unique identifier, or nullptr if a matching item could not...
Definition: qgslayout.cpp:244
void refreshed()
Emitted when the layout has been refreshed and items should also be refreshed and updated.
QgsProject * project() const
The project associated with the layout.
Definition: qgslayout.cpp:138
A map clipping region (in map coordinates and CRS).
void setRestrictToLayers(bool enabled)
Sets whether clipping should be restricted to a subset of layers.
FeatureClippingType
Feature clipping behavior, which controls how features from vector layers will be clipped.
void setFeatureClip(FeatureClippingType type)
Sets the feature clipping type.
void setRestrictedLayers(const QList< QgsMapLayer * > &layers)
Sets a list of layers to restrict the clipping region effects to.
Stores style information (renderer, opacity, labeling, diagrams etc.) applicable to a map layer.
void readXml(const QDomElement &styleElement)
Read style configuration (for project file reading)
void readFromLayer(QgsMapLayer *layer)
Store layer's active style information in the instance.
void writeXml(QDomElement &styleElement) const
Write style configuration (for project file writing)
QString xmlData() const
Returns XML content of the style.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Job implementation that renders everything sequentially using a custom painter.
void cancelWithoutBlocking() override
Triggers cancellation of the rendering job without blocking.
void finished()
emitted when asynchronous rendering is finished (or canceled).
@ RenderLabelsByMapLayer
Labels should be rendered in individual stages by map layer. This allows separation of labels belongi...
static QStringList containsAdvancedEffects(const QgsMapSettings &mapSettings, EffectsCheckFlags flags=QgsMapSettingsUtils::EffectsCheckFlags())
Checks whether any of the layers attached to a map settings object contain advanced effects.
The QgsMapSettings class contains configuration for rendering of the map.
void setElevationShadingRenderer(const QgsElevationShadingRenderer &renderer)
Sets the shading renderer used to render shading on the entire map.
void addClippingRegion(const QgsMapClippingRegion &region)
Adds a new clipping region to the map settings.
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
void setSelectionColor(const QColor &color)
Sets the color that is used for drawing of selected vector features.
void setSimplifyMethod(const QgsVectorSimplifyMethod &method)
Sets the simplification setting to use when rendering vector layers.
QPolygonF visiblePolygon() const
Returns the visible area as a polygon (may be rotated)
void addRenderedFeatureHandler(QgsRenderedFeatureHandlerInterface *handler)
Adds a rendered feature handler to use while rendering the map settings.
void setTextRenderFormat(Qgis::TextRenderFormat format)
Sets the text render format, which dictates how text is rendered (e.g.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
void setDpiTarget(double dpi)
Sets the target dpi (dots per inch) to be taken into consideration when rendering.
long long currentFrame() const
Returns the current frame number of the map, for maps which are part of an animation.
void setOutputDpi(double dpi)
Sets the dpi (dots per inch) used for conversion between real world units (e.g.
void setRendererUsage(Qgis::RendererUsage rendererUsage)
Sets the rendering usage.
QgsRectangle extent() const
Returns geographical coordinates of the rectangle that should be rendered.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the map of map layer style overrides (key: layer ID, value: style name) where a different style ...
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
double frameRate() const
Returns the frame rate of the map (in frames per second), for maps which are part of an animation.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets the global configuration of the labeling engine.
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context, which stores various information regarding which datum transfo...
void setRotation(double rotation)
Sets the rotation of the resulting map image, in degrees clockwise.
void setPathResolver(const QgsPathResolver &resolver)
Sets the path resolver for conversion between relative and absolute paths during rendering operations...
void setLabelBoundaryGeometry(const QgsGeometry &boundary)
Sets the label boundary geometry, which restricts where in the rendered map labels are permitted to b...
void setLabelBlockingRegions(const QList< QgsLabelBlockingRegion > &regions)
Sets a list of regions to avoid placing labels within.
void setOutputSize(QSize size)
Sets the size of the resulting map image, in pixels.
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void setFlag(Qgis::MapSettingsFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
void mapThemeRenamed(const QString &name, const QString &newName)
Emitted when a map theme within the collection is renamed.
void mapThemeChanged(const QString &theme)
Emitted when a map theme changes definition.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
QString description() const
Description.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:105
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
void crsChanged()
Emitted when the CRS of the project has changed.
QgsMapThemeCollection * mapThemeCollection
Definition: qgsproject.h:113
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Definition: qgsrectangle.h:256
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
Definition: qgsrectangle.h:161
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
Definition: qgsrectangle.h:156
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
Definition: qgsrectangle.h:151
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
Definition: qgsrectangle.h:166
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
static QgsRectangle fromCenterAndSize(const QgsPointXY &center, double width, double height)
Creates a new rectangle, given the specified center point and width and height.
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
Contains information about the context of a rendering operation.
void setForceVectorOutput(bool force)
Sets whether rendering operations should use vector operations instead of any faster raster shortcuts...
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
An interface for classes which provider custom handlers for features rendered as part of a map render...
Calculates scale for a given combination of canvas size, map extent, and monitor dpi.
double calculate(const QgsRectangle &mapExtent, double canvasWidth) const
Calculate the scale denominator.
void setDpi(double dpi)
Sets the dpi (dots per inch) for the output resolution, to be used in scale calculations.
void setMapUnits(Qgis::DistanceUnit mapUnits)
Set the map units.
Scoped object for saving and restoring a QPainter object's state.
An interface for classes which can visit style entity (e.g.
@ LayoutItem
Individual item in a print layout.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
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.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
@ NoSimplification
No simplification can be applied.
static double scaleToZoom(double mapScale, double z0Scale=559082264.0287178)
Finds zoom level given map scale denominator.
static int scaleToZoomLevel(double mapScale, int sourceMinZoom, int sourceMaxZoom, double z0Scale=559082264.0287178)
Finds the best fitting zoom level given a map scale denominator and allowed zoom level range.
static Qgis::GeometryType geometryType(Qgis::WkbType type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:865
#define FALLTHROUGH
Definition: qgis.h:4120
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:4093
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:3448
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:4092
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2206
const QgsCoordinateReferenceSystem & crs
Single variable definition for use within a QgsExpressionContextScope.
Contains details of a particular export layer relating to a layout item.
QPainter::CompositionMode compositionMode
Associated composition mode if this layer is associated with a map layer.
QString mapLayerId
Associated map layer ID, or an empty string if this export layer is not associated with a map layer.
double opacity
Associated opacity, if this layer is associated with a map layer.
QString name
User-friendly name for the export layer.
QString mapTheme
Associated map theme, or an empty string if this export layer does not need to be associated with a m...
Contains information relating to a node (i.e.
TYPE * resolveWeakly(const QgsProject *project, MatchType matchType=MatchType::All)
Resolves the map layer by attempting to find a matching layer in a project using a weak match.
QString source
Weak reference to layer public source.
QString name
Weak reference to layer name.
QString provider
Weak reference to layer provider.
TYPE * resolve(const QgsProject *project)
Resolves the map layer by attempting to find a layer with matching ID within a project.
QString layerId
Original layer ID.