QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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"
19 #include "qgslayoutrendercontext.h"
20 #include "qgslayoutreportcontext.h"
21 #include "qgslayoututils.h"
22 #include "qgslayoutmodel.h"
23 #include "qgsmapthemecollection.h"
24 #include "qgsannotationmanager.h"
25 #include "qgsannotation.h"
26 #include "qgsmapsettingsutils.h"
27 #include "qgslayertree.h"
28 #include "qgsmaplayerref.h"
29 #include "qgsmaplayerlistutils.h"
31 #include "qgsvectorlayer.h"
32 #include "qgsexpressioncontext.h"
33 #include "qgsapplication.h"
35 
36 #include <QPainter>
37 #include <QStyleOptionGraphicsItem>
38 
40  : QgsLayoutItem( layout )
41 {
42  mBackgroundUpdateTimer = new QTimer( this );
43  mBackgroundUpdateTimer->setSingleShot( true );
44  connect( mBackgroundUpdateTimer, &QTimer::timeout, this, &QgsLayoutItemMap::recreateCachedImageInBackground );
45 
46  assignFreeId();
47 
48  setCacheMode( QGraphicsItem::NoCache );
49 
50  connect( this, &QgsLayoutItem::sizePositionChanged, this, [ = ]
51  {
52  shapeChanged();
53  } );
54 
55  mGridStack = qgis::make_unique< QgsLayoutItemMapGridStack >( this );
56  mOverviewStack = qgis::make_unique< QgsLayoutItemMapOverviewStack >( this );
57 
58  if ( layout )
59  connectUpdateSlot();
60 }
61 
63 {
64  if ( mPainterJob )
65  {
66  disconnect( mPainterJob.get(), &QgsMapRendererCustomPainterJob::finished, this, &QgsLayoutItemMap::painterJobFinished );
67  mPainterJob->cancel(); // blocks
68  mPainter->end();
69  }
70 }
71 
73 {
75 }
76 
78 {
79  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemMap.svg" ) );
80 }
81 
82 QgsLayoutItem::Flags QgsLayoutItemMap::itemFlags() const
83 {
85 }
86 
88 {
89  if ( !mLayout )
90  return;
91 
92  QList<QgsLayoutItemMap *> mapsList;
93  mLayout->layoutItems( mapsList );
94 
95  int maxId = -1;
96  bool used = false;
97  for ( QgsLayoutItemMap *map : qgis::as_const( mapsList ) )
98  {
99  if ( map == this )
100  continue;
101 
102  if ( map->mMapId == mMapId )
103  used = true;
104 
105  maxId = std::max( maxId, map->mMapId );
106  }
107  if ( used )
108  {
109  mMapId = maxId + 1;
110  mLayout->itemsModel()->updateItemDisplayName( this );
111  }
112  updateToolTip();
113 }
114 
116 {
117  if ( !QgsLayoutItem::id().isEmpty() )
118  {
119  return QgsLayoutItem::id();
120  }
121 
122  return tr( "Map %1" ).arg( mMapId );
123 }
124 
126 {
127  return new QgsLayoutItemMap( layout );
128 }
129 
131 {
133 
134  mCachedLayerStyleOverridesPresetName.clear();
135 
136  invalidateCache();
137 
138  updateAtlasFeature();
139 }
140 
142 {
143  if ( rect().isEmpty() )
144  return 0;
145 
146  QgsScaleCalculator calculator;
147  calculator.setMapUnits( crs().mapUnits() );
148  calculator.setDpi( 25.4 ); //Using mm
149  double widthInMm = mLayout->convertFromLayoutUnits( rect().width(), QgsUnitTypes::LayoutMillimeters ).length();
150  return calculator.calculate( extent(), widthInMm );
151 }
152 
153 void QgsLayoutItemMap::setScale( double scaleDenominator, bool forceUpdate )
154 {
155  double currentScaleDenominator = scale();
156 
157  if ( qgsDoubleNear( scaleDenominator, currentScaleDenominator ) || qgsDoubleNear( scaleDenominator, 0.0 ) )
158  {
159  return;
160  }
161 
162  double scaleRatio = scaleDenominator / currentScaleDenominator;
163  mExtent.scale( scaleRatio );
164 
165  if ( mAtlasDriven && mAtlasScalingMode == Fixed )
166  {
167  //if map is atlas controlled and set to fixed scaling mode, then scale changes should be treated as permanent
168  //and also apply to the map's original extent (see #9602)
169  //we can't use the scaleRatio calculated earlier, as the scale can vary depending on extent for geographic coordinate systems
170  QgsScaleCalculator calculator;
171  calculator.setMapUnits( crs().mapUnits() );
172  calculator.setDpi( 25.4 ); //QGraphicsView units are mm
173  scaleRatio = scaleDenominator / calculator.calculate( mExtent, rect().width() );
174  mExtent.scale( scaleRatio );
175  }
176 
177  invalidateCache();
178  if ( forceUpdate )
179  {
180  emit changed();
181  update();
182  }
183  emit extentChanged();
184 }
185 
187 {
188  if ( mExtent == extent )
189  {
190  return;
191  }
192  mExtent = extent;
193 
194  //recalculate data defined scale and extents, since that may override extent
195  refreshMapExtents();
196 
197  //adjust height
198  QRectF currentRect = rect();
199 
200  double newHeight = currentRect.width() * mExtent.height() / mExtent.width();
201 
202  attemptSetSceneRect( QRectF( pos().x(), pos().y(), currentRect.width(), newHeight ) );
203  update();
204 }
205 
207 {
208  QgsRectangle newExtent = extent;
209  QgsRectangle currentExtent = mExtent;
210  //Make sure the width/height ratio is the same as the current layout map extent.
211  //This is to keep the map item frame size fixed
212  double currentWidthHeightRatio = 1.0;
213  if ( !currentExtent.isNull() )
214  currentWidthHeightRatio = currentExtent.width() / currentExtent.height();
215  else
216  currentWidthHeightRatio = rect().width() / rect().height();
217  double newWidthHeightRatio = newExtent.width() / newExtent.height();
218 
219  if ( currentWidthHeightRatio < newWidthHeightRatio )
220  {
221  //enlarge height of new extent, ensuring the map center stays the same
222  double newHeight = newExtent.width() / currentWidthHeightRatio;
223  double deltaHeight = newHeight - newExtent.height();
224  newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
225  newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
226  }
227  else
228  {
229  //enlarge width of new extent, ensuring the map center stays the same
230  double newWidth = currentWidthHeightRatio * newExtent.height();
231  double deltaWidth = newWidth - newExtent.width();
232  newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
233  newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
234  }
235 
236  if ( mExtent == newExtent )
237  {
238  return;
239  }
240  mExtent = newExtent;
241 
242  //recalculate data defined scale and extents, since that may override extent
243  refreshMapExtents();
244 
245  invalidateCache();
246  emit changed();
247  emit extentChanged();
248 }
249 
251 {
252  return mExtent;
253 }
254 
256 {
257  QPolygonF poly;
258  mapPolygon( mExtent, poly );
259  return poly;
260 }
261 
263 {
264  if ( mCrs.isValid() )
265  return mCrs;
266  else if ( mLayout && mLayout->project() )
267  return mLayout->project()->crs();
269 }
270 
272 {
273  mCrs = crs;
274 }
275 
276 QList<QgsMapLayer *> QgsLayoutItemMap::layers() const
277 {
278  return _qgis_listRefToRaw( mLayers );
279 }
280 
281 void QgsLayoutItemMap::setLayers( const QList<QgsMapLayer *> &layers )
282 {
283  mLayers = _qgis_listRawToRef( layers );
284 }
285 
286 void QgsLayoutItemMap::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
287 {
288  if ( overrides == mLayerStyleOverrides )
289  return;
290 
291  mLayerStyleOverrides = overrides;
292  emit layerStyleOverridesChanged(); // associated legends may listen to this
293 
294 }
295 
297 {
298  mLayerStyleOverrides.clear();
299  for ( const QgsMapLayerRef &layerRef : qgis::as_const( mLayers ) )
300  {
301  if ( QgsMapLayer *layer = layerRef.get() )
302  {
303  QgsMapLayerStyle style;
304  style.readFromLayer( layer );
305  mLayerStyleOverrides.insert( layer->id(), style.xmlData() );
306  }
307  }
308 }
309 
310 void QgsLayoutItemMap::moveContent( double dx, double dy )
311 {
312  mLastRenderedImageOffsetX -= dx;
313  mLastRenderedImageOffsetY -= dy;
314  if ( !mDrawing )
315  {
316  transformShift( dx, dy );
317  mExtent.setXMinimum( mExtent.xMinimum() + dx );
318  mExtent.setXMaximum( mExtent.xMaximum() + dx );
319  mExtent.setYMinimum( mExtent.yMinimum() + dy );
320  mExtent.setYMaximum( mExtent.yMaximum() + dy );
321 
322  //in case data defined extents are set, these override the calculated values
323  refreshMapExtents();
324 
325  invalidateCache();
326  emit changed();
327  emit extentChanged();
328  }
329 }
330 
331 void QgsLayoutItemMap::zoomContent( double factor, QPointF point )
332 {
333  if ( mDrawing )
334  {
335  return;
336  }
337 
338  //find out map coordinates of position
339  double mapX = mExtent.xMinimum() + ( point.x() / rect().width() ) * ( mExtent.xMaximum() - mExtent.xMinimum() );
340  double mapY = mExtent.yMinimum() + ( 1 - ( point.y() / rect().height() ) ) * ( mExtent.yMaximum() - mExtent.yMinimum() );
341 
342  //find out new center point
343  double centerX = ( mExtent.xMaximum() + mExtent.xMinimum() ) / 2;
344  double centerY = ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2;
345 
346  centerX = mapX + ( centerX - mapX ) * ( 1.0 / factor );
347  centerY = mapY + ( centerY - mapY ) * ( 1.0 / factor );
348 
349  double newIntervalX, newIntervalY;
350 
351  if ( factor > 0 )
352  {
353  newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) / factor;
354  newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) / factor;
355  }
356  else //no need to zoom
357  {
358  return;
359  }
360 
361  mExtent.setXMaximum( centerX + newIntervalX / 2 );
362  mExtent.setXMinimum( centerX - newIntervalX / 2 );
363  mExtent.setYMaximum( centerY + newIntervalY / 2 );
364  mExtent.setYMinimum( centerY - newIntervalY / 2 );
365 
366  if ( mAtlasDriven && mAtlasScalingMode == Fixed )
367  {
368  //if map is atlas controlled and set to fixed scaling mode, then scale changes should be treated as permanent
369  //and also apply to the map's original extent (see #9602)
370  //we can't use the scaleRatio calculated earlier, as the scale can vary depending on extent for geographic coordinate systems
371  QgsScaleCalculator calculator;
372  calculator.setMapUnits( crs().mapUnits() );
373  calculator.setDpi( 25.4 ); //QGraphicsView units are mm
374  double scaleRatio = scale() / calculator.calculate( mExtent, rect().width() );
375  mExtent.scale( scaleRatio );
376  }
377 
378  //recalculate data defined scale and extents, since that may override zoom
379  refreshMapExtents();
380 
381  invalidateCache();
382  emit changed();
383  emit extentChanged();
384 }
385 
387 {
388  const QList< QgsMapLayer * > layers = layersToRender();
389  for ( QgsMapLayer *layer : layers )
390  {
391  if ( layer->dataProvider() && layer->dataProvider()->name() == QLatin1String( "wms" ) )
392  {
393  return true;
394  }
395  }
396  return false;
397 }
398 
400 {
402  return true;
403 
404  // we MUST force the whole layout to render as a raster if any map item
405  // uses blend modes, and we are not drawing on a solid opaque background
406  // because in this case the map item needs to be rendered as a raster, but
407  // it also needs to interact with items below it
408  if ( !containsAdvancedEffects() )
409  return false;
410 
411  // TODO layer transparency is probably ok to allow without forcing rasterization
412 
413  if ( hasBackground() && qgsDoubleNear( backgroundColor().alphaF(), 1.0 ) )
414  return false;
415 
416  return true;
417 }
418 
420 {
422  return true;
423 
424  //check easy things first
425 
426  //overviews
427  if ( mOverviewStack->containsAdvancedEffects() )
428  {
429  return true;
430  }
431 
432  //grids
433  if ( mGridStack->containsAdvancedEffects() )
434  {
435  return true;
436  }
437 
438  QgsMapSettings ms;
439  ms.setLayers( layersToRender() );
440  return ( !QgsMapSettingsUtils::containsAdvancedEffects( ms ).isEmpty() );
441 }
442 
443 void QgsLayoutItemMap::setMapRotation( double rotation )
444 {
445  mMapRotation = rotation;
446  mEvaluatedMapRotation = mMapRotation;
447  invalidateCache();
448  emit mapRotationChanged( rotation );
449  emit changed();
450 }
451 
453 {
454  return valueType == QgsLayoutObject::EvaluatedValue ? mEvaluatedMapRotation : mMapRotation;
455 
456 }
457 
459 {
460  mAtlasDriven = enabled;
461 
462  if ( !enabled )
463  {
464  //if not enabling the atlas, we still need to refresh the map extents
465  //so that data defined extents and scale are recalculated
466  refreshMapExtents();
467  }
468 }
469 
471 {
472  if ( valueType == QgsLayoutObject::EvaluatedValue )
473  {
474  //evaluate data defined atlas margin
475 
476  //start with user specified margin
477  double margin = mAtlasMargin;
479 
480  bool ok = false;
481  double ddMargin = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapAtlasMargin, context, 0.0, &ok );
482  if ( ok )
483  {
484  //divide by 100 to convert to 0 -> 1.0 range
485  margin = ddMargin / 100;
486  }
487  return margin;
488  }
489  else
490  {
491  return mAtlasMargin;
492  }
493 }
494 
496 {
497  if ( mGridStack->size() < 1 )
498  {
499  QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( tr( "Grid %1" ).arg( 1 ), this );
500  mGridStack->addGrid( grid );
501  }
502  return mGridStack->grid( 0 );
503 }
504 
506 {
507  if ( mOverviewStack->size() < 1 )
508  {
509  QgsLayoutItemMapOverview *overview = new QgsLayoutItemMapOverview( tr( "Overview %1" ).arg( 1 ), this );
510  mOverviewStack->addOverview( overview );
511  }
512  return mOverviewStack->overview( 0 );
513 }
514 
516 {
517 }
518 
519 bool QgsLayoutItemMap::writePropertiesToElement( QDomElement &mapElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
520 {
521  if ( mKeepLayerSet )
522  {
523  mapElem.setAttribute( QStringLiteral( "keepLayerSet" ), QStringLiteral( "true" ) );
524  }
525  else
526  {
527  mapElem.setAttribute( QStringLiteral( "keepLayerSet" ), QStringLiteral( "false" ) );
528  }
529 
530  if ( mDrawAnnotations )
531  {
532  mapElem.setAttribute( QStringLiteral( "drawCanvasItems" ), QStringLiteral( "true" ) );
533  }
534  else
535  {
536  mapElem.setAttribute( QStringLiteral( "drawCanvasItems" ), QStringLiteral( "false" ) );
537  }
538 
539  //extent
540  QDomElement extentElem = doc.createElement( QStringLiteral( "Extent" ) );
541  extentElem.setAttribute( QStringLiteral( "xmin" ), qgsDoubleToString( mExtent.xMinimum() ) );
542  extentElem.setAttribute( QStringLiteral( "xmax" ), qgsDoubleToString( mExtent.xMaximum() ) );
543  extentElem.setAttribute( QStringLiteral( "ymin" ), qgsDoubleToString( mExtent.yMinimum() ) );
544  extentElem.setAttribute( QStringLiteral( "ymax" ), qgsDoubleToString( mExtent.yMaximum() ) );
545  mapElem.appendChild( extentElem );
546 
547  if ( mCrs.isValid() )
548  {
549  QDomElement crsElem = doc.createElement( QStringLiteral( "crs" ) );
550  mCrs.writeXml( crsElem, doc );
551  mapElem.appendChild( crsElem );
552  }
553 
554  // follow map theme
555  mapElem.setAttribute( QStringLiteral( "followPreset" ), mFollowVisibilityPreset ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
556  mapElem.setAttribute( QStringLiteral( "followPresetName" ), mFollowVisibilityPresetName );
557 
558  //map rotation
559  mapElem.setAttribute( QStringLiteral( "mapRotation" ), QString::number( mMapRotation ) );
560 
561  //layer set
562  QDomElement layerSetElem = doc.createElement( QStringLiteral( "LayerSet" ) );
563  for ( const QgsMapLayerRef &layerRef : mLayers )
564  {
565  if ( !layerRef )
566  continue;
567  QDomElement layerElem = doc.createElement( QStringLiteral( "Layer" ) );
568  QDomText layerIdText = doc.createTextNode( layerRef.layerId );
569  layerElem.appendChild( layerIdText );
570 
571  layerElem.setAttribute( QStringLiteral( "name" ), layerRef.name );
572  layerElem.setAttribute( QStringLiteral( "source" ), layerRef.source );
573  layerElem.setAttribute( QStringLiteral( "provider" ), layerRef.provider );
574 
575  layerSetElem.appendChild( layerElem );
576  }
577  mapElem.appendChild( layerSetElem );
578 
579  // override styles
580  if ( mKeepLayerStyles )
581  {
582  QDomElement stylesElem = doc.createElement( QStringLiteral( "LayerStyles" ) );
583  for ( auto styleIt = mLayerStyleOverrides.constBegin(); styleIt != mLayerStyleOverrides.constEnd(); ++styleIt )
584  {
585  QDomElement styleElem = doc.createElement( QStringLiteral( "LayerStyle" ) );
586 
587  QgsMapLayerRef ref( styleIt.key() );
588  ref.resolve( mLayout->project() );
589 
590  styleElem.setAttribute( QStringLiteral( "layerid" ), ref.layerId );
591  styleElem.setAttribute( QStringLiteral( "name" ), ref.name );
592  styleElem.setAttribute( QStringLiteral( "source" ), ref.source );
593  styleElem.setAttribute( QStringLiteral( "provider" ), ref.provider );
594 
595  QgsMapLayerStyle style( styleIt.value() );
596  style.writeXml( styleElem );
597  stylesElem.appendChild( styleElem );
598  }
599  mapElem.appendChild( stylesElem );
600  }
601 
602  //grids
603  mGridStack->writeXml( mapElem, doc, context );
604 
605  //overviews
606  mOverviewStack->writeXml( mapElem, doc, context );
607 
608  //atlas
609  QDomElement atlasElem = doc.createElement( QStringLiteral( "AtlasMap" ) );
610  atlasElem.setAttribute( QStringLiteral( "atlasDriven" ), mAtlasDriven );
611  atlasElem.setAttribute( QStringLiteral( "scalingMode" ), mAtlasScalingMode );
612  atlasElem.setAttribute( QStringLiteral( "margin" ), qgsDoubleToString( mAtlasMargin ) );
613  mapElem.appendChild( atlasElem );
614 
615  mapElem.setAttribute( QStringLiteral( "labelMargin" ), mLabelMargin.encodeMeasurement() );
616  mapElem.setAttribute( QStringLiteral( "mapFlags" ), static_cast< int>( mMapFlags ) );
617 
618  QDomElement labelBlockingItemsElem = doc.createElement( QStringLiteral( "labelBlockingItems" ) );
619  for ( const auto &item : qgis::as_const( mBlockingLabelItems ) )
620  {
621  if ( !item )
622  continue;
623 
624  QDomElement blockingItemElem = doc.createElement( QStringLiteral( "item" ) );
625  blockingItemElem.setAttribute( QStringLiteral( "uuid" ), item->uuid() );
626  labelBlockingItemsElem.appendChild( blockingItemElem );
627  }
628  mapElem.appendChild( labelBlockingItemsElem );
629 
630  return true;
631 }
632 
633 bool QgsLayoutItemMap::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
634 {
635  mUpdatesEnabled = false;
636 
637  //extent
638  QDomNodeList extentNodeList = itemElem.elementsByTagName( QStringLiteral( "Extent" ) );
639  if ( !extentNodeList.isEmpty() )
640  {
641  QDomElement extentElem = extentNodeList.at( 0 ).toElement();
642  double xmin, xmax, ymin, ymax;
643  xmin = extentElem.attribute( QStringLiteral( "xmin" ) ).toDouble();
644  xmax = extentElem.attribute( QStringLiteral( "xmax" ) ).toDouble();
645  ymin = extentElem.attribute( QStringLiteral( "ymin" ) ).toDouble();
646  ymax = extentElem.attribute( QStringLiteral( "ymax" ) ).toDouble();
647  setExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
648  }
649 
650  QDomNodeList crsNodeList = itemElem.elementsByTagName( QStringLiteral( "crs" ) );
651  if ( !crsNodeList.isEmpty() )
652  {
653  QDomElement crsElem = crsNodeList.at( 0 ).toElement();
654  mCrs.readXml( crsElem );
655  }
656  else
657  {
659  }
660 
661  //map rotation
662  mMapRotation = itemElem.attribute( QStringLiteral( "mapRotation" ), QStringLiteral( "0" ) ).toDouble();
663 
664  // follow map theme
665  mFollowVisibilityPreset = itemElem.attribute( QStringLiteral( "followPreset" ) ).compare( QLatin1String( "true" ) ) == 0;
666  mFollowVisibilityPresetName = itemElem.attribute( QStringLiteral( "followPresetName" ) );
667 
668  //mKeepLayerSet flag
669  QString keepLayerSetFlag = itemElem.attribute( QStringLiteral( "keepLayerSet" ) );
670  if ( keepLayerSetFlag.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
671  {
672  mKeepLayerSet = true;
673  }
674  else
675  {
676  mKeepLayerSet = false;
677  }
678 
679  QString drawCanvasItemsFlag = itemElem.attribute( QStringLiteral( "drawCanvasItems" ), QStringLiteral( "true" ) );
680  if ( drawCanvasItemsFlag.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
681  {
682  mDrawAnnotations = true;
683  }
684  else
685  {
686  mDrawAnnotations = false;
687  }
688 
689  mLayerStyleOverrides.clear();
690 
691  //mLayers
692  mLayers.clear();
693  QDomNodeList layerSetNodeList = itemElem.elementsByTagName( QStringLiteral( "LayerSet" ) );
694  if ( !layerSetNodeList.isEmpty() )
695  {
696  QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
697  QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( QStringLiteral( "Layer" ) );
698  mLayers.reserve( layerIdNodeList.size() );
699  for ( int i = 0; i < layerIdNodeList.size(); ++i )
700  {
701  QDomElement layerElem = layerIdNodeList.at( i ).toElement();
702  QString layerId = layerElem.text();
703  QString layerName = layerElem.attribute( QStringLiteral( "name" ) );
704  QString layerSource = layerElem.attribute( QStringLiteral( "source" ) );
705  QString layerProvider = layerElem.attribute( QStringLiteral( "provider" ) );
706 
707  QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
708  ref.resolveWeakly( mLayout->project() );
709  mLayers << ref;
710  }
711  }
712 
713  // override styles
714  QDomNodeList layerStylesNodeList = itemElem.elementsByTagName( QStringLiteral( "LayerStyles" ) );
715  mKeepLayerStyles = !layerStylesNodeList.isEmpty();
716  if ( mKeepLayerStyles )
717  {
718  QDomElement layerStylesElem = layerStylesNodeList.at( 0 ).toElement();
719  QDomNodeList layerStyleNodeList = layerStylesElem.elementsByTagName( QStringLiteral( "LayerStyle" ) );
720  for ( int i = 0; i < layerStyleNodeList.size(); ++i )
721  {
722  const QDomElement &layerStyleElement = layerStyleNodeList.at( i ).toElement();
723  QString layerId = layerStyleElement.attribute( QStringLiteral( "layerid" ) );
724  QString layerName = layerStyleElement.attribute( QStringLiteral( "name" ) );
725  QString layerSource = layerStyleElement.attribute( QStringLiteral( "source" ) );
726  QString layerProvider = layerStyleElement.attribute( QStringLiteral( "provider" ) );
727  QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
728  ref.resolveWeakly( mLayout->project() );
729 
730  QgsMapLayerStyle style;
731  style.readXml( layerStyleElement );
732  mLayerStyleOverrides.insert( ref.layerId, style.xmlData() );
733  }
734  }
735 
736  mDrawing = false;
737  mNumCachedLayers = 0;
738  mCacheInvalidated = true;
739 
740  //overviews
741  mOverviewStack->readXml( itemElem, doc, context );
742 
743  //grids
744  mGridStack->readXml( itemElem, doc, context );
745 
746  //atlas
747  QDomNodeList atlasNodeList = itemElem.elementsByTagName( QStringLiteral( "AtlasMap" ) );
748  if ( !atlasNodeList.isEmpty() )
749  {
750  QDomElement atlasElem = atlasNodeList.at( 0 ).toElement();
751  mAtlasDriven = ( atlasElem.attribute( QStringLiteral( "atlasDriven" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
752  if ( atlasElem.hasAttribute( QStringLiteral( "fixedScale" ) ) ) // deprecated XML
753  {
754  mAtlasScalingMode = ( atlasElem.attribute( QStringLiteral( "fixedScale" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) ) ? Fixed : Auto;
755  }
756  else if ( atlasElem.hasAttribute( QStringLiteral( "scalingMode" ) ) )
757  {
758  mAtlasScalingMode = static_cast<AtlasScalingMode>( atlasElem.attribute( QStringLiteral( "scalingMode" ) ).toInt() );
759  }
760  mAtlasMargin = atlasElem.attribute( QStringLiteral( "margin" ), QStringLiteral( "0.1" ) ).toDouble();
761  }
762 
763  setLabelMargin( QgsLayoutMeasurement::decodeMeasurement( itemElem.attribute( QStringLiteral( "labelMargin" ), QStringLiteral( "0" ) ) ) );
764 
765  mMapFlags = static_cast< MapItemFlags>( itemElem.attribute( QStringLiteral( "mapFlags" ), nullptr ).toInt() );
766 
767  // label blocking items
768  mBlockingLabelItems.clear();
769  mBlockingLabelItemUuids.clear();
770  QDomNodeList labelBlockingNodeList = itemElem.elementsByTagName( QStringLiteral( "labelBlockingItems" ) );
771  if ( !labelBlockingNodeList.isEmpty() )
772  {
773  QDomElement blockingItems = labelBlockingNodeList.at( 0 ).toElement();
774  QDomNodeList labelBlockingNodeList = blockingItems.childNodes();
775  for ( int i = 0; i < labelBlockingNodeList.size(); ++i )
776  {
777  const QDomElement &itemBlockingElement = labelBlockingNodeList.at( i ).toElement();
778  const QString itemUuid = itemBlockingElement.attribute( QStringLiteral( "uuid" ) );
779  mBlockingLabelItemUuids << itemUuid;
780  }
781  }
782 
784 
785  mUpdatesEnabled = true;
786  return true;
787 }
788 
789 void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem *style, QWidget * )
790 {
791  if ( !mLayout || !painter || !painter->device() || !mUpdatesEnabled )
792  {
793  return;
794  }
795  if ( !shouldDrawItem() )
796  {
797  return;
798  }
799 
800  QRectF thisPaintRect = rect();
801  if ( qgsDoubleNear( thisPaintRect.width(), 0.0 ) || qgsDoubleNear( thisPaintRect.height(), 0 ) )
802  return;
803 
804  //TODO - try to reduce the amount of duplicate code here!
805 
806  if ( mLayout->renderContext().isPreviewRender() )
807  {
808  painter->save();
809  painter->setClipRect( thisPaintRect );
810  if ( !mCacheFinalImage || mCacheFinalImage->isNull() )
811  {
812  // No initial render available - so draw some preview text alerting user
813  painter->setBrush( QBrush( QColor( 125, 125, 125, 125 ) ) );
814  painter->drawRect( thisPaintRect );
815  painter->setBrush( Qt::NoBrush );
816  QFont messageFont;
817  messageFont.setPointSize( 12 );
818  painter->setFont( messageFont );
819  painter->setPen( QColor( 255, 255, 255, 255 ) );
820  painter->drawText( thisPaintRect, Qt::AlignCenter | Qt::AlignHCenter, tr( "Rendering map" ) );
821  if ( mPainterJob && mCacheInvalidated && !mDrawingPreview )
822  {
823  // current job was invalidated - start a new one
824  mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( style );
825  mBackgroundUpdateTimer->start( 1 );
826  }
827  else if ( !mPainterJob && !mDrawingPreview )
828  {
829  // this is the map's very first paint - trigger a cache update
830  mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( style );
831  mBackgroundUpdateTimer->start( 1 );
832  }
833  }
834  else
835  {
836  if ( mCacheInvalidated && !mDrawingPreview )
837  {
838  // cache was invalidated - trigger a background update
839  mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( style );
840  mBackgroundUpdateTimer->start( 1 );
841  }
842 
843  //Background color is already included in cached image, so no need to draw
844 
845  double imagePixelWidth = mCacheFinalImage->width(); //how many pixels of the image are for the map extent?
846  double scale = rect().width() / imagePixelWidth;
847 
848  painter->save();
849 
850  painter->translate( mLastRenderedImageOffsetX + mXOffset, mLastRenderedImageOffsetY + mYOffset );
851  painter->scale( scale, scale );
852  painter->drawImage( 0, 0, *mCacheFinalImage );
853 
854  //restore rotation
855  painter->restore();
856  }
857 
858  painter->setClipRect( thisPaintRect, Qt::NoClip );
859 
860  if ( shouldDrawPart( OverviewMapExtent ) )
861  {
862  mOverviewStack->drawItems( painter, false );
863  }
864  if ( shouldDrawPart( Grid ) )
865  {
866  mGridStack->drawItems( painter );
867  }
868  drawAnnotations( painter );
869  if ( shouldDrawPart( Frame ) )
870  {
871  drawMapFrame( painter );
872  }
873  painter->restore();
874  }
875  else
876  {
877  if ( mDrawing )
878  return;
879 
880  mDrawing = true;
881  QPaintDevice *paintDevice = painter->device();
882  if ( !paintDevice )
883  return;
884 
885  QgsRectangle cExtent = extent();
886  QSizeF size( cExtent.width() * mapUnitsToLayoutUnits(), cExtent.height() * mapUnitsToLayoutUnits() );
887 
888  if ( containsAdvancedEffects() && ( !mLayout || !( mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagForceVectorOutput ) ) )
889  {
890  // rasterize
891  double destinationDpi = QgsLayoutUtils::scaleFactorFromItemStyle( style ) * 25.4;
892  double layoutUnitsInInches = mLayout ? mLayout->convertFromLayoutUnits( 1, QgsUnitTypes::LayoutInches ).length() : 1;
893  int widthInPixels = static_cast< int >( std::round( boundingRect().width() * layoutUnitsInInches * destinationDpi ) );
894  int heightInPixels = static_cast< int >( std::round( boundingRect().height() * layoutUnitsInInches * destinationDpi ) );
895  QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
896 
897  image.fill( Qt::transparent );
898  image.setDotsPerMeterX( static_cast< int >( std::round( 1000 * destinationDpi / 25.4 ) ) );
899  image.setDotsPerMeterY( static_cast< int >( std::round( 1000 * destinationDpi / 25.4 ) ) );
900  double dotsPerMM = destinationDpi / 25.4;
901  QPainter p( &image );
902 
903  QPointF tl = -boundingRect().topLeft();
904  QRect imagePaintRect( static_cast< int >( std::round( tl.x() * dotsPerMM ) ),
905  static_cast< int >( std::round( tl.y() * dotsPerMM ) ),
906  static_cast< int >( std::round( thisPaintRect.width() * dotsPerMM ) ),
907  static_cast< int >( std::round( thisPaintRect.height() * dotsPerMM ) ) );
908  p.setClipRect( imagePaintRect );
909 
910  p.translate( imagePaintRect.topLeft() );
911 
912  // Fill with background color - must be drawn onto the flattened image
913  // so that layers with opacity or blend modes can correctly interact with it
914  if ( shouldDrawPart( Background ) )
915  {
916  p.scale( dotsPerMM, dotsPerMM );
917  drawMapBackground( &p );
918  p.scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
919  }
920 
921  drawMap( &p, cExtent, imagePaintRect.size(), image.logicalDpiX() );
922 
923  // important - all other items, overviews, grids etc must be rendered to the
924  // flattened image, in case these have blend modes must need to interact
925  // with the map
926  p.scale( dotsPerMM, dotsPerMM );
927 
928  if ( shouldDrawPart( OverviewMapExtent ) )
929  {
930  mOverviewStack->drawItems( &p, false );
931  }
932  if ( shouldDrawPart( Grid ) )
933  {
934  mGridStack->drawItems( &p );
935  }
936  drawAnnotations( &p );
937 
938  painter->save();
939  painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
940  painter->drawImage( QPointF( -tl.x()* dotsPerMM, -tl.y() * dotsPerMM ), image );
941  painter->scale( dotsPerMM, dotsPerMM );
942  painter->restore();
943  }
944  else
945  {
946  // Fill with background color
947  if ( shouldDrawPart( Background ) )
948  {
949  drawMapBackground( painter );
950  }
951 
952  painter->save();
953  painter->setClipRect( thisPaintRect );
954  painter->save();
955  painter->translate( mXOffset, mYOffset );
956 
957  double dotsPerMM = paintDevice->logicalDpiX() / 25.4;
958  size *= dotsPerMM; // output size will be in dots (pixels)
959  painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
960  drawMap( painter, cExtent, size, paintDevice->logicalDpiX() );
961 
962  painter->restore();
963 
964  painter->setClipRect( thisPaintRect, Qt::NoClip );
965 
966  if ( shouldDrawPart( OverviewMapExtent ) )
967  {
968  mOverviewStack->drawItems( painter, false );
969  }
970  if ( shouldDrawPart( Grid ) )
971  {
972  mGridStack->drawItems( painter );
973  }
974  drawAnnotations( painter );
975  painter->restore();
976  }
977 
978  if ( shouldDrawPart( Frame ) )
979  {
980  drawMapFrame( painter );
981  }
982  mDrawing = false;
983  }
984 }
985 
987 {
988  return ( hasBackground() ? 1 : 0 )
989  + layersToRender().length()
990  + 1 // for grids, if they exist
991  + 1 // for overviews, if they exist
992  + ( frameEnabled() ? 1 : 0 );
993 }
994 
996 {
999 }
1000 
1001 void QgsLayoutItemMap::drawMap( QPainter *painter, const QgsRectangle &extent, QSizeF size, double dpi )
1002 {
1003  if ( !painter )
1004  {
1005  return;
1006  }
1007  if ( qgsDoubleNear( size.width(), 0.0 ) || qgsDoubleNear( size.height(), 0.0 ) )
1008  {
1009  //don't attempt to draw if size is invalid
1010  return;
1011  }
1012 
1013  // render
1014  QgsMapSettings ms( mapSettings( extent, size, dpi, true ) );
1015  if ( shouldDrawPart( OverviewMapExtent ) )
1016  {
1017  ms.setLayers( mOverviewStack->modifyMapLayerList( ms.layers() ) );
1018  }
1019 
1020  QgsMapRendererCustomPainterJob job( ms, painter );
1021  // Render the map in this thread. This is done because of problems
1022  // with printing to printer on Windows (printing to PDF is fine though).
1023  // Raster images were not displayed - see #10599
1024  job.renderSynchronously();
1025 
1026  mRenderingErrors = job.errors();
1027 }
1028 
1029 void QgsLayoutItemMap::recreateCachedImageInBackground()
1030 {
1031  if ( mPainterJob )
1032  {
1033  disconnect( mPainterJob.get(), &QgsMapRendererCustomPainterJob::finished, this, &QgsLayoutItemMap::painterJobFinished );
1034  QgsMapRendererCustomPainterJob *oldJob = mPainterJob.release();
1035  QPainter *oldPainter = mPainter.release();
1036  QImage *oldImage = mCacheRenderingImage.release();
1037  connect( oldJob, &QgsMapRendererCustomPainterJob::finished, this, [oldPainter, oldJob, oldImage]
1038  {
1039  oldJob->deleteLater();
1040  delete oldPainter;
1041  delete oldImage;
1042  } );
1043  oldJob->cancelWithoutBlocking();
1044  }
1045  else
1046  {
1047  mCacheRenderingImage.reset( nullptr );
1048  }
1049 
1050  Q_ASSERT( !mPainterJob );
1051  Q_ASSERT( !mPainter );
1052  Q_ASSERT( !mCacheRenderingImage );
1053 
1054  QgsRectangle ext = extent();
1055  double widthLayoutUnits = ext.width() * mapUnitsToLayoutUnits();
1056  double heightLayoutUnits = ext.height() * mapUnitsToLayoutUnits();
1057 
1058  int w = static_cast< int >( std::round( widthLayoutUnits * mPreviewScaleFactor ) );
1059  int h = static_cast< int >( std::round( heightLayoutUnits * mPreviewScaleFactor ) );
1060 
1061  // limit size of image for better performance
1062  if ( w > 5000 || h > 5000 )
1063  {
1064  if ( w > h )
1065  {
1066  w = 5000;
1067  h = static_cast< int>( std::round( w * heightLayoutUnits / widthLayoutUnits ) );
1068  }
1069  else
1070  {
1071  h = 5000;
1072  w = static_cast< int >( std::round( h * widthLayoutUnits / heightLayoutUnits ) );
1073  }
1074  }
1075 
1076  if ( w <= 0 || h <= 0 )
1077  return;
1078 
1079  mCacheRenderingImage.reset( new QImage( w, h, QImage::Format_ARGB32 ) );
1080 
1081  // set DPI of the image
1082  mCacheRenderingImage->setDotsPerMeterX( static_cast< int >( std::round( 1000 * w / widthLayoutUnits ) ) );
1083  mCacheRenderingImage->setDotsPerMeterY( static_cast< int >( std::round( 1000 * h / heightLayoutUnits ) ) );
1084 
1085  if ( hasBackground() )
1086  {
1087  //Initially fill image with specified background color. This ensures that layers with blend modes will
1088  //preview correctly
1089  mCacheRenderingImage->fill( backgroundColor().rgba() );
1090  }
1091  else
1092  {
1093  //no background, but start with empty fill to avoid artifacts
1094  mCacheRenderingImage->fill( QColor( 255, 255, 255, 0 ).rgba() );
1095  }
1096 
1097  mCacheInvalidated = false;
1098  mPainter.reset( new QPainter( mCacheRenderingImage.get() ) );
1099  QgsMapSettings settings( mapSettings( ext, QSizeF( w, h ), mCacheRenderingImage->logicalDpiX(), true ) );
1100 
1101  if ( shouldDrawPart( OverviewMapExtent ) )
1102  {
1103  settings.setLayers( mOverviewStack->modifyMapLayerList( settings.layers() ) );
1104  }
1105 
1106  mPainterJob.reset( new QgsMapRendererCustomPainterJob( settings, mPainter.get() ) );
1107  connect( mPainterJob.get(), &QgsMapRendererCustomPainterJob::finished, this, &QgsLayoutItemMap::painterJobFinished );
1108  mPainterJob->start();
1109 
1110  // from now on we can accept refresh requests again
1111  // this must be reset only after the job has been started, because
1112  // some providers (yes, it's you WCS and AMS!) during preparation
1113  // do network requests and start an internal event loop, which may
1114  // end up calling refresh() and would schedule another refresh,
1115  // deleting the one we have just started.
1116 
1117  // ^^ that comment was directly copied from a similar fix in QgsMapCanvas. And
1118  // with little surprise, both those providers are still badly behaved and causing
1119  // annoying bugs for us to deal with...
1120  mDrawingPreview = false;
1121 }
1122 
1123 QgsLayoutItemMap::MapItemFlags QgsLayoutItemMap::mapFlags() const
1124 {
1125  return mMapFlags;
1126 }
1127 
1128 void QgsLayoutItemMap::setMapFlags( QgsLayoutItemMap::MapItemFlags mapFlags )
1129 {
1130  mMapFlags = mapFlags;
1131 }
1132 
1133 QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF size, double dpi, bool includeLayerSettings ) const
1134 {
1135  QgsExpressionContext expressionContext = createExpressionContext();
1136  QgsCoordinateReferenceSystem renderCrs = crs();
1137 
1138  QgsMapSettings jobMapSettings;
1139  jobMapSettings.setDestinationCrs( renderCrs );
1140  jobMapSettings.setExtent( extent );
1141  jobMapSettings.setOutputSize( size.toSize() );
1142  jobMapSettings.setOutputDpi( dpi );
1143  jobMapSettings.setBackgroundColor( Qt::transparent );
1144  jobMapSettings.setRotation( mEvaluatedMapRotation );
1145  if ( mLayout )
1146  jobMapSettings.setEllipsoid( mLayout->project()->ellipsoid() );
1147 
1148  if ( includeLayerSettings )
1149  {
1150  //set layers to render
1151  QList<QgsMapLayer *> layers = layersToRender( &expressionContext );
1152  if ( mLayout && -1 != mLayout->renderContext().currentExportLayer() )
1153  {
1154  const int layerIdx = mLayout->renderContext().currentExportLayer() - ( hasBackground() ? 1 : 0 );
1155  if ( layerIdx >= 0 && layerIdx < layers.length() )
1156  {
1157  // exporting with separate layers (e.g., to svg layers), so we only want to render a single map layer
1158  QgsMapLayer *ml = layers[ layers.length() - layerIdx - 1 ];
1159  layers.clear();
1160  layers << ml;
1161  }
1162  else
1163  {
1164  // exporting decorations such as map frame/grid/overview, so no map layers required
1165  layers.clear();
1166  }
1167  }
1168  jobMapSettings.setLayers( layers );
1169  jobMapSettings.setLayerStyleOverrides( layerStyleOverridesToRender( expressionContext ) );
1170  }
1171 
1172  if ( !mLayout->renderContext().isPreviewRender() )
1173  {
1174  //if outputting layout, disable optimisations like layer simplification
1175  jobMapSettings.setFlag( QgsMapSettings::UseRenderingOptimization, false );
1176  }
1177 
1178  jobMapSettings.setExpressionContext( expressionContext );
1179 
1180  // layout-specific overrides of flags
1181  jobMapSettings.setFlag( QgsMapSettings::ForceVectorOutput, true ); // force vector output (no caching of marker images etc.)
1182  jobMapSettings.setFlag( QgsMapSettings::Antialiasing, mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagAntialiasing );
1183  jobMapSettings.setFlag( QgsMapSettings::DrawEditingInfo, false );
1184  jobMapSettings.setSelectionColor( mLayout->renderContext().selectionColor() );
1185  jobMapSettings.setFlag( QgsMapSettings::DrawSelection, mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagDrawSelection );
1188  jobMapSettings.setTransformContext( mLayout->project()->transformContext() );
1189  jobMapSettings.setPathResolver( mLayout->project()->pathResolver() );
1190 
1191  QgsLabelingEngineSettings labelSettings = mLayout->project()->labelingEngineSettings();
1192 
1193  // override project "show partial labels" setting with this map's setting
1195  jobMapSettings.setLabelingEngineSettings( labelSettings );
1196 
1197  // override the default text render format inherited from the labeling engine settings using the layout's render context setting
1198  jobMapSettings.setTextRenderFormat( mLayout->renderContext().textRenderFormat() );
1199 
1200  if ( mEvaluatedLabelMargin.length() > 0 )
1201  {
1202  QPolygonF visiblePoly = jobMapSettings.visiblePolygon();
1203  visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
1204  const double layoutLabelMargin = mLayout->convertToLayoutUnits( mEvaluatedLabelMargin );
1205  const double layoutLabelMarginInMapUnits = layoutLabelMargin / rect().width() * jobMapSettings.extent().width();
1206  QgsGeometry mapBoundaryGeom = QgsGeometry::fromQPolygonF( visiblePoly );
1207  mapBoundaryGeom = mapBoundaryGeom.buffer( -layoutLabelMarginInMapUnits, 0 );
1208  jobMapSettings.setLabelBoundaryGeometry( mapBoundaryGeom );
1209  }
1210 
1211  if ( !mBlockingLabelItems.isEmpty() )
1212  {
1213  jobMapSettings.setLabelBlockingRegions( createLabelBlockingRegions( jobMapSettings ) );
1214  }
1215 
1216  return jobMapSettings;
1217 }
1218 
1220 {
1221  assignFreeId();
1222 
1223  mBlockingLabelItems.clear();
1224  for ( const QString &uuid : qgis::as_const( mBlockingLabelItemUuids ) )
1225  {
1226  QgsLayoutItem *item = mLayout->itemByUuid( uuid, true );
1227  if ( item )
1228  {
1229  addLabelBlockingItem( item );
1230  }
1231  }
1232 
1233  mOverviewStack->finalizeRestoreFromXml();
1234  mGridStack->finalizeRestoreFromXml();
1235 }
1236 
1237 void QgsLayoutItemMap::setMoveContentPreviewOffset( double xOffset, double yOffset )
1238 {
1239  mXOffset = xOffset;
1240  mYOffset = yOffset;
1241 }
1242 
1244 {
1245  return mCurrentRectangle;
1246 }
1247 
1249 {
1251 
1252  //Can't utilize QgsExpressionContextUtils::mapSettingsScope as we don't always
1253  //have a QgsMapSettings object available when the context is required, so we manually
1254  //add the same variables here
1255  QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Map Settings" ) );
1256 
1257  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_id" ), id(), true ) );
1258  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_rotation" ), mMapRotation, true ) );
1259  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_scale" ), scale(), true ) );
1260 
1261  QgsRectangle currentExtent( extent() );
1262  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_extent" ), QVariant::fromValue( QgsGeometry::fromRect( currentExtent ) ), true ) );
1263  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_extent_width" ), currentExtent.width(), true ) );
1264  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_extent_height" ), currentExtent.height(), true ) );
1265  QgsGeometry centerPoint = QgsGeometry::fromPointXY( currentExtent.center() );
1266  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_extent_center" ), QVariant::fromValue( centerPoint ), true ) );
1267 
1268  QgsCoordinateReferenceSystem mapCrs = crs();
1269  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs" ), mapCrs.authid(), true ) );
1270  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_definition" ), mapCrs.toProj4(), true ) );
1271  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_description" ), mapCrs.description(), true ) );
1272  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_units" ), QgsUnitTypes::toString( mapCrs.mapUnits() ), true ) );
1273  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_acronym" ), mapCrs.projectionAcronym(), true ) );
1274  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_ellipsoid" ), mapCrs.ellipsoidAcronym(), true ) );
1275  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_proj4" ), mapCrs.toProj4(), true ) );
1276  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_wkt" ), mapCrs.toWkt(), true ) );
1277 
1278  QVariantList layersIds;
1279  QVariantList layers;
1280  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_layer_ids" ), layersIds, true ) );
1281  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_layers" ), layers, true ) );
1282 
1283  context.appendScope( scope );
1284 
1285  // The scope map_layer_ids and map_layers variables have been added to the context, only now we can
1286  // call layersToRender (just in case layersToRender relies on evaluating an expression which uses
1287  // other variables contained within the map settings scope
1288  const QList<QgsMapLayer *> layersInMap = layersToRender( &context );
1289 
1290  layersIds.reserve( layersInMap.count() );
1291  layers.reserve( layersInMap.count() );
1292  for ( QgsMapLayer *layer : layersInMap )
1293  {
1294  layersIds << layer->id();
1295  layers << QVariant::fromValue<QgsWeakMapLayerPointer>( QgsWeakMapLayerPointer( layer ) );
1296  }
1297  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_layer_ids" ), layersIds, true ) );
1298  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_layers" ), layers, true ) );
1299 
1300  scope->addFunction( QStringLiteral( "is_layer_visible" ), new QgsExpressionContextUtils::GetLayerVisibility( layersInMap ) );
1301 
1302  return context;
1303 }
1304 
1306 {
1307  double extentWidth = extent().width();
1308  if ( extentWidth <= 0 )
1309  {
1310  return 1;
1311  }
1312  return rect().width() / extentWidth;
1313 }
1314 
1316 {
1317  double dx = mXOffset;
1318  double dy = mYOffset;
1319  transformShift( dx, dy );
1320  QPolygonF poly = visibleExtentPolygon();
1321  poly.translate( -dx, -dy );
1322  return poly;
1323 }
1324 
1326 {
1327  if ( !mBlockingLabelItems.contains( item ) )
1328  mBlockingLabelItems.append( item );
1329 
1330  connect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutItemMap::invalidateCache, Qt::UniqueConnection );
1331 }
1332 
1334 {
1335  mBlockingLabelItems.removeAll( item );
1336  if ( item )
1338 }
1339 
1341 {
1342  return mBlockingLabelItems.contains( item );
1343 }
1344 
1345 QPointF QgsLayoutItemMap::mapToItemCoords( QPointF mapCoords ) const
1346 {
1347  QPolygonF mapPoly = transformedMapPolygon();
1348  if ( mapPoly.empty() )
1349  {
1350  return QPointF( 0, 0 );
1351  }
1352 
1353  QgsRectangle tExtent = transformedExtent();
1354  QgsPointXY rotationPoint( ( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
1355  double dx = mapCoords.x() - rotationPoint.x();
1356  double dy = mapCoords.y() - rotationPoint.y();
1357  QgsLayoutUtils::rotate( -mEvaluatedMapRotation, dx, dy );
1358  QgsPointXY backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
1359 
1360  QgsRectangle unrotatedExtent = transformedExtent();
1361  double xItem = rect().width() * ( backRotatedCoords.x() - unrotatedExtent.xMinimum() ) / unrotatedExtent.width();
1362  double yItem = rect().height() * ( 1 - ( backRotatedCoords.y() - unrotatedExtent.yMinimum() ) / unrotatedExtent.height() );
1363  return QPointF( xItem, yItem );
1364 }
1365 
1367 {
1369  QgsRectangle newExtent = mExtent;
1370  if ( qgsDoubleNear( mEvaluatedMapRotation, 0.0 ) )
1371  {
1372  extent = newExtent;
1373  }
1374  else
1375  {
1376  QPolygonF poly;
1377  mapPolygon( newExtent, poly );
1378  QRectF bRect = poly.boundingRect();
1379  extent.setXMinimum( bRect.left() );
1380  extent.setXMaximum( bRect.right() );
1381  extent.setYMinimum( bRect.top() );
1382  extent.setYMaximum( bRect.bottom() );
1383  }
1384  return extent;
1385 }
1386 
1388 {
1389  if ( mDrawing )
1390  return;
1391 
1392  mCacheInvalidated = true;
1393  update();
1394 }
1395 
1397 {
1398  QRectF rectangle = rect();
1399  double frameExtension = frameEnabled() ? pen().widthF() / 2.0 : 0.0;
1400 
1401  double topExtension = 0.0;
1402  double rightExtension = 0.0;
1403  double bottomExtension = 0.0;
1404  double leftExtension = 0.0;
1405 
1406  if ( mGridStack )
1407  mGridStack->calculateMaxGridExtension( topExtension, rightExtension, bottomExtension, leftExtension );
1408 
1409  topExtension = std::max( topExtension, frameExtension );
1410  rightExtension = std::max( rightExtension, frameExtension );
1411  bottomExtension = std::max( bottomExtension, frameExtension );
1412  leftExtension = std::max( leftExtension, frameExtension );
1413 
1414  rectangle.setLeft( rectangle.left() - leftExtension );
1415  rectangle.setRight( rectangle.right() + rightExtension );
1416  rectangle.setTop( rectangle.top() - topExtension );
1417  rectangle.setBottom( rectangle.bottom() + bottomExtension );
1418  if ( rectangle != mCurrentRectangle )
1419  {
1420  prepareGeometryChange();
1421  mCurrentRectangle = rectangle;
1422  }
1423 }
1424 
1426 {
1428 
1429  //updates data defined properties and redraws item to match
1430  if ( property == QgsLayoutObject::MapRotation || property == QgsLayoutObject::MapScale ||
1431  property == QgsLayoutObject::MapXMin || property == QgsLayoutObject::MapYMin ||
1432  property == QgsLayoutObject::MapXMax || property == QgsLayoutObject::MapYMax ||
1433  property == QgsLayoutObject::MapAtlasMargin ||
1434  property == QgsLayoutObject::AllProperties )
1435  {
1436  QgsRectangle beforeExtent = mExtent;
1437  refreshMapExtents( &context );
1438  emit changed();
1439  if ( mExtent != beforeExtent )
1440  {
1441  emit extentChanged();
1442  }
1443  }
1444  if ( property == QgsLayoutObject::MapLabelMargin || property == QgsLayoutObject::AllProperties )
1445  {
1446  refreshLabelMargin( false );
1447  }
1448 
1449  //force redraw
1450  mCacheInvalidated = true;
1451 
1453 }
1454 
1455 void QgsLayoutItemMap::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
1456 {
1457  if ( !mLayers.isEmpty() || mLayerStyleOverrides.isEmpty() )
1458  {
1459  for ( QgsMapLayer *layer : layers )
1460  {
1461  mLayerStyleOverrides.remove( layer->id() );
1462  }
1463  _qgis_removeLayers( mLayers, layers );
1464  }
1465 }
1466 
1467 void QgsLayoutItemMap::painterJobFinished()
1468 {
1469  mPainter->end();
1470  mPainterJob.reset( nullptr );
1471  mPainter.reset( nullptr );
1472  mCacheFinalImage = std::move( mCacheRenderingImage );
1473  mLastRenderedImageOffsetX = 0;
1474  mLastRenderedImageOffsetY = 0;
1475  update();
1476 }
1477 
1478 void QgsLayoutItemMap::shapeChanged()
1479 {
1480  // keep center as center
1481  QgsPointXY oldCenter = mExtent.center();
1482 
1483  double w = rect().width();
1484  double h = rect().height();
1485 
1486  // keep same width as before
1487  double newWidth = mExtent.width();
1488  // but scale height to match item's aspect ratio
1489  double newHeight = newWidth * h / w;
1490 
1491  mExtent = QgsRectangle::fromCenterAndSize( oldCenter, newWidth, newHeight );
1492 
1493  //recalculate data defined scale and extents
1494  refreshMapExtents();
1496  invalidateCache();
1497  emit changed();
1498  emit extentChanged();
1499 }
1500 
1501 void QgsLayoutItemMap::mapThemeChanged( const QString &theme )
1502 {
1503  if ( theme == mCachedLayerStyleOverridesPresetName )
1504  mCachedLayerStyleOverridesPresetName.clear(); // force cache regeneration at next redraw
1505 }
1506 
1507 void QgsLayoutItemMap::connectUpdateSlot()
1508 {
1509  //connect signal from layer registry to update in case of new or deleted layers
1510  QgsProject *project = mLayout->project();
1511  if ( project )
1512  {
1513  // handles updating the stored layer state BEFORE the layers are removed
1514  connect( project, static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *>& layers ) > ( &QgsProject::layersWillBeRemoved ),
1515  this, &QgsLayoutItemMap::layersAboutToBeRemoved );
1516  // redraws the map AFTER layers are removed
1517  connect( project->layerTreeRoot(), &QgsLayerTree::layerOrderChanged, this, [ = ]
1518  {
1519  if ( layers().isEmpty() )
1520  {
1521  //using project layers, and layer order has changed
1522  invalidateCache();
1523  }
1524  } );
1525 
1526  connect( project, &QgsProject::crsChanged, this, [ = ]
1527  {
1528  if ( !mCrs.isValid() )
1529  {
1530  //using project CRS, which just changed....
1531  invalidateCache();
1532  }
1533  } );
1534 
1535  // If project colors change, we need to redraw the map, as layer symbols may rely on project colors
1536  connect( project, &QgsProject::projectColorsChanged, this, [ = ]
1537  {
1538  invalidateCache();
1539  } );
1540  }
1542 
1543  connect( project->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsLayoutItemMap::mapThemeChanged );
1544 }
1545 
1546 QTransform QgsLayoutItemMap::layoutToMapCoordsTransform() const
1547 {
1548  QPolygonF thisExtent = visibleExtentPolygon();
1549  QTransform mapTransform;
1550  QPolygonF thisRectPoly = QPolygonF( QRectF( 0, 0, rect().width(), rect().height() ) );
1551  //workaround QT Bug #21329
1552  thisRectPoly.pop_back();
1553  thisExtent.pop_back();
1554 
1555  QPolygonF thisItemPolyInLayout = mapToScene( thisRectPoly );
1556 
1557  //create transform from layout coordinates to map coordinates
1558  QTransform::quadToQuad( thisItemPolyInLayout, thisExtent, mapTransform );
1559  return mapTransform;
1560 }
1561 
1562 QList<QgsLabelBlockingRegion> QgsLayoutItemMap::createLabelBlockingRegions( const QgsMapSettings & ) const
1563 {
1564  const QTransform mapTransform = layoutToMapCoordsTransform();
1565  QList< QgsLabelBlockingRegion > blockers;
1566  blockers.reserve( mBlockingLabelItems.count() );
1567  for ( const auto &item : qgis::as_const( mBlockingLabelItems ) )
1568  {
1569  if ( !item || !item->isVisible() ) // invisible items don't block labels!
1570  continue;
1571 
1572  QPolygonF itemRectInMapCoordinates = mapTransform.map( item->mapToScene( item->rect() ) );
1573  itemRectInMapCoordinates.append( itemRectInMapCoordinates.at( 0 ) ); //close polygon
1574  QgsGeometry blockingRegion = QgsGeometry::fromQPolygonF( itemRectInMapCoordinates );
1575  blockers << QgsLabelBlockingRegion( blockingRegion );
1576  }
1577  return blockers;
1578 }
1579 
1581 {
1582  return mLabelMargin;
1583 }
1584 
1586 {
1587  mLabelMargin = margin;
1588  refreshLabelMargin( false );
1589 }
1590 
1591 void QgsLayoutItemMap::updateToolTip()
1592 {
1593  setToolTip( displayName() );
1594 }
1595 
1596 QList<QgsMapLayer *> QgsLayoutItemMap::layersToRender( const QgsExpressionContext *context ) const
1597 {
1598  QgsExpressionContext scopedContext;
1599  if ( !context )
1600  scopedContext = createExpressionContext();
1601  const QgsExpressionContext *evalContext = context ? context : &scopedContext;
1602 
1603  QList<QgsMapLayer *> renderLayers;
1604 
1605  if ( mFollowVisibilityPreset )
1606  {
1607  QString presetName = mFollowVisibilityPresetName;
1608 
1609  // preset name can be overridden by data-defined one
1610  presetName = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapStylePreset, *evalContext, presetName );
1611 
1612  if ( mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
1613  renderLayers = mLayout->project()->mapThemeCollection()->mapThemeVisibleLayers( presetName );
1614  else // fallback to using map canvas layers
1615  renderLayers = mLayout->project()->mapThemeCollection()->masterVisibleLayers();
1616  }
1617  else if ( !layers().isEmpty() )
1618  {
1619  renderLayers = layers();
1620  }
1621  else
1622  {
1623  renderLayers = mLayout->project()->mapThemeCollection()->masterVisibleLayers();
1624  }
1625 
1626  bool ok = false;
1627  QString ddLayers = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapLayers, *evalContext, QString(), &ok );
1628  if ( ok )
1629  {
1630  renderLayers.clear();
1631 
1632  const QStringList layerNames = ddLayers.split( '|' );
1633  //need to convert layer names to layer ids
1634  for ( const QString &name : layerNames )
1635  {
1636  const QList< QgsMapLayer * > matchingLayers = mLayout->project()->mapLayersByName( name );
1637  for ( QgsMapLayer *layer : matchingLayers )
1638  {
1639  renderLayers << layer;
1640  }
1641  }
1642  }
1643 
1644  //remove atlas coverage layer if required
1645  if ( mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagHideCoverageLayer )
1646  {
1647  //hiding coverage layer
1648  int removeAt = renderLayers.indexOf( mLayout->reportContext().layer() );
1649  if ( removeAt != -1 )
1650  {
1651  renderLayers.removeAt( removeAt );
1652  }
1653  }
1654 
1655  return renderLayers;
1656 }
1657 
1658 QMap<QString, QString> QgsLayoutItemMap::layerStyleOverridesToRender( const QgsExpressionContext &context ) const
1659 {
1660  if ( mFollowVisibilityPreset )
1661  {
1662  QString presetName = mFollowVisibilityPresetName;
1663 
1664  // data defined preset name?
1665  presetName = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapStylePreset, context, presetName );
1666 
1667  if ( mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
1668  {
1669  if ( presetName.isEmpty() || presetName != mCachedLayerStyleOverridesPresetName )
1670  {
1671  // have to regenerate cache of style overrides
1672  mCachedPresetLayerStyleOverrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( presetName );
1673  mCachedLayerStyleOverridesPresetName = presetName;
1674  }
1675 
1676  return mCachedPresetLayerStyleOverrides;
1677  }
1678  else
1679  return QMap<QString, QString>();
1680  }
1681  else if ( mKeepLayerStyles )
1682  {
1683  return mLayerStyleOverrides;
1684  }
1685  else
1686  {
1687  return QMap<QString, QString>();
1688  }
1689 }
1690 
1691 QgsRectangle QgsLayoutItemMap::transformedExtent() const
1692 {
1693  double dx = mXOffset;
1694  double dy = mYOffset;
1695  transformShift( dx, dy );
1696  return QgsRectangle( mExtent.xMinimum() - dx, mExtent.yMinimum() - dy, mExtent.xMaximum() - dx, mExtent.yMaximum() - dy );
1697 }
1698 
1699 void QgsLayoutItemMap::mapPolygon( const QgsRectangle &extent, QPolygonF &poly ) const
1700 {
1701  poly.clear();
1702  if ( qgsDoubleNear( mEvaluatedMapRotation, 0.0 ) )
1703  {
1704  poly << QPointF( extent.xMinimum(), extent.yMaximum() );
1705  poly << QPointF( extent.xMaximum(), extent.yMaximum() );
1706  poly << QPointF( extent.xMaximum(), extent.yMinimum() );
1707  poly << QPointF( extent.xMinimum(), extent.yMinimum() );
1708  //ensure polygon is closed by readding first point
1709  poly << QPointF( poly.at( 0 ) );
1710  return;
1711  }
1712 
1713  //there is rotation
1714  QgsPointXY rotationPoint( ( extent.xMaximum() + extent.xMinimum() ) / 2.0, ( extent.yMaximum() + extent.yMinimum() ) / 2.0 );
1715  double dx, dy; //x-, y- shift from rotation point to corner point
1716 
1717  //top left point
1718  dx = rotationPoint.x() - extent.xMinimum();
1719  dy = rotationPoint.y() - extent.yMaximum();
1720  QgsLayoutUtils::rotate( mEvaluatedMapRotation, dx, dy );
1721  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
1722 
1723  //top right point
1724  dx = rotationPoint.x() - extent.xMaximum();
1725  dy = rotationPoint.y() - extent.yMaximum();
1726  QgsLayoutUtils::rotate( mEvaluatedMapRotation, dx, dy );
1727  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
1728 
1729  //bottom right point
1730  dx = rotationPoint.x() - extent.xMaximum();
1731  dy = rotationPoint.y() - extent.yMinimum();
1732  QgsLayoutUtils::rotate( mEvaluatedMapRotation, dx, dy );
1733  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
1734 
1735  //bottom left point
1736  dx = rotationPoint.x() - extent.xMinimum();
1737  dy = rotationPoint.y() - extent.yMinimum();
1738  QgsLayoutUtils::rotate( mEvaluatedMapRotation, dx, dy );
1739  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
1740 
1741  //ensure polygon is closed by readding first point
1742  poly << QPointF( poly.at( 0 ) );
1743 }
1744 
1745 void QgsLayoutItemMap::transformShift( double &xShift, double &yShift ) const
1746 {
1747  double mmToMapUnits = 1.0 / mapUnitsToLayoutUnits();
1748  double dxScaled = xShift * mmToMapUnits;
1749  double dyScaled = - yShift * mmToMapUnits;
1750 
1751  QgsLayoutUtils::rotate( mEvaluatedMapRotation, dxScaled, dyScaled );
1752 
1753  xShift = dxScaled;
1754  yShift = dyScaled;
1755 }
1756 
1757 void QgsLayoutItemMap::drawAnnotations( QPainter *painter )
1758 {
1759  if ( !mLayout || !mLayout->project() || !mDrawAnnotations )
1760  {
1761  return;
1762  }
1763 
1764  const QList< QgsAnnotation * > annotations = mLayout->project()->annotationManager()->annotations();
1765  if ( annotations.isEmpty() )
1766  return;
1767 
1769  rc.setForceVectorOutput( true );
1771  QList< QgsMapLayer * > layers = layersToRender( &rc.expressionContext() );
1772 
1773  for ( QgsAnnotation *annotation : annotations )
1774  {
1775  if ( !annotation || !annotation->isVisible() )
1776  {
1777  continue;
1778  }
1779  if ( annotation->mapLayer() && !layers.contains( annotation->mapLayer() ) )
1780  continue;
1781 
1782  drawAnnotation( annotation, rc );
1783  }
1784 }
1785 
1786 void QgsLayoutItemMap::drawAnnotation( const QgsAnnotation *annotation, QgsRenderContext &context )
1787 {
1788  if ( !annotation || !annotation->isVisible() || !context.painter() || !context.painter()->device() )
1789  {
1790  return;
1791  }
1792 
1793  context.painter()->save();
1794  context.painter()->setRenderHint( QPainter::Antialiasing, context.flags() & QgsRenderContext::Antialiasing );
1795 
1796  double itemX, itemY;
1797  if ( annotation->hasFixedMapPosition() )
1798  {
1799  QPointF mapPos = layoutMapPosForItem( annotation );
1800  itemX = mapPos.x();
1801  itemY = mapPos.y();
1802  }
1803  else
1804  {
1805  itemX = annotation->relativePosition().x() * rect().width();
1806  itemY = annotation->relativePosition().y() * rect().height();
1807  }
1808  context.painter()->translate( itemX, itemY );
1809 
1810  //setup painter scaling to dots so that symbology is drawn to scale
1811  double dotsPerMM = context.painter()->device()->logicalDpiX() / 25.4;
1812  context.painter()->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
1813 
1814  annotation->render( context );
1815  context.painter()->restore();
1816 }
1817 
1818 QPointF QgsLayoutItemMap::layoutMapPosForItem( const QgsAnnotation *annotation ) const
1819 {
1820  if ( !annotation )
1821  return QPointF( 0, 0 );
1822 
1823  double mapX = 0.0;
1824  double mapY = 0.0;
1825 
1826  mapX = annotation->mapPosition().x();
1827  mapY = annotation->mapPosition().y();
1828  QgsCoordinateReferenceSystem annotationCrs = annotation->mapPositionCrs();
1829 
1830  if ( annotationCrs != crs() )
1831  {
1832  //need to reproject
1833  QgsCoordinateTransform t( annotationCrs, crs(), mLayout->project() );
1834  double z = 0.0;
1835  try
1836  {
1837  t.transformInPlace( mapX, mapY, z );
1838  }
1839  catch ( const QgsCsException & )
1840  {
1841  }
1842  }
1843 
1844  return mapToItemCoords( QPointF( mapX, mapY ) );
1845 }
1846 
1847 void QgsLayoutItemMap::drawMapFrame( QPainter *p )
1848 {
1849  if ( frameEnabled() && p )
1850  {
1851  p->save();
1852  p->setPen( pen() );
1853  p->setBrush( Qt::NoBrush );
1854  p->setRenderHint( QPainter::Antialiasing, true );
1855  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
1856  p->restore();
1857  }
1858 }
1859 
1860 void QgsLayoutItemMap::drawMapBackground( QPainter *p )
1861 {
1862  if ( hasBackground() && p )
1863  {
1864  p->save();
1865  p->setBrush( brush() );//this causes a problem in atlas generation
1866  p->setPen( Qt::NoPen );
1867  p->setRenderHint( QPainter::Antialiasing, true );
1868  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
1869  p->restore();
1870  }
1871 }
1872 
1873 bool QgsLayoutItemMap::shouldDrawPart( QgsLayoutItemMap::PartType part ) const
1874 {
1875  int currentExportLayer = mLayout->renderContext().currentExportLayer();
1876 
1877  if ( -1 == currentExportLayer )
1878  {
1879  //all parts of the map are visible
1880  return true;
1881  }
1882 
1883  int idx = numberExportLayers();
1884  if ( isSelected() )
1885  {
1886  --idx;
1887  if ( SelectionBoxes == part )
1888  {
1889  return currentExportLayer == idx;
1890  }
1891  }
1892 
1893  if ( frameEnabled() )
1894  {
1895  --idx;
1896  if ( Frame == part )
1897  {
1898  return currentExportLayer == idx;
1899  }
1900  }
1901  --idx;
1902  if ( OverviewMapExtent == part )
1903  {
1904  return currentExportLayer == idx;
1905  }
1906  --idx;
1907  if ( Grid == part )
1908  {
1909  return currentExportLayer == idx;
1910  }
1911  if ( hasBackground() )
1912  {
1913  if ( Background == part )
1914  {
1915  return currentExportLayer == 0;
1916  }
1917  }
1918 
1919  return true; // for Layer
1920 }
1921 
1922 void QgsLayoutItemMap::refreshMapExtents( const QgsExpressionContext *context )
1923 {
1924  QgsExpressionContext scopedContext;
1925  if ( !context )
1926  scopedContext = createExpressionContext();
1927  const QgsExpressionContext *evalContext = context ? context : &scopedContext;
1928 
1929  //data defined map extents set?
1930  QgsRectangle newExtent = extent();
1931  bool useDdXMin = false;
1932  bool useDdXMax = false;
1933  bool useDdYMin = false;
1934  bool useDdYMax = false;
1935  double minXD = 0;
1936  double minYD = 0;
1937  double maxXD = 0;
1938  double maxYD = 0;
1939 
1940  bool ok = false;
1941  minXD = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapXMin, *evalContext, 0.0, &ok );
1942  if ( ok )
1943  {
1944  useDdXMin = true;
1945  newExtent.setXMinimum( minXD );
1946  }
1947  minYD = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapYMin, *evalContext, 0.0, &ok );
1948  if ( ok )
1949  {
1950  useDdYMin = true;
1951  newExtent.setYMinimum( minYD );
1952  }
1953  maxXD = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapXMax, *evalContext, 0.0, &ok );
1954  if ( ok )
1955  {
1956  useDdXMax = true;
1957  newExtent.setXMaximum( maxXD );
1958  }
1959  maxYD = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapYMax, *evalContext, 0.0, &ok );
1960  if ( ok )
1961  {
1962  useDdYMax = true;
1963  newExtent.setYMaximum( maxYD );
1964  }
1965 
1966  if ( newExtent != mExtent )
1967  {
1968  //calculate new extents to fit data defined extents
1969 
1970  //Make sure the width/height ratio is the same as in current map extent.
1971  //This is to keep the map item frame and the page layout fixed
1972  double currentWidthHeightRatio = mExtent.width() / mExtent.height();
1973  double newWidthHeightRatio = newExtent.width() / newExtent.height();
1974 
1975  if ( currentWidthHeightRatio < newWidthHeightRatio )
1976  {
1977  //enlarge height of new extent, ensuring the map center stays the same
1978  double newHeight = newExtent.width() / currentWidthHeightRatio;
1979  double deltaHeight = newHeight - newExtent.height();
1980  newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
1981  newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
1982  }
1983  else
1984  {
1985  //enlarge width of new extent, ensuring the map center stays the same
1986  double newWidth = currentWidthHeightRatio * newExtent.height();
1987  double deltaWidth = newWidth - newExtent.width();
1988  newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
1989  newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
1990  }
1991 
1992  mExtent = newExtent;
1993  }
1994 
1995  //now refresh scale, as this potentially overrides extents
1996 
1997  //data defined map scale set?
1998  double scaleD = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapScale, *evalContext, 0.0, &ok );
1999  if ( ok )
2000  {
2001  setScale( scaleD, false );
2002  newExtent = mExtent;
2003  }
2004 
2005  if ( useDdXMax || useDdXMin || useDdYMax || useDdYMin )
2006  {
2007  //if only one of min/max was set for either x or y, then make sure our extent is locked on that value
2008  //as we can do this without altering the scale
2009  if ( useDdXMin && !useDdXMax )
2010  {
2011  double xMax = mExtent.xMaximum() - ( mExtent.xMinimum() - minXD );
2012  newExtent.setXMinimum( minXD );
2013  newExtent.setXMaximum( xMax );
2014  }
2015  else if ( !useDdXMin && useDdXMax )
2016  {
2017  double xMin = mExtent.xMinimum() - ( mExtent.xMaximum() - maxXD );
2018  newExtent.setXMinimum( xMin );
2019  newExtent.setXMaximum( maxXD );
2020  }
2021  if ( useDdYMin && !useDdYMax )
2022  {
2023  double yMax = mExtent.yMaximum() - ( mExtent.yMinimum() - minYD );
2024  newExtent.setYMinimum( minYD );
2025  newExtent.setYMaximum( yMax );
2026  }
2027  else if ( !useDdYMin && useDdYMax )
2028  {
2029  double yMin = mExtent.yMinimum() - ( mExtent.yMaximum() - maxYD );
2030  newExtent.setYMinimum( yMin );
2031  newExtent.setYMaximum( maxYD );
2032  }
2033 
2034  if ( newExtent != mExtent )
2035  {
2036  mExtent = newExtent;
2037  }
2038  }
2039 
2040  //lastly, map rotation overrides all
2041  double mapRotation = mMapRotation;
2042 
2043  //data defined map rotation set?
2044  mapRotation = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapRotation, *evalContext, mapRotation );
2045 
2046  if ( !qgsDoubleNear( mEvaluatedMapRotation, mapRotation ) )
2047  {
2048  mEvaluatedMapRotation = mapRotation;
2049  emit mapRotationChanged( mapRotation );
2050  }
2051 }
2052 
2053 void QgsLayoutItemMap::refreshLabelMargin( bool updateItem )
2054 {
2055  //data defined label margin set?
2057  mEvaluatedLabelMargin.setLength( labelMargin );
2058  mEvaluatedLabelMargin.setUnits( mLabelMargin.units() );
2059 
2060  if ( updateItem )
2061  {
2062  update();
2063  }
2064 }
2065 
2066 void QgsLayoutItemMap::updateAtlasFeature()
2067 {
2068  if ( !atlasDriven() || !mLayout->reportContext().layer() )
2069  return; // nothing to do
2070 
2071  QgsRectangle bounds = computeAtlasRectangle();
2072  if ( bounds.isNull() )
2073  return;
2074 
2075  double xa1 = bounds.xMinimum();
2076  double xa2 = bounds.xMaximum();
2077  double ya1 = bounds.yMinimum();
2078  double ya2 = bounds.yMaximum();
2079  QgsRectangle newExtent = bounds;
2080  QgsRectangle originalExtent = mExtent;
2081 
2082  //sanity check - only allow fixed scale mode for point layers
2083  bool isPointLayer = QgsWkbTypes::geometryType( mLayout->reportContext().layer()->wkbType() ) == QgsWkbTypes::PointGeometry;
2084 
2085  if ( mAtlasScalingMode == Fixed || mAtlasScalingMode == Predefined || isPointLayer )
2086  {
2087  QgsScaleCalculator calc;
2088  calc.setMapUnits( crs().mapUnits() );
2089  calc.setDpi( 25.4 );
2090  double originalScale = calc.calculate( originalExtent, rect().width() );
2091  double geomCenterX = ( xa1 + xa2 ) / 2.0;
2092  double geomCenterY = ( ya1 + ya2 ) / 2.0;
2093 
2094  if ( mAtlasScalingMode == Fixed || isPointLayer )
2095  {
2096  // only translate, keep the original scale (i.e. width x height)
2097  double xMin = geomCenterX - originalExtent.width() / 2.0;
2098  double yMin = geomCenterY - originalExtent.height() / 2.0;
2099  newExtent = QgsRectangle( xMin,
2100  yMin,
2101  xMin + originalExtent.width(),
2102  yMin + originalExtent.height() );
2103 
2104  //scale newExtent to match original scale of map
2105  //this is required for geographic coordinate systems, where the scale varies by extent
2106  double newScale = calc.calculate( newExtent, rect().width() );
2107  newExtent.scale( originalScale / newScale );
2108  }
2109  else if ( mAtlasScalingMode == Predefined )
2110  {
2111  // choose one of the predefined scales
2112  double newWidth = originalExtent.width();
2113  double newHeight = originalExtent.height();
2114  QVector<qreal> scales = mLayout->reportContext().predefinedScales();
2115  for ( int i = 0; i < scales.size(); i++ )
2116  {
2117  double ratio = scales[i] / originalScale;
2118  newWidth = originalExtent.width() * ratio;
2119  newHeight = originalExtent.height() * ratio;
2120 
2121  // compute new extent, centered on feature
2122  double xMin = geomCenterX - newWidth / 2.0;
2123  double yMin = geomCenterY - newHeight / 2.0;
2124  newExtent = QgsRectangle( xMin,
2125  yMin,
2126  xMin + newWidth,
2127  yMin + newHeight );
2128 
2129  //scale newExtent to match desired map scale
2130  //this is required for geographic coordinate systems, where the scale varies by extent
2131  double newScale = calc.calculate( newExtent, rect().width() );
2132  newExtent.scale( scales[i] / newScale );
2133 
2134  if ( ( newExtent.width() >= bounds.width() ) && ( newExtent.height() >= bounds.height() ) )
2135  {
2136  // this is the smallest extent that embeds the feature, stop here
2137  break;
2138  }
2139  }
2140  }
2141  }
2142  else if ( mAtlasScalingMode == Auto )
2143  {
2144  // auto scale
2145 
2146  double geomRatio = bounds.width() / bounds.height();
2147  double mapRatio = originalExtent.width() / originalExtent.height();
2148 
2149  // geometry height is too big
2150  if ( geomRatio < mapRatio )
2151  {
2152  // extent the bbox's width
2153  double adjWidth = ( mapRatio * bounds.height() - bounds.width() ) / 2.0;
2154  xa1 -= adjWidth;
2155  xa2 += adjWidth;
2156  }
2157  // geometry width is too big
2158  else if ( geomRatio > mapRatio )
2159  {
2160  // extent the bbox's height
2161  double adjHeight = ( bounds.width() / mapRatio - bounds.height() ) / 2.0;
2162  ya1 -= adjHeight;
2163  ya2 += adjHeight;
2164  }
2165  newExtent = QgsRectangle( xa1, ya1, xa2, ya2 );
2166 
2167  const double evaluatedAtlasMargin = atlasMargin();
2168  if ( evaluatedAtlasMargin > 0.0 )
2169  {
2170  newExtent.scale( 1 + evaluatedAtlasMargin );
2171  }
2172  }
2173 
2174  // set the new extent (and render)
2175  setExtent( newExtent );
2176  emit preparedForAtlas();
2177 }
2178 
2179 QgsRectangle QgsLayoutItemMap::computeAtlasRectangle()
2180 {
2181  // QgsGeometry::boundingBox is expressed in the geometry"s native CRS
2182  // We have to transform the geometry to the destination CRS and ask for the bounding box
2183  // Note: we cannot directly take the transformation of the bounding box, since transformations are not linear
2184  QgsGeometry g = mLayout->reportContext().currentGeometry( crs() );
2185  // Rotating the geometry, so the bounding box is correct wrt map rotation
2186  if ( mEvaluatedMapRotation != 0.0 )
2187  {
2188  QgsPointXY prevCenter = g.boundingBox().center();
2189  g.rotate( mEvaluatedMapRotation, g.boundingBox().center() );
2190  // Rotation center will be still the bounding box center of an unrotated geometry.
2191  // Which means, if the center of bbox moves after rotation, the viewport will
2192  // also be offset, and part of the geometry will fall out of bounds.
2193  // Here we compensate for that roughly: by extending the rotated bounds
2194  // so that its center is the same as the original.
2195  QgsRectangle bounds = g.boundingBox();
2196  double dx = std::max( std::abs( prevCenter.x() - bounds.xMinimum() ),
2197  std::abs( prevCenter.x() - bounds.xMaximum() ) );
2198  double dy = std::max( std::abs( prevCenter.y() - bounds.yMinimum() ),
2199  std::abs( prevCenter.y() - bounds.yMaximum() ) );
2200  QgsPointXY center = g.boundingBox().center();
2201  return QgsRectangle( center.x() - dx, center.y() - dy,
2202  center.x() + dx, center.y() + dy );
2203  }
2204  else
2205  {
2206  return g.boundingBox();
2207  }
2208 }
QIcon icon() const override
Returns the item&#39;s icon.
QString displayName() const override
Gets item display name.
void setForceVectorOutput(bool force)
Sets whether rendering operations should use vector operations instead of any faster raster shortcuts...
The class is used as a container of context for various read/write operations on other objects...
QgsUnitTypes::LayoutUnit units() const
Returns the units for the measurement.
static double scaleFactorFromItemStyle(const QStyleOptionGraphicsItem *style)
Extracts the scale factor from an item style.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void draw(QgsLayoutItemRenderContext &context) override
Draws the item&#39;s contents using the specified item render context.
void finished()
emitted when asynchronous rendering is finished (or canceled).
Single variable definition for use within a QgsExpressionContextScope.
void addLabelBlockingItem(QgsLayoutItem *item)
Sets the specified layout item as a "label blocking item" for this map.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Base class for all map layer types.
Definition: qgsmaplayer.h:78
void setExtent(const QgsRectangle &rect, bool magnified=true)
Set coordinates of the rectangle which should be rendered.
Job implementation that renders everything sequentially using a custom painter.
bool isVisible() const
Returns true if the annotation is visible and should be rendered.
Definition: qgsannotation.h:89
QPointF mapToItemCoords(QPointF mapCoords) const
Transforms map coordinates to item coordinates (considering rotation and move offset) ...
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context, which stores various information regarding which datum transfo...
bool containsAdvancedEffects() const override
Returns true if the item contains contents with blend modes or transparency effects which can only be...
Base class for graphical items within a QgsLayout.
double calculate(const QgsRectangle &mapExtent, double canvasWidth)
Calculate the scale denominator.
An individual overview which is drawn above the map content in a QgsLayoutItemMap, and shows the extent of another QgsLayoutItemMap.
virtual bool containsAdvancedEffects() const
Returns true if the item contains contents with blend modes or transparency effects which can only be...
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
QgsLayoutItemMap(QgsLayout *layout)
Constructor for QgsLayoutItemMap, with the specified parent layout.
void moveContent(double dx, double dy) override
Moves the content of the item, by a specified dx and dy in layout units.
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...
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
int type() const override
Use antialiasing while drawing.
QString xmlData() const
Returns XML content of the style.
void readXml(const QDomElement &styleElement)
Read style configuration (for project file reading)
void addFunction(const QString &name, QgsScopedExpressionFunction *function)
Adds a function to the scope.
Layer and style map theme.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void setExtent(const QgsRectangle &extent)
Sets a new extent for the map.
double y
Definition: qgspointxy.h:48
void layerOrderChanged()
Emitted when the layer order has changed.
A class to represent a 2D point.
Definition: qgspointxy.h:43
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Definition: qgsrectangle.h:235
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 extentChanged()
Emitted when the map&#39;s extent changes.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:265
OperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
static QgsLayoutItemMap * create(QgsLayout *layout)
Returns a new map item for the specified layout.
Whether to use also label candidates that are partially outside of the map view.
bool frameEnabled() const
Returns true if the item includes a frame.
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
TYPE * resolveWeakly(const QgsProject *project)
Resolves the map layer by attempting to find a matching layer in a project using a weak match...
void setOutputDpi(double dpi)
Sets DPI used for conversion between real world units (e.g. mm) and pixels.
QString toProj4() const
Returns a Proj4 string representation of this CRS.
void crsChanged()
Emitted when the CRS of the project has changed.
void setDpi(double dpi)
Sets the dpi (dots per inch) for the output resolution, to be used in scale calculations.
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
void zoomContent(double factor, QPointF point) override
Zooms content of item.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
virtual void setFrameStrokeWidth(QgsLayoutMeasurement width)
Sets the frame stroke width.
static const QStringList containsAdvancedEffects(const QgsMapSettings &mapSettings)
Checks whether any of the layers attached to a map settings object contain advanced effects...
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...
void projectColorsChanged()
Emitted whenever the project&#39;s color scheme has been changed.
bool drawAnnotations() const
Returns whether annotations are drawn within the map.
Flags flags() const
Returns combination of flags used for rendering.
void invalidateCache() override
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Set map of map layer style overrides (key: layer ID, value: style name) where a different style shoul...
void preparedForAtlas()
Emitted when the map has been prepared for atlas rendering, just before actual rendering.
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:1576
Enable layer opacity and blending effects.
bool hasFixedMapPosition
Definition: qgsannotation.h:68
QPolygonF transformedMapPolygon() const
Returns extent that considers rotation and shift with mOffsetX / mOffsetY.
friend class QgsLayoutItemMapOverview
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties)
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
QPointF relativePosition() const
Returns the relative position of the annotation, if it is not attached to a fixed map position...
Map extent x minimum.
Vector graphics should not be cached and drawn as raster images.
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
Abstract base class for annotation items which are drawn over a map.
Definition: qgsannotation.h:49
void setFrameStrokeWidth(QgsLayoutMeasurement width) override
Sets the frame stroke width.
The QgsMapSettings class contains configuration for rendering of the map.
PropertyValueType
Specifies whether the value returned by a function should be the original, user set value...
void readFromLayer(QgsMapLayer *layer)
Store layer&#39;s active style information in the instance.
void storeCurrentLayerStyles()
Stores the current project layer styles into style overrides.
void sizePositionChanged()
Emitted when the item&#39;s size or position changes.
Stores style information (renderer, opacity, labeling, diagrams etc.) applicable to a map layer...
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
void mapThemeChanged(const QString &theme)
Emitted when a map theme changes definition.
static QgsRectangle fromCenterAndSize(QgsPointXY center, double width, double height)
Creates a new rectangle, given the specified center point and width and height.
QgsRectangle extent() const
Returns geographical coordinates of the rectangle that should be rendered.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
QList< QgsMapLayer * > layers() const
Returns the stored layer set.
QgsRectangle extent() const
Returns the current map extent.
Layout graphical items for displaying a map.
void setOutputSize(QSize size)
Sets the size of the resulting map image.
QString layerId
Original layer ID.
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
void setTextRenderFormat(QgsRenderContext::TextRenderFormat format)
Sets the text render format, which dictates how text is rendered (e.g.
Map extent x maximum.
QgsMapThemeCollection mapThemeCollection
Definition: qgsproject.h:98
void setLength(const double length)
Sets the length of the measurement.
void setLayers(const QList< QgsMapLayer *> &layers)
Sets the stored layers set.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
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...
bool hasBackground() const
Returns true if the item has a background.
Whether vector selections should be shown in the rendered map.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:225
~QgsLayoutItemMap() override
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void attemptSetSceneRect(const QRectF &rect, bool includesFrame=false)
Attempts to update the item&#39;s position and size to match the passed rect in layout coordinates...
double atlasMargin(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue)
Returns the margin size (percentage) used when the map is in atlas mode.
static QgsRenderContext createRenderContextForMap(QgsLayoutItemMap *map, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout map and painter destination.
QColor backgroundColor() const
Returns the background color for this item.
void refresh() override
Enable anti-aliasing for map rendering.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:666
void render(QgsRenderContext &context) const
Renders the annotation to a target render context.
double mapUnitsToLayoutUnits() const
Returns the conversion factor from map units to layout units.
QPointer< QgsLayout > mLayout
void setMapUnits(QgsUnitTypes::DistanceUnit mapUnits)
Set the map units.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QString description() const
Returns the descriptive name of the CRS, e.g., "WGS 84" or "GDA 94 / Vicgrid94".
Whether to draw labels which are partially outside of the map view.
void setMoveContentPreviewOffset(double dx, double dy) override
Sets temporary offset for the item, by a specified dx and dy in layout units.
Reads and writes project states.
Definition: qgsproject.h:89
void setLabelBoundaryGeometry(const QgsGeometry &boundary)
Sets the label boundary geometry, which restricts where in the rendered map labels are permitted to b...
QgsLayoutItemMap::MapItemFlags mapFlags() const
Returns the map item&#39;s flags, which control how the map content is drawn.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the map&#39;s preset crs (coordinate reference system).
QString id() const
Returns the item&#39;s ID name.
QgsLayoutItem::Flags itemFlags() const override
Returns the item&#39;s flags, which indicate how the item behaves.
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...
Label blocking region (in map coordinates and CRS).
Single scope for storing variables and functions for use within a QgsExpressionContext.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
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...
static void rotate(double angle, double &x, double &y)
Rotates a point / vector around the origin.
If set, then raster layers will not be drawn as separate tiles. This may improve the appearance in ex...
Enable drawing of vertex markers for layers in editing mode.
The extent is adjusted so that each feature is fully visible.
An individual grid which is drawn above the map content in a QgsLayoutItemMap.
double x
Definition: qgspointxy.h:47
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation...
void mapRotationChanged(double newRotation)
Emitted when the map&#39;s rotation changes.
Use antialiasing when drawing items.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the stored overrides of styles for layers.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
void setRotation(double rotation)
Sets the rotation of the resulting map image, in degrees clockwise.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:44
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
QgsExpressionContext & expressionContext()
Gets the expression context.
Calculates scale for a given combination of canvas size, map extent, and monitor dpi.
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
void setUnits(const QgsUnitTypes::LayoutUnit units)
Sets the units for the measurement.
void setAtlasDriven(bool enabled)
Sets whether the map extent will follow the current atlas feature.
Map extent y maximum.
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project&#39;s layer tree.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
static QgsLayoutMeasurement decodeMeasurement(const QString &string)
Decodes a measurement from a string.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
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...
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
double mapRotation(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the layout item, in degrees clockwise.
Return the current evaluated value for the property.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets the global configuration of the labeling engine.
void setLabelMargin(const QgsLayoutMeasurement &margin)
Sets the margin from the map edges in which no labels may be placed.
QgsCoordinateReferenceSystem mapPositionCrs() const
Returns the CRS of the map position, or an invalid CRS if the annotation does not have a fixed map po...
Contains information about the context of a rendering operation.
void removeLabelBlockingItem(QgsLayoutItem *item)
Removes the specified layout item from the map&#39;s "label blocking items".
Force output in vector format where possible, even if items require rasterization to keep their corre...
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double length() const
Returns the length of the measurement.
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...
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
QPainter * painter()
Returns the destination QPainter for the render operation.
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
void setSelectionColor(const QColor &color)
Sets color that is used for drawing of selected vector features.
Map extent y minimum.
bool containsWmsLayer() const
Returns true if the map contains a WMS layer.
void writeXml(QDomElement &styleElement) const
Write style configuration (for project file writing)
QgsLayoutItemMapOverview * overview()
Returns the map item&#39;s first overview.
Enable advanced effects such as blend modes.
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
void updateBoundingRect()
Updates the bounding rect of this item. Call this function before doing any changes related to annota...
void refreshed()
Emitted when the layout has been refreshed and items should also be refreshed and updated...
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
The current scale of the map is used for each feature of the atlas.
QgsPointXY mapPosition
Definition: qgsannotation.h:69
double scale() const
Returns the map scale.
friend class QgsLayoutItemMapGrid
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Stores global configuration for labeling engine.
virtual QString uuid() const
Returns the item identification string.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
This class represents a coordinate reference system (CRS).
QString toWkt() const
Returns a WKT representation of this CRS.
void assignFreeId()
Sets the map id() to a number not yet used in the layout.
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:436
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Class for doing transforms between two map coordinate systems.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
void setLayers(const QList< QgsMapLayer *> &layers)
Set list of layers for map rendering.
void setPathResolver(const QgsPathResolver &resolver)
Sets the path resolver for conversion between relative and absolute paths during rendering operations...
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
void layerStyleOverridesChanged()
Emitted when layer style overrides are changed...
Enable vector simplification and other rendering optimizations.
Item overrides the default layout item painting method.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QPolygonF visiblePolygon() const
Returns the visible area as a polygon (may be rotated)
bool atlasDriven() const
Returns whether the map extent is set to follow the current atlas feature.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:230
void zoomToExtent(const QgsRectangle &extent)
Zooms the map so that the specified extent is fully visible within the map item.
AtlasScalingMode
Scaling modes used for the serial rendering (atlas)
bool isLabelBlockingItem(QgsLayoutItem *item) const
Returns true if the specified item is a "label blocking item".
int numberExportLayers() const override
Returns the number of layers that this item requires for exporting during layered exports (e...
void setMapFlags(QgsLayoutItemMap::MapItemFlags flags)
Sets the map item&#39;s flags, which control how the map content is drawn.
QString encodeMeasurement() const
Encodes the layout measurement to a string.
TYPE * resolve(const QgsProject *project)
Resolves the map layer by attempting to find a layer with matching ID within a project.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
void changed()
Emitted when the object&#39;s properties change.
DataDefinedProperty
Data defined properties for different item types.
QgsLayoutMeasurement labelMargin() const
Returns the margin from the map edges in which no labels may be placed.
void setScale(double scale, bool forceUpdate=true)
Sets new map scale and changes only the map extent.
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
QRectF boundingRect() const override
void renderSynchronously()
Render the map synchronously in this thread.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
QString authid() const
Returns the authority identifier for the CRS.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
A scale is chosen from the predefined scales.
void setFlag(Flag f, bool enabled=true)
Sets whether a particual flag is enabled.
All properties for item.
void setLabelBlockingRegions(const QList< QgsLabelBlockingRegion > &regions)
Sets a list of regions to avoid placing labels within.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QgsLayoutItemMapGrid * grid()
Returns the map item&#39;s first grid.