38 #include <QStyleOptionGraphicsItem>
43 mBackgroundUpdateTimer =
new QTimer(
this );
44 mBackgroundUpdateTimer->setSingleShot(
true );
45 connect( mBackgroundUpdateTimer, &QTimer::timeout,
this, &QgsLayoutItemMap::recreateCachedImageInBackground );
49 setCacheMode( QGraphicsItem::NoCache );
56 mGridStack = qgis::make_unique< QgsLayoutItemMapGridStack >(
this );
57 mOverviewStack = qgis::make_unique< QgsLayoutItemMapOverviewStack >(
this );
69 mPainterJob->cancel();
94 QList<QgsLayoutItemMap *> mapsList;
95 mLayout->layoutItems( mapsList );
104 if ( map->mMapId == mMapId )
107 maxId = std::max( maxId, map->mMapId );
112 mLayout->itemsModel()->updateItemDisplayName(
this );
124 return tr(
"Map %1" ).arg( mMapId );
136 mCachedLayerStyleOverridesPresetName.clear();
140 updateAtlasFeature();
145 if ( rect().isEmpty() )
150 calculator.
setDpi( 25.4 );
157 double currentScaleDenominator =
scale();
164 double scaleRatio = scaleDenominator / currentScaleDenominator;
165 mExtent.
scale( scaleRatio );
167 if ( mAtlasDriven && mAtlasScalingMode ==
Fixed )
174 calculator.
setDpi( 25.4 );
175 scaleRatio = scaleDenominator / calculator.
calculate( mExtent, rect().width() );
176 mExtent.
scale( scaleRatio );
200 QRectF currentRect = rect();
202 double newHeight = currentRect.width() * mExtent.
height() / mExtent.
width();
214 double currentWidthHeightRatio = 1.0;
215 if ( !currentExtent.
isNull() )
216 currentWidthHeightRatio = currentExtent.
width() / currentExtent.
height();
218 currentWidthHeightRatio = rect().width() / rect().height();
219 double newWidthHeightRatio = newExtent.
width() / newExtent.
height();
221 if ( currentWidthHeightRatio < newWidthHeightRatio )
224 double newHeight = newExtent.
width() / currentWidthHeightRatio;
225 double deltaHeight = newHeight - newExtent.
height();
232 double newWidth = currentWidthHeightRatio * newExtent.
height();
233 double deltaWidth = newWidth - newExtent.
width();
238 if ( mExtent == newExtent )
260 mapPolygon( mExtent, poly );
269 return mLayout->project()->crs();
280 return _qgis_listRefToRaw( mLayers );
285 mLayers = _qgis_listRawToRef(
layers );
290 if ( overrides == mLayerStyleOverrides )
293 mLayerStyleOverrides = overrides;
300 mLayerStyleOverrides.clear();
301 for (
const QgsMapLayerRef &layerRef : qgis::as_const( mLayers ) )
307 mLayerStyleOverrides.insert( layer->id(), style.
xmlData() );
314 if ( mFollowVisibilityPreset == follow )
317 mFollowVisibilityPreset = follow;
318 if ( !mFollowVisibilityPresetName.isEmpty() )
319 emit
themeChanged( mFollowVisibilityPreset ? mFollowVisibilityPresetName : QString() );
324 if ( name == mFollowVisibilityPresetName )
327 mFollowVisibilityPresetName = name;
328 if ( mFollowVisibilityPreset )
334 mLastRenderedImageOffsetX -= dx;
335 mLastRenderedImageOffsetY -= dy;
338 transformShift( dx, dy );
362 double mapY = mExtent.
yMinimum() + ( 1 - ( point.y() / rect().height() ) ) * ( mExtent.
yMaximum() - mExtent.
yMinimum() );
368 centerX = mapX + ( centerX - mapX ) * ( 1.0 / factor );
369 centerY = mapY + ( centerY - mapY ) * ( 1.0 / factor );
371 double newIntervalX, newIntervalY;
388 if ( mAtlasDriven && mAtlasScalingMode ==
Fixed )
395 calculator.
setDpi( 25.4 );
396 double scaleRatio =
scale() / calculator.
calculate( mExtent, rect().width() );
397 mExtent.
scale( scaleRatio );
413 if ( layer->dataProvider() && layer->providerType() == QLatin1String(
"wms" ) )
449 if ( mOverviewStack->containsAdvancedEffects() )
455 if ( mGridStack->containsAdvancedEffects() )
467 mMapRotation = rotation;
468 mEvaluatedMapRotation = mMapRotation;
482 mAtlasDriven = enabled;
499 double margin = mAtlasMargin;
507 margin = ddMargin / 100;
519 if ( mGridStack->size() < 1 )
522 mGridStack->addGrid(
grid );
524 return mGridStack->grid( 0 );
529 if ( mOverviewStack->size() < 1 )
532 mOverviewStack->addOverview(
overview );
534 return mOverviewStack->overview( 0 );
545 mapElem.setAttribute( QStringLiteral(
"keepLayerSet" ), QStringLiteral(
"true" ) );
549 mapElem.setAttribute( QStringLiteral(
"keepLayerSet" ), QStringLiteral(
"false" ) );
552 if ( mDrawAnnotations )
554 mapElem.setAttribute( QStringLiteral(
"drawCanvasItems" ), QStringLiteral(
"true" ) );
558 mapElem.setAttribute( QStringLiteral(
"drawCanvasItems" ), QStringLiteral(
"false" ) );
562 QDomElement extentElem = doc.createElement( QStringLiteral(
"Extent" ) );
567 mapElem.appendChild( extentElem );
571 QDomElement crsElem = doc.createElement( QStringLiteral(
"crs" ) );
573 mapElem.appendChild( crsElem );
577 mapElem.setAttribute( QStringLiteral(
"followPreset" ), mFollowVisibilityPreset ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
578 mapElem.setAttribute( QStringLiteral(
"followPresetName" ), mFollowVisibilityPresetName );
581 mapElem.setAttribute( QStringLiteral(
"mapRotation" ), QString::number( mMapRotation ) );
584 QDomElement layerSetElem = doc.createElement( QStringLiteral(
"LayerSet" ) );
589 QDomElement layerElem = doc.createElement( QStringLiteral(
"Layer" ) );
590 QDomText layerIdText = doc.createTextNode( layerRef.layerId );
591 layerElem.appendChild( layerIdText );
593 layerElem.setAttribute( QStringLiteral(
"name" ), layerRef.name );
594 layerElem.setAttribute( QStringLiteral(
"source" ), layerRef.source );
595 layerElem.setAttribute( QStringLiteral(
"provider" ), layerRef.provider );
597 layerSetElem.appendChild( layerElem );
599 mapElem.appendChild( layerSetElem );
602 if ( mKeepLayerStyles )
604 QDomElement stylesElem = doc.createElement( QStringLiteral(
"LayerStyles" ) );
605 for (
auto styleIt = mLayerStyleOverrides.constBegin(); styleIt != mLayerStyleOverrides.constEnd(); ++styleIt )
607 QDomElement styleElem = doc.createElement( QStringLiteral(
"LayerStyle" ) );
612 styleElem.setAttribute( QStringLiteral(
"layerid" ), ref.
layerId );
613 styleElem.setAttribute( QStringLiteral(
"name" ), ref.
name );
614 styleElem.setAttribute( QStringLiteral(
"source" ), ref.
source );
615 styleElem.setAttribute( QStringLiteral(
"provider" ), ref.
provider );
619 stylesElem.appendChild( styleElem );
621 mapElem.appendChild( stylesElem );
625 mGridStack->writeXml( mapElem, doc, context );
628 mOverviewStack->writeXml( mapElem, doc, context );
631 QDomElement atlasElem = doc.createElement( QStringLiteral(
"AtlasMap" ) );
632 atlasElem.setAttribute( QStringLiteral(
"atlasDriven" ), mAtlasDriven );
633 atlasElem.setAttribute( QStringLiteral(
"scalingMode" ), mAtlasScalingMode );
634 atlasElem.setAttribute( QStringLiteral(
"margin" ),
qgsDoubleToString( mAtlasMargin ) );
635 mapElem.appendChild( atlasElem );
637 mapElem.setAttribute( QStringLiteral(
"labelMargin" ), mLabelMargin.
encodeMeasurement() );
638 mapElem.setAttribute( QStringLiteral(
"mapFlags" ),
static_cast< int>( mMapFlags ) );
640 QDomElement labelBlockingItemsElem = doc.createElement( QStringLiteral(
"labelBlockingItems" ) );
641 for (
const auto &item : qgis::as_const( mBlockingLabelItems ) )
646 QDomElement blockingItemElem = doc.createElement( QStringLiteral(
"item" ) );
647 blockingItemElem.setAttribute( QStringLiteral(
"uuid" ), item->uuid() );
648 labelBlockingItemsElem.appendChild( blockingItemElem );
650 mapElem.appendChild( labelBlockingItemsElem );
653 mapElem.setAttribute( QStringLiteral(
"isTemporal" ),
isTemporal() ? 1 : 0 );
656 mapElem.setAttribute( QStringLiteral(
"temporalRangeBegin" ),
temporalRange().begin().toString( Qt::ISODate ) );
657 mapElem.setAttribute( QStringLiteral(
"temporalRangeEnd" ),
temporalRange().end().toString( Qt::ISODate ) );
665 mUpdatesEnabled =
false;
668 QDomNodeList extentNodeList = itemElem.elementsByTagName( QStringLiteral(
"Extent" ) );
669 if ( !extentNodeList.isEmpty() )
671 QDomElement extentElem = extentNodeList.at( 0 ).toElement();
672 double xmin, xmax, ymin, ymax;
673 xmin = extentElem.attribute( QStringLiteral(
"xmin" ) ).toDouble();
674 xmax = extentElem.attribute( QStringLiteral(
"xmax" ) ).toDouble();
675 ymin = extentElem.attribute( QStringLiteral(
"ymin" ) ).toDouble();
676 ymax = extentElem.attribute( QStringLiteral(
"ymax" ) ).toDouble();
680 QDomNodeList crsNodeList = itemElem.elementsByTagName( QStringLiteral(
"crs" ) );
681 if ( !crsNodeList.isEmpty() )
683 QDomElement crsElem = crsNodeList.at( 0 ).toElement();
692 mMapRotation = itemElem.attribute( QStringLiteral(
"mapRotation" ), QStringLiteral(
"0" ) ).toDouble();
693 mEvaluatedMapRotation = mMapRotation;
696 mFollowVisibilityPreset = itemElem.attribute( QStringLiteral(
"followPreset" ) ).compare( QLatin1String(
"true" ) ) == 0;
697 mFollowVisibilityPresetName = itemElem.attribute( QStringLiteral(
"followPresetName" ) );
700 QString keepLayerSetFlag = itemElem.attribute( QStringLiteral(
"keepLayerSet" ) );
701 if ( keepLayerSetFlag.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 )
703 mKeepLayerSet =
true;
707 mKeepLayerSet =
false;
710 QString drawCanvasItemsFlag = itemElem.attribute( QStringLiteral(
"drawCanvasItems" ), QStringLiteral(
"true" ) );
711 if ( drawCanvasItemsFlag.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 )
713 mDrawAnnotations =
true;
717 mDrawAnnotations =
false;
720 mLayerStyleOverrides.clear();
724 QDomNodeList layerSetNodeList = itemElem.elementsByTagName( QStringLiteral(
"LayerSet" ) );
725 if ( !layerSetNodeList.isEmpty() )
727 QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
728 QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( QStringLiteral(
"Layer" ) );
729 mLayers.reserve( layerIdNodeList.size() );
730 for (
int i = 0; i < layerIdNodeList.size(); ++i )
732 QDomElement layerElem = layerIdNodeList.at( i ).toElement();
733 QString layerId = layerElem.text();
734 QString layerName = layerElem.attribute( QStringLiteral(
"name" ) );
735 QString layerSource = layerElem.attribute( QStringLiteral(
"source" ) );
736 QString layerProvider = layerElem.attribute( QStringLiteral(
"provider" ) );
738 QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
745 QDomNodeList layerStylesNodeList = itemElem.elementsByTagName( QStringLiteral(
"LayerStyles" ) );
746 mKeepLayerStyles = !layerStylesNodeList.isEmpty();
747 if ( mKeepLayerStyles )
749 QDomElement layerStylesElem = layerStylesNodeList.at( 0 ).toElement();
750 QDomNodeList layerStyleNodeList = layerStylesElem.elementsByTagName( QStringLiteral(
"LayerStyle" ) );
751 for (
int i = 0; i < layerStyleNodeList.size(); ++i )
753 const QDomElement &layerStyleElement = layerStyleNodeList.at( i ).toElement();
754 QString layerId = layerStyleElement.attribute( QStringLiteral(
"layerid" ) );
755 QString layerName = layerStyleElement.attribute( QStringLiteral(
"name" ) );
756 QString layerSource = layerStyleElement.attribute( QStringLiteral(
"source" ) );
757 QString layerProvider = layerStyleElement.attribute( QStringLiteral(
"provider" ) );
758 QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
762 style.
readXml( layerStyleElement );
768 mNumCachedLayers = 0;
769 mCacheInvalidated =
true;
772 mOverviewStack->readXml( itemElem, doc, context );
775 mGridStack->readXml( itemElem, doc, context );
778 QDomNodeList atlasNodeList = itemElem.elementsByTagName( QStringLiteral(
"AtlasMap" ) );
779 if ( !atlasNodeList.isEmpty() )
781 QDomElement atlasElem = atlasNodeList.at( 0 ).toElement();
782 mAtlasDriven = ( atlasElem.attribute( QStringLiteral(
"atlasDriven" ), QStringLiteral(
"0" ) ) != QLatin1String(
"0" ) );
783 if ( atlasElem.hasAttribute( QStringLiteral(
"fixedScale" ) ) )
785 mAtlasScalingMode = ( atlasElem.attribute( QStringLiteral(
"fixedScale" ), QStringLiteral(
"0" ) ) != QLatin1String(
"0" ) ) ?
Fixed :
Auto;
787 else if ( atlasElem.hasAttribute( QStringLiteral(
"scalingMode" ) ) )
789 mAtlasScalingMode =
static_cast<AtlasScalingMode>( atlasElem.attribute( QStringLiteral(
"scalingMode" ) ).toInt() );
791 mAtlasMargin = atlasElem.attribute( QStringLiteral(
"margin" ), QStringLiteral(
"0.1" ) ).toDouble();
796 mMapFlags =
static_cast< MapItemFlags
>( itemElem.attribute( QStringLiteral(
"mapFlags" ),
nullptr ).toInt() );
799 mBlockingLabelItems.clear();
800 mBlockingLabelItemUuids.clear();
801 QDomNodeList labelBlockingNodeList = itemElem.elementsByTagName( QStringLiteral(
"labelBlockingItems" ) );
802 if ( !labelBlockingNodeList.isEmpty() )
804 QDomElement blockingItems = labelBlockingNodeList.at( 0 ).toElement();
805 QDomNodeList labelBlockingNodeList = blockingItems.childNodes();
806 for (
int i = 0; i < labelBlockingNodeList.size(); ++i )
808 const QDomElement &itemBlockingElement = labelBlockingNodeList.at( i ).toElement();
809 const QString itemUuid = itemBlockingElement.attribute( QStringLiteral(
"uuid" ) );
810 mBlockingLabelItemUuids << itemUuid;
817 setIsTemporal( itemElem.attribute( QStringLiteral(
"isTemporal" ) ).toInt() );
820 QDateTime begin = QDateTime::fromString( itemElem.attribute( QStringLiteral(
"temporalRangeBegin" ) ), Qt::ISODate );
821 QDateTime end = QDateTime::fromString( itemElem.attribute( QStringLiteral(
"temporalRangeEnd" ) ), Qt::ISODate );
825 mUpdatesEnabled =
true;
831 if ( !
mLayout || !painter || !painter->device() || !mUpdatesEnabled )
840 QRectF thisPaintRect = rect();
846 if (
mLayout->renderContext().isPreviewRender() )
849 painter->setClipRect( thisPaintRect );
850 if ( !mCacheFinalImage || mCacheFinalImage->isNull() )
853 painter->setBrush( QBrush( QColor( 125, 125, 125, 125 ) ) );
854 painter->drawRect( thisPaintRect );
855 painter->setBrush( Qt::NoBrush );
857 messageFont.setPointSize( 12 );
858 painter->setFont( messageFont );
859 painter->setPen( QColor( 255, 255, 255, 255 ) );
860 painter->drawText( thisPaintRect, Qt::AlignCenter | Qt::AlignHCenter, tr(
"Rendering map" ) );
861 if ( mPainterJob && mCacheInvalidated && !mDrawingPreview )
865 mBackgroundUpdateTimer->start( 1 );
867 else if ( !mPainterJob && !mDrawingPreview )
871 mBackgroundUpdateTimer->start( 1 );
876 if ( mCacheInvalidated && !mDrawingPreview )
880 mBackgroundUpdateTimer->start( 1 );
885 double imagePixelWidth = mCacheFinalImage->width();
886 double scale = rect().width() / imagePixelWidth;
890 painter->translate( mLastRenderedImageOffsetX + mXOffset, mLastRenderedImageOffsetY + mYOffset );
892 painter->drawImage( 0, 0, *mCacheFinalImage );
898 painter->setClipRect( thisPaintRect, Qt::NoClip );
900 mOverviewStack->drawItems( painter,
false );
901 mGridStack->drawItems( painter );
903 drawMapFrame( painter );
912 QPaintDevice *paintDevice = painter->device();
924 int widthInPixels =
static_cast< int >( std::round(
boundingRect().width() * layoutUnitsInInches * destinationDpi ) );
925 int heightInPixels =
static_cast< int >( std::round(
boundingRect().height() * layoutUnitsInInches * destinationDpi ) );
926 QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
928 image.fill( Qt::transparent );
929 image.setDotsPerMeterX(
static_cast< int >( std::round( 1000 * destinationDpi / 25.4 ) ) );
930 image.setDotsPerMeterY(
static_cast< int >( std::round( 1000 * destinationDpi / 25.4 ) ) );
931 double dotsPerMM = destinationDpi / 25.4;
932 QPainter p( &image );
935 QRect imagePaintRect(
static_cast< int >( std::round( tl.x() * dotsPerMM ) ),
936 static_cast< int >( std::round( tl.y() * dotsPerMM ) ),
937 static_cast< int >( std::round( thisPaintRect.width() * dotsPerMM ) ),
938 static_cast< int >( std::round( thisPaintRect.height() * dotsPerMM ) ) );
939 p.setClipRect( imagePaintRect );
941 p.translate( imagePaintRect.topLeft() );
945 if ( shouldDrawPart( Background ) )
947 p.scale( dotsPerMM, dotsPerMM );
948 drawMapBackground( &p );
949 p.scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
952 drawMap( &p, cExtent, imagePaintRect.size(), image.logicalDpiX() );
957 p.scale( dotsPerMM, dotsPerMM );
959 if ( shouldDrawPart( OverviewMapExtent ) )
961 mOverviewStack->drawItems( &p,
false );
963 if ( shouldDrawPart( Grid ) )
965 mGridStack->drawItems( &p );
970 painter->scale( 1 / dotsPerMM, 1 / dotsPerMM );
971 painter->drawImage( QPointF( -tl.x()* dotsPerMM, -tl.y() * dotsPerMM ), image );
972 painter->scale( dotsPerMM, dotsPerMM );
978 if ( shouldDrawPart( Background ) )
980 drawMapBackground( painter );
984 painter->setClipRect( thisPaintRect );
989 painter->translate( mXOffset, mYOffset );
991 double dotsPerMM = paintDevice->logicalDpiX() / 25.4;
993 painter->scale( 1 / dotsPerMM, 1 / dotsPerMM );
995 if ( mCurrentExportPart != NotLayered )
997 if ( !mStagedRendererJob )
999 createStagedRenderJob( cExtent, size, paintDevice->logicalDpiX() );
1002 mStagedRendererJob->renderCurrentPart( painter );
1006 drawMap( painter, cExtent, size, paintDevice->logicalDpiX() );
1012 painter->setClipRect( thisPaintRect, Qt::NoClip );
1014 if ( shouldDrawPart( OverviewMapExtent ) )
1016 mOverviewStack->drawItems( painter,
false );
1018 if ( shouldDrawPart( Grid ) )
1020 mGridStack->drawItems( painter );
1026 if ( shouldDrawPart( Frame ) )
1028 drawMapFrame( painter );
1039 + ( layerCount + ( layerCount ? 1 : 0 ) )
1040 + ( mGridStack->hasEnabledItems() ? 1 : 0 )
1041 + ( mOverviewStack->hasEnabledItems() ? 1 : 0 )
1047 mCurrentExportPart = Start;
1049 mExportThemes = !mFollowVisibilityPreset ?
mLayout->renderContext().exportThemes() : QStringList();
1050 mExportThemeIt = mExportThemes.begin();
1055 mCurrentExportPart = NotLayered;
1056 mExportThemes.clear();
1057 mExportThemeIt = mExportThemes.begin();
1062 switch ( mCurrentExportPart )
1067 mCurrentExportPart = Background;
1073 mCurrentExportPart = Layer;
1077 if ( mStagedRendererJob )
1079 if ( mStagedRendererJob->nextPart() )
1082 mStagedRendererJob.reset();
1085 if ( mExportThemeIt != mExportThemes.end() && ++mExportThemeIt != mExportThemes.end() )
1091 if ( mGridStack->hasEnabledItems() )
1093 mCurrentExportPart = Grid;
1099 for (
int i = 0; i < mOverviewStack->size(); ++i )
1104 mCurrentExportPart = OverviewMapExtent;
1110 case OverviewMapExtent:
1113 mCurrentExportPart = Frame;
1120 if ( isSelected() && !
mLayout->renderContext().isPreviewRender() )
1122 mCurrentExportPart = SelectionBoxes;
1127 case SelectionBoxes:
1128 mCurrentExportPart = End;
1149 switch ( mCurrentExportPart )
1159 if ( !mExportThemes.empty() && mExportThemeIt != mExportThemes.end() )
1162 if ( mStagedRendererJob )
1164 switch ( mStagedRendererJob->currentStage() )
1168 detail.
mapLayerId = mStagedRendererJob->currentLayerId();
1169 detail.
compositionMode = mStagedRendererJob->currentLayerCompositionMode();
1170 detail.
opacity = mStagedRendererJob->currentLayerOpacity();
1176 detail.
name = QStringLiteral(
"%1: %2" ).arg(
displayName(), layer->name() );
1181 const QList<QgsLayoutItemMapOverview *> res = mOverviewStack->asList();
1187 if ( item->mapLayer() && detail.
mapLayerId == item->mapLayer()->id() )
1190 detail.
name = QStringLiteral(
"%1 (%2): %3" ).arg(
displayName(), detail.
mapTheme, item->mapLayer()->name() );
1192 detail.
name = QStringLiteral(
"%1: %2" ).arg(
displayName(), item->mapLayer()->name() );
1201 detail.
mapLayerId = mStagedRendererJob->currentLayerId();
1207 detail.
name = tr(
"%1: %2 (Labels)" ).arg(
displayName(), layer->name() );
1242 case OverviewMapExtent:
1250 case SelectionBoxes:
1265 void QgsLayoutItemMap::drawMap( QPainter *painter,
const QgsRectangle &extent, QSizeF size,
double dpi )
1279 if ( shouldDrawPart( OverviewMapExtent ) )
1281 ms.setLayers( mOverviewStack->modifyMapLayerList( ms.layers() ) );
1288 job.renderSynchronously();
1290 mRenderingErrors = job.errors();
1293 void QgsLayoutItemMap::recreateCachedImageInBackground()
1299 QPainter *oldPainter = mPainter.release();
1300 QImage *oldImage = mCacheRenderingImage.release();
1303 oldJob->deleteLater();
1311 mCacheRenderingImage.reset(
nullptr );
1315 Q_ASSERT( !mPainterJob );
1316 Q_ASSERT( !mPainter );
1317 Q_ASSERT( !mCacheRenderingImage );
1323 int w =
static_cast< int >( std::round( widthLayoutUnits * mPreviewScaleFactor ) );
1324 int h =
static_cast< int >( std::round( heightLayoutUnits * mPreviewScaleFactor ) );
1327 if ( w > 5000 || h > 5000 )
1332 h =
static_cast< int>( std::round( w * heightLayoutUnits / widthLayoutUnits ) );
1337 w =
static_cast< int >( std::round( h * widthLayoutUnits / heightLayoutUnits ) );
1341 if ( w <= 0 || h <= 0 )
1344 mCacheRenderingImage.reset(
new QImage( w, h, QImage::Format_ARGB32 ) );
1347 mCacheRenderingImage->setDotsPerMeterX(
static_cast< int >( std::round( 1000 * w / widthLayoutUnits ) ) );
1348 mCacheRenderingImage->setDotsPerMeterY(
static_cast< int >( std::round( 1000 * h / heightLayoutUnits ) ) );
1359 mCacheRenderingImage->fill( QColor( 255, 255, 255, 0 ).rgba() );
1362 mCacheInvalidated =
false;
1363 mPainter.reset(
new QPainter( mCacheRenderingImage.get() ) );
1366 if ( shouldDrawPart( OverviewMapExtent ) )
1368 settings.setLayers( mOverviewStack->modifyMapLayerList( settings.layers() ) );
1373 mPainterJob->start();
1385 mDrawingPreview =
false;
1409 jobMapSettings.
setRotation( mEvaluatedMapRotation );
1413 if ( includeLayerSettings )
1421 if ( !
mLayout->renderContext().isPreviewRender() )
1456 if ( mEvaluatedLabelMargin.
length() > 0 )
1459 visiblePoly.append( visiblePoly.at( 0 ) );
1460 const double layoutLabelMargin =
mLayout->convertToLayoutUnits( mEvaluatedLabelMargin );
1461 const double layoutLabelMarginInMapUnits = layoutLabelMargin / rect().width() * jobMapSettings.
extent().
width();
1463 mapBoundaryGeom = mapBoundaryGeom.
buffer( -layoutLabelMarginInMapUnits, 0 );
1467 if ( !mBlockingLabelItems.isEmpty() )
1480 return jobMapSettings;
1487 mBlockingLabelItems.clear();
1488 for (
const QString &
uuid : qgis::as_const( mBlockingLabelItemUuids ) )
1497 mOverviewStack->finalizeRestoreFromXml();
1498 mGridStack->finalizeRestoreFromXml();
1509 return mCurrentRectangle;
1542 QVariantList layersIds;
1552 const QList<QgsMapLayer *> layersInMap =
layersToRender( &context );
1554 layersIds.reserve( layersInMap.count() );
1555 layers.reserve( layersInMap.count() );
1558 layersIds << layer->id();
1564 scope->
addFunction( QStringLiteral(
"is_layer_visible" ),
new QgsExpressionContextUtils::GetLayerVisibility( layersInMap,
scale() ) );
1576 if ( extentWidth <= 0 )
1580 return rect().width() / extentWidth;
1585 double dx = mXOffset;
1586 double dy = mYOffset;
1587 transformShift( dx, dy );
1589 poly.translate( -dx, -dy );
1595 if ( !mBlockingLabelItems.contains( item ) )
1596 mBlockingLabelItems.append( item );
1603 mBlockingLabelItems.removeAll( item );
1610 return mBlockingLabelItems.contains( item );
1619 if ( mOverviewStack )
1621 for (
int i = 0; i < mOverviewStack->size(); ++i )
1623 if ( mOverviewStack->item( i )->accept( visitor ) )
1630 for (
int i = 0; i < mGridStack->size(); ++i )
1632 if ( mGridStack->item( i )->accept( visitor ) )
1645 mRenderedFeatureHandlers.append( handler );
1650 mRenderedFeatureHandlers.removeAll( handler );
1656 if ( mapPoly.empty() )
1658 return QPointF( 0, 0 );
1663 double dx = mapCoords.x() - rotationPoint.
x();
1664 double dy = mapCoords.y() - rotationPoint.
y();
1666 QgsPointXY backRotatedCoords( rotationPoint.
x() + dx, rotationPoint.
y() + dy );
1669 double xItem = rect().width() * ( backRotatedCoords.
x() - unrotatedExtent.
xMinimum() ) / unrotatedExtent.
width();
1670 double yItem = rect().height() * ( 1 - ( backRotatedCoords.
y() - unrotatedExtent.
yMinimum() ) / unrotatedExtent.
height() );
1671 return QPointF( xItem, yItem );
1685 mapPolygon( newExtent, poly );
1686 QRectF bRect = poly.boundingRect();
1700 mCacheInvalidated =
true;
1706 QRectF rectangle = rect();
1707 double frameExtension =
frameEnabled() ? pen().widthF() / 2.0 : 0.0;
1709 double topExtension = 0.0;
1710 double rightExtension = 0.0;
1711 double bottomExtension = 0.0;
1712 double leftExtension = 0.0;
1715 mGridStack->calculateMaxGridExtension( topExtension, rightExtension, bottomExtension, leftExtension );
1717 topExtension = std::max( topExtension, frameExtension );
1718 rightExtension = std::max( rightExtension, frameExtension );
1719 bottomExtension = std::max( bottomExtension, frameExtension );
1720 leftExtension = std::max( leftExtension, frameExtension );
1722 rectangle.setLeft( rectangle.left() - leftExtension );
1723 rectangle.setRight( rectangle.right() + rightExtension );
1724 rectangle.setTop( rectangle.top() - topExtension );
1725 rectangle.setBottom( rectangle.bottom() + bottomExtension );
1726 if ( rectangle != mCurrentRectangle )
1728 prepareGeometryChange();
1729 mCurrentRectangle = rectangle;
1751 refreshMapExtents( &context );
1753 if ( mExtent != beforeExtent )
1760 refreshLabelMargin(
false );
1764 const QString previousTheme = mLastEvaluatedThemeName.isEmpty() ? mFollowVisibilityPresetName : mLastEvaluatedThemeName;
1766 if ( mLastEvaluatedThemeName != previousTheme )
1784 mCacheInvalidated =
true;
1789 void QgsLayoutItemMap::layersAboutToBeRemoved(
const QList<QgsMapLayer *> &layers )
1791 if ( !mLayers.isEmpty() || mLayerStyleOverrides.isEmpty() )
1795 mLayerStyleOverrides.remove( layer->id() );
1797 _qgis_removeLayers( mLayers,
layers );
1801 void QgsLayoutItemMap::painterJobFinished()
1804 mPainterJob.reset(
nullptr );
1805 mPainter.reset(
nullptr );
1806 mCacheFinalImage = std::move( mCacheRenderingImage );
1807 mLastRenderedImageOffsetX = 0;
1808 mLastRenderedImageOffsetY = 0;
1813 void QgsLayoutItemMap::shapeChanged()
1818 double w = rect().width();
1819 double h = rect().height();
1822 double newWidth = mExtent.
width();
1824 double newHeight = newWidth * h / w;
1829 refreshMapExtents();
1836 void QgsLayoutItemMap::mapThemeChanged(
const QString &theme )
1838 if ( theme == mCachedLayerStyleOverridesPresetName )
1839 mCachedLayerStyleOverridesPresetName.clear();
1842 void QgsLayoutItemMap::currentMapThemeRenamed(
const QString &theme,
const QString &newTheme )
1844 if ( theme == mFollowVisibilityPresetName )
1846 mFollowVisibilityPresetName = newTheme;
1850 void QgsLayoutItemMap::connectUpdateSlot()
1858 this, &QgsLayoutItemMap::layersAboutToBeRemoved );
1862 if ( layers().isEmpty() )
1890 if ( mAtlasScalingMode == Predefined )
1891 updateAtlasFeature();
1898 QTransform mapTransform;
1899 QPolygonF thisRectPoly = QPolygonF( QRectF( 0, 0, rect().width(), rect().height() ) );
1901 thisRectPoly.pop_back();
1902 thisExtent.pop_back();
1904 QPolygonF thisItemPolyInLayout = mapToScene( thisRectPoly );
1907 QTransform::quadToQuad( thisItemPolyInLayout, thisExtent, mapTransform );
1908 return mapTransform;
1911 QList<QgsLabelBlockingRegion> QgsLayoutItemMap::createLabelBlockingRegions(
const QgsMapSettings & )
const
1914 QList< QgsLabelBlockingRegion > blockers;
1915 blockers.reserve( mBlockingLabelItems.count() );
1916 for (
const auto &item : qgis::as_const( mBlockingLabelItems ) )
1923 if ( item->property(
"wasVisible" ).isValid() )
1925 if ( !item->property(
"wasVisible" ).toBool() )
1928 else if ( !item->isVisible() )
1931 QPolygonF itemRectInMapCoordinates = mapTransform.map( item->mapToScene( item->rect() ) );
1932 itemRectInMapCoordinates.append( itemRectInMapCoordinates.at( 0 ) );
1941 return mLabelMargin;
1946 mLabelMargin = margin;
1947 refreshLabelMargin(
false );
1950 void QgsLayoutItemMap::updateToolTip()
1959 if ( mFollowVisibilityPreset )
1961 presetName = mFollowVisibilityPresetName;
1965 else if ( !mExportThemes.empty() && mExportThemeIt != mExportThemes.end() )
1966 presetName = *mExportThemeIt;
1977 QList<QgsMapLayer *> renderLayers;
1979 QString presetName = themeToRender( *evalContext );
1980 if ( !presetName.isEmpty() )
1982 if (
mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
1983 renderLayers =
mLayout->project()->mapThemeCollection()->mapThemeVisibleLayers( presetName );
1985 renderLayers =
mLayout->project()->mapThemeCollection()->masterVisibleLayers();
1987 else if ( !
layers().isEmpty() )
1993 renderLayers =
mLayout->project()->mapThemeCollection()->masterVisibleLayers();
2000 renderLayers.clear();
2002 const QStringList layerNames = ddLayers.split(
'|' );
2004 for (
const QString &name : layerNames )
2006 const QList< QgsMapLayer * > matchingLayers =
mLayout->project()->mapLayersByName( name );
2009 renderLayers << layer;
2018 int removeAt = renderLayers.indexOf(
mLayout->reportContext().layer() );
2019 if ( removeAt != -1 )
2021 renderLayers.removeAt( removeAt );
2026 renderLayers.erase( std::remove_if( renderLayers.begin(), renderLayers.end(), [](
QgsMapLayer * layer )
2028 return !layer || !layer->isValid();
2029 } ), renderLayers.end() );
2031 return renderLayers;
2034 QMap<QString, QString> QgsLayoutItemMap::layerStyleOverridesToRender(
const QgsExpressionContext &context )
const
2036 QString presetName = themeToRender( context );
2037 if ( !presetName.isEmpty() )
2039 if (
mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
2041 if ( presetName != mCachedLayerStyleOverridesPresetName )
2044 mCachedPresetLayerStyleOverrides =
mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( presetName );
2045 mCachedLayerStyleOverridesPresetName = presetName;
2048 return mCachedPresetLayerStyleOverrides;
2051 return QMap<QString, QString>();
2053 else if ( mFollowVisibilityPreset )
2055 QString presetName = mFollowVisibilityPresetName;
2058 if (
mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
2060 if ( presetName.isEmpty() || presetName != mCachedLayerStyleOverridesPresetName )
2063 mCachedPresetLayerStyleOverrides =
mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( presetName );
2064 mCachedLayerStyleOverridesPresetName = presetName;
2067 return mCachedPresetLayerStyleOverrides;
2070 return QMap<QString, QString>();
2072 else if ( mKeepLayerStyles )
2074 return mLayerStyleOverrides;
2078 return QMap<QString, QString>();
2082 QgsRectangle QgsLayoutItemMap::transformedExtent()
const
2084 double dx = mXOffset;
2085 double dy = mYOffset;
2086 transformShift( dx, dy );
2090 void QgsLayoutItemMap::mapPolygon(
const QgsRectangle &extent, QPolygonF &poly )
const
2100 poly << QPointF( poly.at( 0 ) );
2112 poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2118 poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2124 poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2130 poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2133 poly << QPointF( poly.at( 0 ) );
2136 void QgsLayoutItemMap::transformShift(
double &xShift,
double &yShift )
const
2139 double dxScaled = xShift * mmToMapUnits;
2140 double dyScaled = - yShift * mmToMapUnits;
2155 const QList< QgsAnnotation * > annotations =
mLayout->project()->annotationManager()->annotations();
2156 if ( annotations.isEmpty() )
2166 if ( !annotation || !annotation->isVisible() )
2170 if ( annotation->mapLayer() && !
layers.contains( annotation->mapLayer() ) )
2173 drawAnnotation( annotation, rc );
2187 double itemX, itemY;
2190 QPointF mapPos = layoutMapPosForItem( annotation );
2199 context.
painter()->translate( itemX, itemY );
2202 double dotsPerMM = context.
painter()->device()->logicalDpiX() / 25.4;
2203 context.
painter()->scale( 1 / dotsPerMM, 1 / dotsPerMM );
2205 annotation->
render( context );
2209 QPointF QgsLayoutItemMap::layoutMapPosForItem(
const QgsAnnotation *annotation )
const
2212 return QPointF( 0, 0 );
2221 if ( annotationCrs !=
crs() )
2228 t.transformInPlace( mapX, mapY, z );
2238 void QgsLayoutItemMap::drawMapFrame( QPainter *p )
2249 void QgsLayoutItemMap::drawMapBackground( QPainter *p )
2260 bool QgsLayoutItemMap::shouldDrawPart( QgsLayoutItemMap::PartType part )
const
2262 if ( mCurrentExportPart == NotLayered )
2280 return mCurrentExportPart == Layer;
2283 return mCurrentExportPart == Grid && mGridStack->hasEnabledItems();
2285 case OverviewMapExtent:
2286 return mCurrentExportPart == OverviewMapExtent && mOverviewStack->hasEnabledItems();
2291 case SelectionBoxes:
2292 return mCurrentExportPart == SelectionBoxes && isSelected();
2313 bool useDdXMin =
false;
2314 bool useDdXMax =
false;
2315 bool useDdYMin =
false;
2316 bool useDdYMax =
false;
2347 if ( newExtent != mExtent )
2353 double currentWidthHeightRatio = mExtent.
width() / mExtent.
height();
2354 double newWidthHeightRatio = newExtent.
width() / newExtent.
height();
2356 if ( currentWidthHeightRatio < newWidthHeightRatio )
2359 double newHeight = newExtent.
width() / currentWidthHeightRatio;
2360 double deltaHeight = newHeight - newExtent.
height();
2367 double newWidth = currentWidthHeightRatio * newExtent.
height();
2368 double deltaWidth = newWidth - newExtent.
width();
2373 mExtent = newExtent;
2383 newExtent = mExtent;
2386 if ( useDdXMax || useDdXMin || useDdYMax || useDdYMin )
2390 if ( useDdXMin && !useDdXMax )
2396 else if ( !useDdXMin && useDdXMax )
2402 if ( useDdYMin && !useDdYMax )
2408 else if ( !useDdYMin && useDdYMax )
2415 if ( newExtent != mExtent )
2417 mExtent = newExtent;
2434 void QgsLayoutItemMap::refreshLabelMargin(
bool updateItem )
2447 void QgsLayoutItemMap::updateAtlasFeature()
2466 if ( mAtlasScalingMode ==
Fixed || mAtlasScalingMode ==
Predefined || isPointLayer )
2471 double originalScale = calc.
calculate( originalExtent, rect().width() );
2472 double geomCenterX = ( xa1 + xa2 ) / 2.0;
2473 double geomCenterY = ( ya1 + ya2 ) / 2.0;
2474 QVector<qreal> scales;
2476 if ( !
mLayout->reportContext().predefinedScales().empty() )
2477 scales =
mLayout->reportContext().predefinedScales();
2479 scales =
mLayout->renderContext().predefinedScales();
2481 if ( mAtlasScalingMode ==
Fixed || isPointLayer || scales.isEmpty() )
2484 double xMin = geomCenterX - originalExtent.
width() / 2.0;
2485 double yMin = geomCenterY - originalExtent.
height() / 2.0;
2488 xMin + originalExtent.
width(),
2489 yMin + originalExtent.
height() );
2493 double newScale = calc.
calculate( newExtent, rect().width() );
2494 newExtent.
scale( originalScale / newScale );
2499 double newWidth = originalExtent.
width();
2500 double newHeight = originalExtent.
height();
2501 for (
int i = 0; i < scales.size(); i++ )
2503 double ratio = scales[i] / originalScale;
2504 newWidth = originalExtent.
width() * ratio;
2505 newHeight = originalExtent.
height() * ratio;
2508 double xMin = geomCenterX - newWidth / 2.0;
2509 double yMin = geomCenterY - newHeight / 2.0;
2517 double newScale = calc.
calculate( newExtent, rect().width() );
2518 newExtent.
scale( scales[i] / newScale );
2528 else if ( mAtlasScalingMode ==
Auto )
2532 double geomRatio = bounds.
width() / bounds.
height();
2533 double mapRatio = originalExtent.
width() / originalExtent.
height();
2536 if ( geomRatio < mapRatio )
2539 double adjWidth = ( mapRatio * bounds.
height() - bounds.
width() ) / 2.0;
2544 else if ( geomRatio > mapRatio )
2547 double adjHeight = ( bounds.
width() / mapRatio - bounds.
height() ) / 2.0;
2553 const double evaluatedAtlasMargin =
atlasMargin();
2554 if ( evaluatedAtlasMargin > 0.0 )
2556 newExtent.
scale( 1 + evaluatedAtlasMargin );
2572 if ( mEvaluatedMapRotation != 0.0 )
2582 double dx = std::max( std::abs( prevCenter.
x() - bounds.
xMinimum() ),
2583 std::abs( prevCenter.
x() - bounds.
xMaximum() ) );
2584 double dy = std::max( std::abs( prevCenter.
y() - bounds.
yMinimum() ),
2585 std::abs( prevCenter.
y() - bounds.
yMaximum() ) );
2588 center.
x() + dx, center.
y() + dy );
2596 void QgsLayoutItemMap::createStagedRenderJob(
const QgsRectangle &extent,
const QSizeF size,
double dpi )
2599 settings.
setLayers( mOverviewStack->modifyMapLayerList( settings.
layers() ) );
2601 mStagedRendererJob = qgis::make_unique< QgsMapRendererStagedRenderJob >( settings,
2604 : QgsMapRendererStagedRenderJob::Flags(
nullptr ) );
2605 mStagedRendererJob->start();