QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgslayoutitemlegend.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemlegend.cpp
3 -----------------------
4 begin : October 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17#include <limits>
18
19#include "qgslayoutitemlegend.h"
20#include "moc_qgslayoutitemlegend.cpp"
22#include "qgslayoutitemmap.h"
23#include "qgslayoutmodel.h"
24#include "qgslayertree.h"
25#include "qgslayertreemodel.h"
27#include "qgslegendrenderer.h"
28#include "qgslegendstyle.h"
29#include "qgslogger.h"
30#include "qgsmapsettings.h"
31#include "qgsproject.h"
32#include "qgscolorutils.h"
33#include "qgslayertreeutils.h"
34#include "qgslayoututils.h"
35#include "qgslayout.h"
38#include "qgsvectorlayer.h"
43
44#include <QDomDocument>
45#include <QDomElement>
46#include <QPainter>
48
50 : QgsLayoutItem( layout )
51 , mLegendModel( new QgsLegendModel( nullptr, this ) )
52{
53#if 0 //no longer required?
54 connect( &layout->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsLayoutItemLegend::onAtlasEnded );
55#endif
56
57 mTitle = mSettings.title();
58
59 connect( mLegendModel.get(), &QgsLayerTreeModel::hitTestStarted, this, [this] { emit backgroundTaskCountChanged( 1 ); } );
60 connect( mLegendModel.get(), &QgsLayerTreeModel::hitTestCompleted, this, [this]
61 {
62 adjustBoxSize();
63 emit backgroundTaskCountChanged( 0 );
64 } );
65
66 // Connect to the main layertreeroot.
67 // It serves in "auto update mode" as a medium between the main app legend and this one
68 connect( mLayout->project()->layerTreeRoot(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayoutItemLegend::nodeCustomPropertyChanged );
69
70 // If project colors change, we need to redraw legend, as legend symbols may rely on project colors
71 connect( mLayout->project(), &QgsProject::projectColorsChanged, this, [this]
72 {
73 invalidateCache();
74 update();
75 } );
76 connect( mLegendModel.get(), &QgsLegendModel::refreshLegend, this, [this]
77 {
78 // NOTE -- we do NOT connect to ::refresh here, as we don't want to trigger the call to onAtlasFeature() which sets mFilterAskedForUpdate to true,
79 // causing an endless loop.
80 invalidateCache();
81 update();
82 } );
83}
84
89
94
96{
97 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemLegend.svg" ) );
98}
99
104
105void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
106{
107 if ( !painter )
108 return;
109
110 const QPointF oldPos = pos();
111
112 ensureModelIsInitialized();
113
114 if ( mFilterAskedForUpdate )
115 {
116 mFilterAskedForUpdate = false;
117 doUpdateFilterByMap();
118 }
119
120 const int dpi = painter->device()->logicalDpiX();
121 const double dotsPerMM = dpi / 25.4;
122
123 if ( mLayout )
124 {
126 // no longer required, but left set for api stability
128 mSettings.setDpi( dpi );
131 }
132 if ( mMap && mLayout )
133 {
135 // no longer required, but left set for api stability
136 mSettings.setMmPerMapUnit( mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() );
138
139 // use a temporary QgsMapSettings to find out real map scale
140 const QSizeF mapSizePixels = QSizeF( mMap->rect().width() * dotsPerMM, mMap->rect().height() * dotsPerMM );
141 const QgsRectangle mapExtent = mMap->extent();
142
143 const QgsMapSettings ms = mMap->mapSettings( mapExtent, mapSizePixels, dpi, false );
144
145 // no longer required, but left set for api stability
147 mSettings.setMapScale( ms.scale() );
149 }
150 mInitialMapScaleCalculated = true;
151
152 QgsLegendRenderer legendRenderer = createRenderer();
153 legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
154
155 //adjust box if width or height is too small
156 if ( mSizeToContents )
157 {
158 QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter )
160
161 const QSizeF size = legendRenderer.minimumSize( &context );
162 if ( mForceResize )
163 {
164 mForceResize = false;
165
166 //set new rect, respecting position mode and data defined size/position
167 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
168 attemptResize( newSize );
169 }
170 else if ( size.height() > rect().height() || size.width() > rect().width() )
171 {
172 //need to resize box
173 QSizeF targetSize = rect().size();
174 if ( size.height() > targetSize.height() )
175 targetSize.setHeight( size.height() );
176 if ( size.width() > targetSize.width() )
177 targetSize.setWidth( size.width() );
178
179 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( targetSize, sizeWithUnits().units() );
180 //set new rect, respecting position mode and data defined size/position
181 attemptResize( newSize );
182 }
183 }
184
185 // attemptResize may change the legend position and would call setPos
186 // BUT the position is actually changed for the next draw, so we need to translate of the difference
187 // between oldPos and newPos
188 // the issue doesn't appear in desktop rendering but only in export because in the first one,
189 // Qt triggers a redraw on position change
190 painter->save();
191 painter->translate( pos() - oldPos );
192 QgsLayoutItem::paint( painter, itemStyle, pWidget );
193 painter->restore();
194}
195
197{
198 if ( !mMapUuid.isEmpty() )
199 {
200 setLinkedMap( qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) ) );
201 }
202
203 if ( !mFilterByMapUuids.isEmpty() )
204 {
205 QList< QgsLayoutItemMap * > maps;
206 maps.reserve( mFilterByMapUuids.size() );
207 for ( const QString &uuid : std::as_const( mFilterByMapUuids ) )
208 {
209 if ( QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( uuid, true ) ) )
210 {
211 maps << map;
212 }
213 }
214 setFilterByMapItems( maps );
215 }
216}
217
219{
221 clearLegendCachedData();
222 onAtlasFeature();
223}
224
226{
227 clearLegendCachedData();
229}
230
232{
233 QPainter *painter = context.renderContext().painter();
234
235 QgsRenderContext rc = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter, context.renderContext().scaleFactor() * 25.4 )
237
239
240 const QgsScopedQPainterState painterState( painter );
241
242 // painter is scaled to dots, so scale back to layout units
243 painter->scale( rc.scaleFactor(), rc.scaleFactor() );
244
245 painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
246
247 if ( !mSizeToContents )
248 {
249 // set a clip region to crop out parts of legend which don't fit
250 const QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
251 painter->setClipRect( thisPaintRect );
252 }
253
254 if ( mLayout )
255 {
256 // no longer required, but left for API compatibility
258 mSettings.setDpi( mLayout->renderContext().dpi() );
260 }
261
262 QgsLegendRenderer legendRenderer = createRenderer();
263 legendRenderer.setLegendSize( rect().size() );
264
265 legendRenderer.drawLegend( rc );
266}
267
269{
270 if ( !mSizeToContents )
271 return;
272
273 if ( !mInitialMapScaleCalculated )
274 {
275 // this is messy - but until we have painted the item we have no knowledge of the current DPI
276 // and so cannot correctly calculate the map scale. This results in incorrect size calculations
277 // for marker symbols with size in map units, causing the legends to initially expand to huge
278 // sizes if we attempt to calculate the box size first.
279 return;
280 }
281
282 QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, nullptr ) :
284
285 QgsLegendRenderer legendRenderer = createRenderer();
286 const QSizeF size = legendRenderer.minimumSize( &context );
287 QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ), 2 );
288 if ( size.isValid() )
289 {
290 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
291 //set new rect, respecting position mode and data defined size/position
292 attemptResize( newSize );
293 }
294}
295
297{
298 mSizeToContents = enabled;
299}
300
302{
303 return mSizeToContents;
304}
305
306void QgsLayoutItemLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
307{
308 if ( !mDeferLegendModelInitialization )
309 {
310 mLegendModel->setRootGroup( rootGroup ? rootGroup : ( mLayout ? mLayout->project()->layerTreeRoot() : nullptr ) );
311 }
312
313 mCustomLayerTree.reset( rootGroup );
314}
315
316void QgsLayoutItemLegend::ensureModelIsInitialized() const
317{
318 if ( mDeferLegendModelInitialization )
319 {
320 QgsLayoutItemLegend *mutableThis = const_cast< QgsLayoutItemLegend * >( this );
321 mutableThis->mDeferLegendModelInitialization = false;
322 mutableThis->setCustomLayerTree( mutableThis->mCustomLayerTree.release() );
323 }
324}
325
326QgsLegendRenderer QgsLayoutItemLegend::createRenderer() const
327{
328 QgsLegendRenderer res( mLegendModel.get(), mSettings );
329
330 // only show private layers when not in auto update mode
331 res.proxyModel()->setShowPrivateLayers( static_cast< bool >( mCustomLayerTree ) );
332
333 return res;
334}
335
337{
338 ensureModelIsInitialized();
339 return mLegendModel.get();
340}
341
343{
344 ensureModelIsInitialized();
345 return mLegendModel.get();
346}
347
349{
350 if ( autoUpdate == autoUpdateModel() )
351 return;
352
353 setCustomLayerTree( autoUpdate ? nullptr : mLayout->project()->layerTreeRoot()->clone() );
355 updateFilterByMap( false );
356}
357
358void QgsLayoutItemLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString &key )
359{
360 if ( key == QLatin1String( "cached_name" ) )
361 return;
362
363 if ( autoUpdateModel() )
364 {
365 // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
366 // we must then call updateItem to reflect the changes
367 updateFilterByMap( false );
368 }
369}
370
372{
373 return !mCustomLayerTree;
374}
375
377{
378 if ( mLegendFilterByMap == enabled )
379 return;
380
381 mLegendFilterByMap = enabled;
382 updateFilterByMap( false );
383}
384
385void QgsLayoutItemLegend::setTitle( const QString &t )
386{
387 mTitle = t;
388 mSettings.setTitle( t );
389
390 if ( mLayout && id().isEmpty() )
391 {
392 //notify the model that the display name has changed
393 mLayout->itemsModel()->updateItemDisplayName( this );
394 }
395}
397{
398 return mTitle;
399}
400
401Qt::AlignmentFlag QgsLayoutItemLegend::titleAlignment() const
402{
403 return mSettings.titleAlignment();
404}
405
406void QgsLayoutItemLegend::setTitleAlignment( Qt::AlignmentFlag alignment )
407{
408 mSettings.setTitleAlignment( alignment );
409}
410
415
417{
418 return mSettings.style( s );
419}
420
422{
423 mSettings.setStyle( s, style );
424}
425
432
439
441{
442 rstyle( s ).setMargin( margin );
443}
444
446{
447 rstyle( s ).setMargin( side, margin );
448}
449
456
458{
460 mSettings.setLineSpacing( spacing );
462}
463
465{
466 return mSettings.boxSpace();
467}
468
470{
471 mSettings.setBoxSpace( s );
472}
473
475{
476 return mSettings.columnSpace();
477}
478
480{
481 mSettings.setColumnSpace( s );
482}
483
485{
487 return mSettings.fontColor();
489}
490
497
499{
500 return mSettings.symbolSize().width();
501}
502
504{
505 mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) );
506}
507
509{
510 return mSettings.maximumSymbolSize();
511}
512
514{
515 mSettings.setMaximumSymbolSize( size );
516}
517
519{
520 return mSettings.minimumSymbolSize();
521}
522
524{
525 mSettings.setMinimumSymbolSize( size );
526}
527
528void QgsLayoutItemLegend::setSymbolAlignment( Qt::AlignmentFlag alignment )
529{
530 mSettings.setSymbolAlignment( alignment );
531}
532
534{
535 return mSettings.symbolAlignment();
536}
537
539{
540 return mSettings.symbolSize().height();
541}
542
544{
545 mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) );
546}
547
549{
550 return mSettings.wmsLegendSize().width();
551}
552
554{
555 mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) );
556}
557
559{
560 return mSettings.wmsLegendSize().height();
561}
563{
564 mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) );
565}
566
568{
569 mSettings.setWrapChar( t );
570}
571
573{
574 return mSettings.wrapChar();
575}
576
578{
579 return mColumnCount;
580}
581
583{
584 mColumnCount = c;
585 mSettings.setColumnCount( c );
586}
587
589{
590 return mSettings.splitLayer();
591}
592
594{
595 mSettings.setSplitLayer( s );
596}
597
599{
600 return mSettings.equalColumnWidth();
601}
602
604{
605 mSettings.setEqualColumnWidth( s );
606}
607
609{
610 return mSettings.drawRasterStroke();
611}
612
614{
615 mSettings.setDrawRasterStroke( enabled );
616}
617
619{
620 return mSettings.rasterStrokeColor();
621}
622
624{
625 mSettings.setRasterStrokeColor( color );
626}
627
629{
630 return mSettings.rasterStrokeWidth();
631}
632
634{
635 mSettings.setRasterStrokeWidth( width );
636}
637
643
644bool QgsLayoutItemLegend::writePropertiesToElement( QDomElement &legendElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
645{
646
647 //write general properties
648 legendElem.setAttribute( QStringLiteral( "title" ), mTitle );
649 legendElem.setAttribute( QStringLiteral( "titleAlignment" ), QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
650 legendElem.setAttribute( QStringLiteral( "columnCount" ), QString::number( mColumnCount ) );
651 legendElem.setAttribute( QStringLiteral( "splitLayer" ), QString::number( mSettings.splitLayer() ) );
652 legendElem.setAttribute( QStringLiteral( "equalColumnWidth" ), QString::number( mSettings.equalColumnWidth() ) );
653
654 legendElem.setAttribute( QStringLiteral( "boxSpace" ), QString::number( mSettings.boxSpace() ) );
655 legendElem.setAttribute( QStringLiteral( "columnSpace" ), QString::number( mSettings.columnSpace() ) );
656
657 legendElem.setAttribute( QStringLiteral( "symbolWidth" ), QString::number( mSettings.symbolSize().width() ) );
658 legendElem.setAttribute( QStringLiteral( "symbolHeight" ), QString::number( mSettings.symbolSize().height() ) );
659 legendElem.setAttribute( QStringLiteral( "maxSymbolSize" ), QString::number( mSettings.maximumSymbolSize() ) );
660 legendElem.setAttribute( QStringLiteral( "minSymbolSize" ), QString::number( mSettings.minimumSymbolSize() ) );
661
662 legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
663
664 legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
665
666 legendElem.setAttribute( QStringLiteral( "rasterBorder" ), mSettings.drawRasterStroke() );
667 legendElem.setAttribute( QStringLiteral( "rasterBorderColor" ), QgsColorUtils::colorToString( mSettings.rasterStrokeColor() ) );
668 legendElem.setAttribute( QStringLiteral( "rasterBorderWidth" ), QString::number( mSettings.rasterStrokeWidth() ) );
669
670 legendElem.setAttribute( QStringLiteral( "wmsLegendWidth" ), QString::number( mSettings.wmsLegendSize().width() ) );
671 legendElem.setAttribute( QStringLiteral( "wmsLegendHeight" ), QString::number( mSettings.wmsLegendSize().height() ) );
672 legendElem.setAttribute( QStringLiteral( "wrapChar" ), mSettings.wrapChar() );
673
674 legendElem.setAttribute( QStringLiteral( "resizeToContents" ), mSizeToContents );
675
676 if ( mMap )
677 {
678 legendElem.setAttribute( QStringLiteral( "map_uuid" ), mMap->uuid() );
679 }
680
681 if ( !mFilterByMapItems.empty() )
682 {
683 QDomElement filterByMapsElem = doc.createElement( QStringLiteral( "filterByMaps" ) );
684 for ( QgsLayoutItemMap *map : mFilterByMapItems )
685 {
686 if ( map )
687 {
688 QDomElement mapElem = doc.createElement( QStringLiteral( "map" ) );
689 mapElem.setAttribute( QStringLiteral( "uuid" ), map->uuid() );
690 filterByMapsElem.appendChild( mapElem );
691 }
692 }
693 legendElem.appendChild( filterByMapsElem );
694 }
695
696 QDomElement legendStyles = doc.createElement( QStringLiteral( "styles" ) );
697 legendElem.appendChild( legendStyles );
698
699 style( QgsLegendStyle::Title ).writeXml( QStringLiteral( "title" ), legendStyles, doc, context );
700 style( QgsLegendStyle::Group ).writeXml( QStringLiteral( "group" ), legendStyles, doc, context );
701 style( QgsLegendStyle::Subgroup ).writeXml( QStringLiteral( "subgroup" ), legendStyles, doc, context );
702 style( QgsLegendStyle::Symbol ).writeXml( QStringLiteral( "symbol" ), legendStyles, doc, context );
703 style( QgsLegendStyle::SymbolLabel ).writeXml( QStringLiteral( "symbolLabel" ), legendStyles, doc, context );
704
705 if ( mCustomLayerTree )
706 {
707 // if not using auto-update - store the custom layer tree
708 mCustomLayerTree->writeXml( legendElem, context );
709 }
710
711 if ( mLegendFilterByMap )
712 {
713 legendElem.setAttribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "1" ) );
714 }
715 legendElem.setAttribute( QStringLiteral( "legendFilterByAtlas" ), mFilterOutAtlas ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
716
717 return true;
718}
719
720bool QgsLayoutItemLegend::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
721{
722 //read general properties
723 mTitle = itemElem.attribute( QStringLiteral( "title" ) );
724 mSettings.setTitle( mTitle );
725 if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
726 {
727 mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
728 }
729 int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
730 if ( colCount < 1 ) colCount = 1;
731 mColumnCount = colCount;
732 mSettings.setColumnCount( mColumnCount );
733 mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
734 mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
735
736 const QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
737 if ( !stylesNodeList.isEmpty() )
738 {
739 const QDomNode stylesNode = stylesNodeList.at( 0 );
740 for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
741 {
742 const QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
744 style.readXml( styleElem, doc, context );
745 const QString name = styleElem.attribute( QStringLiteral( "name" ) );
747 if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
748 else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
749 else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
750 else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
751 else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
752 else continue;
753 setStyle( s, style );
754 }
755 }
756
757 //font color
758 if ( itemElem.hasAttribute( QStringLiteral( "fontColor" ) ) )
759 {
760 QColor fontClr;
761 fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
766 }
767
768 //spaces
769 mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
770 mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
771
772 mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
773 mSettings.setSymbolAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "symbolAlignment" ), QString::number( Qt::AlignLeft ) ).toInt() ) );
774
775 mSettings.setMaximumSymbolSize( itemElem.attribute( QStringLiteral( "maxSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
776 mSettings.setMinimumSymbolSize( itemElem.attribute( QStringLiteral( "minSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
777
778 mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
779
780 if ( itemElem.hasAttribute( QStringLiteral( "lineSpacing" ) ) )
781 {
782 const double spacing = itemElem.attribute( QStringLiteral( "lineSpacing" ), QStringLiteral( "1.0" ) ).toDouble();
783 // line spacing *was* a fixed amount (in mm) added between each line of text.
785 // assume font sizes in points, since that was what we always had from before this method was deprecated
786 f.setLineHeight( f.size() * 0.352778 + spacing );
789
791 f.setLineHeight( f.size() * 0.352778 + spacing );
794
796 f.setLineHeight( f.size() * 0.352778 + spacing );
799
801 f.setLineHeight( f.size() * 0.352778 + spacing );
804 }
805
806 mSettings.setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
807 mSettings.setRasterStrokeColor( QgsColorUtils::colorFromString( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
808 mSettings.setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
809
810 mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
811
812 mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
813
814 // map
815 mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
816
817 mMapUuid.clear();
818 if ( !itemElem.attribute( QStringLiteral( "map_uuid" ) ).isEmpty() )
819 {
820 mMapUuid = itemElem.attribute( QStringLiteral( "map_uuid" ) );
821 }
822
823 mFilterByMapUuids.clear();
824 {
825 const QDomElement filterByMapsElem = itemElem.firstChildElement( QStringLiteral( "filterByMaps" ) );
826 if ( !filterByMapsElem.isNull() )
827 {
828 QDomElement mapsElem = filterByMapsElem.firstChildElement( QStringLiteral( "map" ) );
829 while ( !mapsElem.isNull() )
830 {
831 mFilterByMapUuids << mapsElem.attribute( QStringLiteral( "uuid" ) );
832 mapsElem = mapsElem.nextSiblingElement( QStringLiteral( "map" ) );
833 }
834 }
835 else if ( !mMapUuid.isEmpty() )
836 {
837 // for compatibility with < QGIS 3.32 projects
838 mFilterByMapUuids << mMapUuid;
839 }
840 }
841
842 // disconnect current map
843 setupMapConnections( mMap, false );
844 mMap = nullptr;
845
846 mFilterOutAtlas = itemElem.attribute( QStringLiteral( "legendFilterByAtlas" ), QStringLiteral( "0" ) ).toInt();
847
848 // QGIS >= 2.6
849 QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
850 if ( layerTreeElem.isNull() )
851 layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
852
853 if ( !layerTreeElem.isNull() )
854 {
855 std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem, context ) );
856 if ( mLayout )
857 tree->resolveReferences( mLayout->project(), true );
858 setCustomLayerTree( tree.release() );
859 }
860 else
861 setCustomLayerTree( nullptr );
862
863 return true;
864}
865
867{
868 if ( !id().isEmpty() )
869 {
870 return id();
871 }
872
873 //if no id, default to portion of title text
874 QString text = mSettings.title();
875 if ( text.isEmpty() )
876 {
877 return tr( "<Legend>" );
878 }
879 if ( text.length() > 25 )
880 {
881 return tr( "%1…" ).arg( text.left( 25 ) );
882 }
883 else
884 {
885 return text;
886 }
887}
888
890{
891 return blendMode() != QPainter::CompositionMode_SourceOver;
892}
893
895{
896 return mEvaluatedOpacity < 1.0;
897}
898
899void QgsLayoutItemLegend::setupMapConnections( QgsLayoutItemMap *map, bool connectSlots )
900{
901 if ( !map )
902 return;
903
904 if ( !connectSlots )
905 {
906 disconnect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
907 disconnect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
908 disconnect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
909 disconnect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
910 disconnect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
911 disconnect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
912 }
913 else
914 {
915 connect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
916 connect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
917 connect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
918 connect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
919 connect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
920 connect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
921 }
922}
923
925{
926 if ( mMap == map )
927 return;
928
929 if ( mMap )
930 {
931 setupMapConnections( mMap, false );
932 }
933
934 mMap = map;
935
936 if ( mMap )
937 {
938 setupMapConnections( mMap, true );
939 mapThemeChanged( mMap->themeToRender( mMap->createExpressionContext() ) );
940 }
941
943}
944
945void QgsLayoutItemLegend::setFilterByMapItems( const QList<QgsLayoutItemMap *> &maps )
946{
947 if ( filterByMapItems() == maps )
948 return;
949
950 for ( QgsLayoutItemMap *map : std::as_const( mFilterByMapItems ) )
951 {
952 setupMapConnections( map, false );
953 }
954
955 mFilterByMapItems.clear();
956 mFilterByMapItems.reserve( maps.size() );
957 for ( QgsLayoutItemMap *map : maps )
958 {
959 if ( map )
960 {
961 mFilterByMapItems.append( map );
962 setupMapConnections( map, true );
963 }
964 }
965
967}
968
969QList<QgsLayoutItemMap *> QgsLayoutItemLegend::filterByMapItems() const
970{
971 QList<QgsLayoutItemMap *> res;
972 res.reserve( mFilterByMapItems.size() );
973 for ( QgsLayoutItemMap *map : mFilterByMapItems )
974 {
975 if ( map )
976 res.append( map );
977 }
978 return res;
979}
980
981void QgsLayoutItemLegend::invalidateCurrentMap()
982{
983 setLinkedMap( nullptr );
984}
985
987{
989
990 bool forceUpdate = false;
991 //updates data defined properties and redraws item to match
993 {
994 bool ok = false;
996 if ( ok )
997 {
998 mSettings.setTitle( t );
999 forceUpdate = true;
1000 }
1001 }
1003 {
1004 bool ok = false;
1006 if ( ok && cols >= 0 )
1007 {
1008 mSettings.setColumnCount( cols );
1009 forceUpdate = true;
1010 }
1011 }
1012 if ( forceUpdate )
1013 {
1014 adjustBoxSize();
1015 update();
1016 }
1017
1019}
1020
1021
1022void QgsLayoutItemLegend::updateFilterByMapAndRedraw()
1023{
1024 updateFilterByMap( true );
1025}
1026
1027void QgsLayoutItemLegend::setModelStyleOverrides( const QMap<QString, QString> &overrides )
1028{
1029 mLegendModel->setLayerStyleOverrides( overrides );
1030 if ( QgsLayerTree *rootGroup = mLegendModel->rootGroup() )
1031 {
1032 const QList< QgsLayerTreeLayer * > layers = rootGroup->findLayers();
1033 for ( QgsLayerTreeLayer *nodeLayer : std::as_const( layers ) )
1034 mLegendModel->refreshLayerLegend( nodeLayer );
1035 }
1036}
1037
1038void QgsLayoutItemLegend::clearLegendCachedData()
1039{
1040 std::function< void( QgsLayerTreeNode * ) > clearNodeCache;
1041 clearNodeCache = [&]( QgsLayerTreeNode * node )
1042 {
1043 mLegendModel->clearCachedData( node );
1044 if ( QgsLayerTree::isGroup( node ) )
1045 {
1047 const QList< QgsLayerTreeNode * > children = group->children();
1048 for ( QgsLayerTreeNode *child : children )
1049 {
1050 clearNodeCache( child );
1051 }
1052 }
1053 };
1054
1055 if ( QgsLayerTree *rootGroup = mLegendModel->rootGroup() )
1056 {
1057 clearNodeCache( rootGroup );
1058 }
1059}
1060
1061void QgsLayoutItemLegend::mapLayerStyleOverridesChanged()
1062{
1063 if ( !mMap )
1064 return;
1065
1066 // map's style has been changed, so make sure to update the legend here
1067 if ( mLegendFilterByMap )
1068 {
1069 // legend is being filtered by map, so we need to re run the hit test too
1070 // as the style overrides may also have affected the visible symbols
1071 updateFilterByMap( false );
1072 }
1073 else
1074 {
1075 setModelStyleOverrides( mMap->layerStyleOverrides() );
1076 }
1077
1078 adjustBoxSize();
1079
1080 updateFilterByMap( false );
1081}
1082
1083void QgsLayoutItemLegend::mapThemeChanged( const QString &theme )
1084{
1085 if ( mThemeName == theme )
1086 return;
1087
1088 mThemeName = theme;
1089
1090 // map's theme has been changed, so make sure to update the legend here
1091
1092 // legend is being filtered by map, so we need to re run the hit test too
1093 // as the style overrides may also have affected the visible symbols
1094 updateFilterByMap( false );
1095
1096 adjustBoxSize();
1097
1099}
1100
1102{
1103 // ask for update
1104 // the actual update will take place before the redraw.
1105 // This is to avoid multiple calls to the filter
1106 mFilterAskedForUpdate = true;
1107
1108 if ( redraw )
1109 update();
1110}
1111
1112void QgsLayoutItemLegend::doUpdateFilterByMap()
1113{
1114 // There's an incompatibility here between legend handling of linked map themes and layer style overrides vs
1115 // how expression evaluation is made in legend content text. The logic below is hacked together to get
1116 // all the existing unit tests passing, but these two features are incompatible with each other and fixing
1117 // this is extremely non-trivial. Let's just hope no-one tries to use those features together!
1118 // Ideally, all the branches below would be consistently using either "setModelStyleOverrides" (which forces
1119 // a rebuild of each layer's legend, and breaks legend text expression evaluation) OR
1120 // "mLegendModel->setLayerStyleOverrides" which just handles the expression updates but which doesn't correctly
1121 // generate the legend content from the associated theme settings.
1122 if ( mMap && !mThemeName.isEmpty() )
1123 {
1124 // get style overrides for theme
1125 const QMap<QString, QString> overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName );
1126 setModelStyleOverrides( overrides );
1127 }
1128 else if ( mMap )
1129 {
1130 mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() );
1131 }
1132 else
1133 {
1134 mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );
1135 }
1136
1137 // only use thread hit tests for preview renders. In other cases we'll need a blocking hit test anyway, and we run a risk
1138 // of deadlocks if a non-preview render is then started on the main thread.
1139 mLegendModel->setFlag( QgsLayerTreeModel::UseThreadedHitTest, mLayout->renderContext().isPreviewRender() );
1140
1141 const bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree.get() : mLayout->project()->layerTreeRoot() ) );
1142
1143 const bool hasValidFilter = filterByExpression
1144 || ( mLegendFilterByMap && ( mMap || !mFilterByMapItems.empty() ) )
1145 || mInAtlas;
1146
1147 if ( hasValidFilter )
1148 {
1149 const double dpi = mLayout->renderContext().dpi();
1150
1151 QSet< QgsLayoutItemMap * > linkedFilterMaps;
1152 if ( mLegendFilterByMap )
1153 {
1154 linkedFilterMaps = qgis::listToSet( filterByMapItems() );
1155 if ( mMap )
1156 linkedFilterMaps.insert( mMap );
1157 }
1158
1159 QgsMapSettings mapSettings;
1160 QgsGeometry filterGeometry;
1161 if ( mMap )
1162 {
1163 // if a specific linked map has been set, use it for the reference scale and extent
1164 const QgsRectangle requestRectangle = mMap->requestedExtent();
1165 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1166 size *= mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1167 mapSettings = mMap->mapSettings( requestRectangle, size, dpi, true );
1168
1169 filterGeometry = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
1170 }
1171 else if ( !linkedFilterMaps.empty() )
1172 {
1173 // otherwise just take the first linked filter map
1174 const QgsRectangle requestRectangle = ( *linkedFilterMaps.constBegin() )->requestedExtent();
1175 QSizeF size( requestRectangle.width(), requestRectangle.height() );
1176 size *= mLayout->convertFromLayoutUnits( ( *linkedFilterMaps.constBegin() )->mapUnitsToLayoutUnits(), Qgis::LayoutUnit::Millimeters ).length() * dpi / 25.4;
1177 mapSettings = ( *linkedFilterMaps.constBegin() )->mapSettings( requestRectangle, size, dpi, true );
1178
1179 filterGeometry = QgsGeometry::fromQPolygonF( ( *linkedFilterMaps.constBegin() )->visibleExtentPolygon() );
1180 }
1181
1183
1184 const QgsGeometry atlasGeometry { mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) };
1185
1186 QgsLayerTreeFilterSettings filterSettings( mapSettings );
1187
1188 QList<QgsMapLayer *> layersToClip;
1189 if ( !atlasGeometry.isNull() && mMap->atlasClippingSettings()->enabled() )
1190 {
1191 layersToClip = mMap->atlasClippingSettings()->layersToClip();
1192 for ( QgsMapLayer *layer : std::as_const( layersToClip ) )
1193 {
1194 QList<QgsMapLayer *> mapLayers { filterSettings.mapSettings().layers( true ) };
1195 mapLayers.removeAll( layer );
1196 filterSettings.mapSettings().setLayers( mapLayers );
1197 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( atlasGeometry, mapSettings.destinationCrs() ) );
1198 }
1199 }
1200
1201
1202 if ( !linkedFilterMaps.empty() )
1203 {
1204 for ( QgsLayoutItemMap *map : std::as_const( linkedFilterMaps ) )
1205 {
1206
1207 if ( map == mMap )
1208 continue;
1209
1211
1212 //transform back to destination CRS
1213 const QgsCoordinateTransform mapTransform( map->crs(), mapSettings.destinationCrs(), mLayout->project() );
1214 try
1215 {
1216 mapExtent.transform( mapTransform );
1217 }
1218 catch ( QgsCsException & )
1219 {
1220 continue;
1221 }
1222
1223 const QList< QgsMapLayer * > layersForMap = map->layersToRender();
1224 for ( QgsMapLayer *layer : layersForMap )
1225 {
1226 if ( mInAtlas && !atlasGeometry.isNull() )
1227 {
1228 mapExtent = mapExtent.intersection( atlasGeometry );
1229 }
1230
1231 filterSettings.addVisibleExtentForLayer( layer, QgsReferencedGeometry( mapExtent, mapSettings.destinationCrs() ) );
1232 }
1233 }
1234 }
1235
1236 if ( mInAtlas )
1237 {
1238 if ( !filterGeometry.isEmpty() )
1239 filterGeometry = mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() );
1240 else
1241 filterGeometry = filterGeometry.intersection( mLayout->reportContext().currentGeometry( mapSettings.destinationCrs() ) );
1242 }
1243
1244 filterSettings.setLayerFilterExpressionsFromLayerTree( mLegendModel->rootGroup() );
1245 if ( !filterGeometry.isNull() )
1246 {
1247 filterSettings.setFilterPolygon( filterGeometry );
1248 }
1249 else
1250 {
1251 filterSettings.setFlags( Qgis::LayerTreeFilterFlag::SkipVisibilityCheck );
1252 }
1253
1254 mLegendModel->setFilterSettings( &filterSettings );
1255 }
1256 else
1257 {
1258 mLegendModel->setFilterSettings( nullptr );
1259 }
1260
1261 clearLegendCachedData();
1262 mForceResize = true;
1263}
1264
1266{
1267 return mThemeName;
1268}
1269
1271{
1272 mFilterOutAtlas = doFilter;
1273}
1274
1276{
1277 return mFilterOutAtlas;
1278}
1279
1280void QgsLayoutItemLegend::onAtlasFeature()
1281{
1282 if ( !mLayout || !mLayout->reportContext().feature().isValid() )
1283 return;
1284 mInAtlas = mFilterOutAtlas;
1286}
1287
1288void QgsLayoutItemLegend::onAtlasEnded()
1289{
1290 mInAtlas = false;
1292}
1293
1295{
1297
1298 // We only want the last scope from the map's expression context, as this contains
1299 // the map specific variables. We don't want the rest of the map's context, because that
1300 // will contain duplicate global, project, layout, etc scopes.
1301 if ( mMap )
1302 context.appendScope( mMap->createExpressionContext().popScope() );
1303
1304 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Legend Settings" ) );
1305
1306 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_title" ), title(), true ) );
1307 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_column_count" ), columnCount(), true ) );
1308 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_split_layers" ), splitLayer(), true ) );
1309 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_wrap_string" ), wrapString(), true ) );
1310 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_by_map" ), legendFilterByMapEnabled(), true ) );
1311 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_out_atlas" ), legendFilterOutAtlas(), true ) );
1312
1313 context.appendScope( scope );
1314 return context;
1315}
1316
1321
1323{
1324 std::function<bool( QgsLayerTreeGroup *group ) >visit;
1325
1326 visit = [this, visitor, &visit]( QgsLayerTreeGroup * group ) -> bool
1327 {
1328 const QList<QgsLayerTreeNode *> childNodes = group->children();
1329 for ( QgsLayerTreeNode *node : childNodes )
1330 {
1331 if ( QgsLayerTree::isGroup( node ) )
1332 {
1333 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
1334 if ( !visit( nodeGroup ) )
1335 return false;
1336 }
1337 else if ( QgsLayerTree::isLayer( node ) )
1338 {
1339 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
1340 if ( !nodeLayer->patchShape().isNull() )
1341 {
1342 QgsStyleLegendPatchShapeEntity entity( nodeLayer->patchShape() );
1343 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1344 return false;
1345 }
1346 const QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
1347 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
1348 {
1349 if ( QgsSymbolLegendNode *symbolNode = dynamic_cast< QgsSymbolLegendNode * >( legendNode ) )
1350 {
1351 if ( !symbolNode->patchShape().isNull() )
1352 {
1353 QgsStyleLegendPatchShapeEntity entity( symbolNode->patchShape() );
1354 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
1355 return false;
1356 }
1357 }
1358 }
1359 }
1360 }
1361 return true;
1362 };
1363 return visit( model()->rootGroup( ) );
1364}
1365
1367{
1368 return mLegendModel->hitTestInProgress();
1369}
1370
1371
1372// -------------------------------------------------------------------------
1373
1375 : QgsLayerTreeModel( rootNode, parent )
1376 , mLayoutLegend( layout )
1377{
1381 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1382}
1383
1385 : QgsLayerTreeModel( rootNode )
1386 , mLayoutLegend( layout )
1387{
1391 connect( this, &QgsLegendModel::dataChanged, this, &QgsLegendModel::refreshLegend );
1392}
1393
1394QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
1395{
1396 // handle custom layer node labels
1397
1399 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::isLayer( node ) ? QgsLayerTree::toLayer( node ) : nullptr;
1400 if ( nodeLayer && ( role == Qt::DisplayRole || role == Qt::EditRole ) )
1401 {
1402 QString name = node->customProperty( QStringLiteral( "cached_name" ) ).toString();
1403 if ( !name.isEmpty() )
1404 return name;
1405
1406 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
1407
1408 //finding the first label that is stored
1409 name = nodeLayer->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
1410 if ( name.isEmpty() )
1411 name = nodeLayer->name();
1412 if ( name.isEmpty() )
1413 name = node->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
1414 if ( name.isEmpty() )
1415 name = node->name();
1416 if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() )
1417 {
1418 if ( vlayer && vlayer->featureCount() >= 0 )
1419 {
1420 name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
1421 node->setCustomProperty( QStringLiteral( "cached_name" ), name );
1422 return name;
1423 }
1424 }
1425 node->setCustomProperty( QStringLiteral( "cached_name" ), name );
1426 return name;
1427 }
1428 return QgsLayerTreeModel::data( index, role );
1429}
1430
1431Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
1432{
1433 // make the legend nodes selectable even if they are not by default
1434 if ( index2legendNode( index ) )
1435 return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
1436
1438}
1439
1440QList<QgsLayerTreeModelLegendNode *> QgsLegendModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent ) const
1441{
1442 if ( !mLegend.contains( nodeLayer ) )
1443 return QList<QgsLayerTreeModelLegendNode *>();
1444
1445 const LayerLegendData &data = mLegend[nodeLayer];
1446 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1447 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1448 lst.prepend( data.embeddedNodeInParent );
1449 return lst;
1450}
1451
1453{
1454 node->removeCustomProperty( QStringLiteral( "cached_name" ) );
1455}
1456
1457void QgsLegendModel::forceRefresh()
1458{
1459 emit refreshLegend();
1460}
@ Millimeters
Millimeters.
@ SkipVisibilityCheck
If set, the standard visibility check should be skipped.
@ Millimeters
Millimeters.
int valueAsInt(int key, const QgsExpressionContext &context, int defaultValue=0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an integer.
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.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
A geometry is the spatial representation of a feature.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
Contains settings relating to filtering the contents of QgsLayerTreeModel and views.
Layer tree group node serves as a container for layers and further groups.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Layer tree node points to a map layer.
QgsLegendPatchShape patchShape() const
Returns the symbol patch shape to use when rendering the legend node symbol.
QString name() const override
Returns the layer's name.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
The QgsLayerTreeModel class is model implementation for Qt item views framework.
Flags flags() const
Returns OR-ed combination of model flags.
void hitTestStarted()
Emitted when a hit test for visible legend items starts.
void hitTestCompleted()
Emitted when a hit test for visible legend items completes.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
void setFlag(Flag f, bool on=true)
Enable or disable a model flag.
QHash< QgsLayerTreeLayer *, LayerLegendData > mLegend
Per layer data about layer's legend nodes.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
@ AllowNodeReorder
Allow reordering with drag'n'drop.
@ AllowLegendChangeState
Allow check boxes for legend nodes (if supported by layer's legend)
@ UseThreadedHitTest
Run legend hit tests in a background thread.
This class is a base class for nodes in a layer tree.
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
virtual QString name() const =0
Returns name of the node.
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
static bool hasLegendFilterExpression(const QgsLayerTreeGroup &group)
Test if one of the layers in a group has an expression filter.
Namespace with helper functions for layer tree operations.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTree * readXml(QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
A layout item subclass for map legends.
bool autoUpdateModel() const
Returns whether the legend content should auto update to reflect changes in the project's layer tree.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void setStyleMargin(QgsLegendStyle::Style component, double margin)
Set the margin for a legend component.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
QString title() const
Returns the legend title.
void setSplitLayer(bool enabled)
Sets whether the legend items from a single layer can be split over multiple columns.
QgsLegendModel * model()
Returns the legend model.
void adjustBoxSize()
Sets the legend's item bounds to fit the whole legend content.
double wmsLegendWidth() const
Returns the WMS legend width.
void setColumnSpace(double spacing)
Sets the legend column spacing.
void setBoxSpace(double space)
Sets the legend box space.
bool splitLayer() const
Returns whether the legend items from a single layer can be split over multiple columns.
double symbolHeight() const
Returns the legend symbol height.
void updateFilterByMap(bool redraw=true)
Updates the legend content when filtered by map.
void setEqualColumnWidth(bool equalize)
Sets whether column widths should be equalized.
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties) override
Q_DECL_DEPRECATED QFont styleFont(QgsLegendStyle::Style component) const
Returns the font settings for a legend component.
static QgsLayoutItemLegend * create(QgsLayout *layout)
Returns a new legend item for the specified layout.
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...
void setLegendFilterOutAtlas(bool doFilter)
When set to true, during an atlas rendering, it will filter out legend elements where features are ou...
void setSymbolWidth(double width)
Sets the legend symbol width.
QString wrapString() const
Returns the legend text wrapping string.
bool resizeToContents() const
Returns whether the legend should automatically resize to fit its contents.
void setResizeToContents(bool enabled)
Sets whether the legend should automatically resize to fit its contents.
void updateLegend()
Updates the model and all legend entries.
QgsLayoutItemLegend(QgsLayout *layout)
Constructor for QgsLayoutItemLegend, with the specified parent layout.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map to associate with the legend.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void setSymbolAlignment(Qt::AlignmentFlag alignment)
Sets the alignment for placement of legend symbols.
QgsLegendStyle & rstyle(QgsLegendStyle::Style s)
Returns reference to modifiable legend style.
Q_DECL_DEPRECATED QColor fontColor() const
Returns the legend font color.
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
double maximumSymbolSize() const
Returns the maximum symbol size (in mm).
void setWmsLegendWidth(double width)
Sets the WMS legend width.
void setTitle(const QString &title)
Sets the legend title.
Q_DECL_DEPRECATED void setFontColor(const QColor &color)
Sets the legend font color.
double boxSpace() const
Returns the legend box space.
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
QString displayName() const override
Gets item display name.
bool isRefreshing() const override
Returns true if the item is currently refreshing content in the background.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
double symbolWidth() const
Returns the legend symbol width.
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
int type() const override
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
bool legendFilterOutAtlas() const
Returns whether to filter out legend elements outside of the current atlas feature.
void setStyle(QgsLegendStyle::Style component, const QgsLegendStyle &style)
Sets the style of component to style for the legend.
ExportLayerBehavior exportLayerBehavior() const override
Returns the behavior of this item during exporting to layered exports (e.g.
Q_DECL_DEPRECATED void setLineSpacing(double spacing)
Sets the spacing in-between multiple lines.
double columnSpace() const
Returns the legend column spacing.
QgsLayoutItem::Flags itemFlags() const override
Returns the item's flags, which indicate how the item behaves.
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
void setFilterByMapItems(const QList< QgsLayoutItemMap * > &maps)
Sets the maps to use when filtering legend content by map extents.
void setLegendFilterByMapEnabled(bool enabled)
Set whether legend items should be filtered to show just the ones visible in the associated map.
QList< QgsLayoutItemMap * > filterByMapItems() const
Returns the maps to use when filtering legend content by map extents.
void setSymbolHeight(double height)
Sets the legend symbol height.
void setMinimumSymbolSize(double size)
Set the minimum symbol size for symbol (in millimeters).
void setAutoUpdateModel(bool autoUpdate)
Sets whether the legend content should auto update to reflect changes in the project's layer tree.
void setMaximumSymbolSize(double size)
Set the maximum symbol size for symbol (in millimeters).
QIcon icon() const override
Returns the item's icon.
double wmsLegendHeight() const
Returns the WMS legend height.
void setWmsLegendHeight(double height)
Sets the WMS legend height.
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
bool containsAdvancedEffects() const override
Returns true if the item contains contents with blend modes or transparency effects which can only be...
double minimumSymbolSize() const
Returns the minimum symbol size (in mm).
double rasterStrokeWidth() const
Returns the stroke width (in layout units) for the stroke drawn around raster symbol items.
Q_DECL_DEPRECATED double lineSpacing() const
Returns the spacing in-between lines in layout units.
void invalidateCache() override
Q_DECL_DEPRECATED void setStyleFont(QgsLegendStyle::Style component, const QFont &font)
Sets the style font for a legend component.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns legend style.
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
void setWrapString(const QString &string)
Sets the legend text wrapping string.
QString themeName() const
Returns the name of the theme currently linked to the legend.
void setColumnCount(int count)
Sets the legend column count.
bool equalColumnWidth() const
Returns whether column widths should be equalized.
int columnCount() const
Returns the legend column count.
bool legendFilterByMapEnabled() const
Find out whether legend items are filtered to show just the ones visible in the associated map.
QList< QgsMapLayer * > layersToClip() const
Returns the list of map layers to clip to the atlas feature.
bool enabled() const
Returns true if the map content should be clipped to the current atlas feature.
Layout graphical items for displaying a map.
void extentChanged()
Emitted when the map's extent changes.
QgsMapSettings mapSettings(const QgsRectangle &extent, QSizeF size, double dpi, bool includeLayerSettings) const
Returns map settings that will be used for drawing of the map.
void layerStyleOverridesChanged()
Emitted when layer style overrides are changed... a means to let associated legend items know they sh...
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
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.
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...
QMap< QString, QString > layerStyleOverrides() const
Returns stored overrides of styles for layers.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
double mapUnitsToLayoutUnits() const
Returns the conversion factor from map units to layout units.
void themeChanged(const QString &theme)
Emitted when the map's associated theme is changed.
QgsRectangle extent() const
Returns the current map extent.
QgsLayoutItemMapAtlasClippingSettings * atlasClippingSettings()
Returns the map's atlas clipping settings.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Base class for graphical items within a QgsLayout.
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties)
Refreshes a data defined property for the item by reevaluating the property's value and redrawing the...
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Handles preparing a paint surface for the layout item and painting the item's content.
virtual void redraw()
Triggers a redraw (update) of the item.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
@ FlagOverridesPaint
Item overrides the default layout item painting method.
virtual void invalidateCache()
Forces a deferred update of any cached image the item uses.
virtual QString uuid() const
Returns the item identification string.
QString id() const
Returns the item's ID name.
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
@ MustPlaceInOwnLayer
Item must be placed in its own individual layer.
QFlags< Flag > Flags
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
QPainter::CompositionMode blendMode() const
Returns the item's composition blending mode.
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.
@ LegendColumnCount
Legend column count.
@ AllProperties
All properties for item.
@ FlagUseAdvancedEffects
Enable advanced effects such as blend modes.
@ FlagSynchronousLegendGraphics
Query legend graphics synchronously.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
static QgsRenderContext createRenderContextForMap(QgsLayoutItemMap *map, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout map and painter destination.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:49
Item model implementation based on layer tree model for layout legend.
void clearCachedData(QgsLayerTreeNode *node) const
Clears any previously cached data for the specified node.
void refreshLegend()
Emitted to refresh the legend.
QgsLegendModel(QgsLayerTree *rootNode, QObject *parent=nullptr, QgsLayoutItemLegend *layout=nullptr)
Construct the model based on the given layer tree.
QVariant data(const QModelIndex &index, int role) const override
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false) const
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
Handles automatic layout and rendering of legends.
QSizeF minimumSize(QgsRenderContext *renderContext=nullptr)
Runs the layout algorithm and returns the minimum size required for the legend.
void setLegendSize(QSizeF s)
Sets the preferred resulting legend size.
Q_DECL_DEPRECATED void drawLegend(QPainter *painter)
Draws the legend with given painter.
void setSymbolAlignment(Qt::AlignmentFlag alignment)
Sets the alignment for placement of legend symbols.
QString wrapChar() const
Returns the string used as a wrapping character.
Q_DECL_DEPRECATED void setFontColor(const QColor &c)
Sets the font color used for legend items.
void setWrapChar(const QString &t)
Sets a string to use as a wrapping character.
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
void setStyle(QgsLegendStyle::Style s, const QgsLegendStyle &style)
Sets the style for a legend component.
void setColumnSpace(double s)
Sets the margin space between adjacent columns (in millimeters).
Q_DECL_DEPRECATED void setMapScale(double scale)
Sets the legend map scale.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns the style for a legend component.
void setTitle(const QString &t)
Sets the title for the legend, which will be rendered above all legend items.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
QSizeF wmsLegendSize() const
Returns the size (in millimeters) of WMS legend graphics shown in the legend.
double minimumSymbolSize() const
Returns the minimum symbol size (in mm).
double rasterStrokeWidth() const
Returns the stroke width (in millimeters) for the stroke drawn around raster symbol items.
Q_DECL_DEPRECATED void setLineSpacing(double s)
Sets the line spacing to use between lines of legend text.
void setColumnCount(int c)
Sets the desired minimum number of columns to show in the legend.
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
Q_DECL_DEPRECATED void setMmPerMapUnit(double mmPerMapUnit)
Q_DECL_DEPRECATED void setDpi(int dpi)
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
QSizeF symbolSize() const
Returns the default symbol size (in millimeters) used for legend items.
Q_DECL_DEPRECATED QColor fontColor() const
Returns the font color used for legend items.
double maximumSymbolSize() const
Returns the maximum symbol size (in mm).
QString title() const
Returns the title for the legend, which will be rendered above all legend items.
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
Q_DECL_DEPRECATED double lineSpacing() const
Returns the line spacing to use between lines of legend text.
Q_DECL_DEPRECATED void setUseAdvancedEffects(bool use)
void setSplitLayer(bool s)
Sets whether layer components can be split over multiple columns.
double columnSpace() const
Returns the margin space between adjacent columns (in millimeters).
QgsLegendStyle & rstyle(QgsLegendStyle::Style s)
Returns modifiable reference to the style for a legend component.
void setEqualColumnWidth(bool s)
Sets whether all columns should have equal widths.
void setBoxSpace(double s)
Sets the legend box space (in millimeters), which is the empty margin around the inside of the legend...
void setSynchronousLegendRequests(bool b)
Sets whether to request legend graphics synchronously.
double boxSpace() const
Returns the legend box space (in millimeters), which is the empty margin around the inside of the leg...
void setMaximumSymbolSize(double size)
Set the maximum symbol size for symbol (in millimeters).
bool splitLayer() const
Returns true if layer components can be split over multiple columns.
void setMinimumSymbolSize(double size)
Set the minimum symbol size for symbol (in millimeters).
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
bool equalColumnWidth() const
Returns true if all columns should have equal widths.
void setSymbolSize(QSizeF s)
Sets the default symbol size (in millimeters) used for legend items.
void setWmsLegendSize(QSizeF s)
Sets the desired size (in millimeters) of WMS legend graphics shown in the legend.
Contains detailed styling information relating to how a layout legend should be rendered.
Q_DECL_DEPRECATED QFont font() const
Returns the font used for rendering this legend component.
QgsTextFormat & textFormat()
Returns the text format used for rendering this legend component.
void setMargin(Side side, double margin)
Sets the margin (in mm) for the specified side of the component.
Side
Margin sides.
void readXml(const QDomElement &elem, const QDomDocument &doc, const QgsReadWriteContext &context=QgsReadWriteContext())
Reads the component's style definition from an XML element.
Style
Component of legends which can be styled.
@ Group
Legend group title.
@ Symbol
Symbol icon (excluding label)
@ Subgroup
Legend subgroup title.
@ Title
Legend title.
@ SymbolLabel
Symbol label (excluding icon)
Q_DECL_DEPRECATED void setFont(const QFont &font)
Sets the font used for rendering this legend component.
void writeXml(const QString &name, QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context=QgsReadWriteContext()) const
Writes the component's style definition to an XML element.
void setTextFormat(const QgsTextFormat &format)
Sets the text format used for rendering this legend component.
Base class for all map layer types.
Definition qgsmaplayer.h:76
The QgsMapSettings class contains configuration for rendering of the map.
double scale() const
Returns the calculated map scale.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
A QgsGeometry with associated coordinate reference system.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
Scoped object for saving and restoring a QPainter object's state.
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A legend patch shape entity for QgsStyle databases.
Definition qgsstyle.h:1519
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setLineHeightUnit(Qgis::RenderUnit unit)
Sets the unit for the line height for text.
double size() const
Returns the size for rendered text.
void setLineHeight(double height)
Sets the line height for text.
Represents a vector layer which manages a vector based data sets.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6643
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6642
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
Single variable definition for use within a QgsExpressionContextScope.
Structure that stores all data associated with one map layer.
Contains information relating to the style entity currently being visited.