diff --git a/python/core/auto_generated/layout/qgslayoutobject.sip.in b/python/core/auto_generated/layout/qgslayoutobject.sip.in
index 6f8d6a194ed..7ad5a7f6681 100644
--- a/python/core/auto_generated/layout/qgslayoutobject.sip.in
+++ b/python/core/auto_generated/layout/qgslayoutobject.sip.in
@@ -135,6 +135,7 @@ A base class for objects which belong to a layout.
MapAtlasMargin,
MapLayers,
MapStylePreset,
+ MapLabelMargin,
//composer picture
PictureSource,
PictureSvgBackgroundColor,
diff --git a/src/app/layout/qgslayoutmapwidget.cpp b/src/app/layout/qgslayoutmapwidget.cpp
index a1ca51061e2..3d53a896a63 100644
--- a/src/app/layout/qgslayoutmapwidget.cpp
+++ b/src/app/layout/qgslayoutmapwidget.cpp
@@ -75,6 +75,8 @@ QgsLayoutMapWidget::QgsLayoutMapWidget( QgsLayoutItemMap *item )
connect( mOverviewCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mOverviewCheckBox_toggled );
connect( mOverviewListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged );
connect( mOverviewListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_itemChanged );
+ connect( mLabelSettingsButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::showLabelSettings );
+
setPanelTitle( tr( "Map Properties" ) );
mMapRotationSpinBox->setClearValue( 0 );
@@ -344,6 +346,12 @@ void QgsLayoutMapWidget::overviewSymbolChanged()
mMapItem->update();
}
+void QgsLayoutMapWidget::showLabelSettings()
+{
+ QgsLayoutMapLabelingWidget *w = new QgsLayoutMapLabelingWidget( mMapItem );
+ openPanel( w );
+}
+
void QgsLayoutMapWidget::mAtlasCheckBox_toggled( bool checked )
{
if ( !mMapItem )
@@ -1635,3 +1643,53 @@ void QgsLayoutMapWidget::mOverviewCenterCheckbox_toggled( bool state )
mMapItem->update();
mMapItem->endCommand();
}
+
+//
+// QgsLayoutMapLabelingWidget
+//
+
+QgsLayoutMapLabelingWidget::QgsLayoutMapLabelingWidget( QgsLayoutItemMap *map )
+ : QgsLayoutItemBaseWidget( nullptr, map )
+ , mMapItem( map )
+{
+ setupUi( this );
+ setPanelTitle( tr( "Label Settings" ) );
+
+ mLabelBoundarySpinBox->setClearValue( 0 );
+ mLabelBoundarySpinBox->setShowClearButton( true );
+
+ mLabelBoundaryUnitsCombo->linkToWidget( mLabelBoundarySpinBox );
+ mLabelBoundaryUnitsCombo->setConverter( &mMapItem->layout()->renderContext().measurementConverter() );
+
+ mLabelBoundarySpinBox->setValue( mMapItem->labelMargin().length() );
+ mLabelBoundaryUnitsCombo->setUnit( mMapItem->labelMargin().units() );
+
+ connect( mLabelBoundaryUnitsCombo, &QgsLayoutUnitsComboBox::changed, this, &QgsLayoutMapLabelingWidget::labelMarginUnitsChanged );
+ connect( mLabelBoundarySpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutMapLabelingWidget::labelMarginChanged );
+
+ registerDataDefinedButton( mLabelMarginDDBtn, QgsLayoutObject::MapLabelMargin );
+
+ updateDataDefinedButton( mLabelMarginDDBtn );
+}
+
+void QgsLayoutMapLabelingWidget::labelMarginChanged( double val )
+{
+ if ( !mMapItem )
+ return;
+
+ mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin );
+ mMapItem->setLabelMargin( QgsLayoutMeasurement( val, mLabelBoundaryUnitsCombo->unit() ) );
+ mMapItem->layout()->undoStack()->endCommand();
+ mMapItem->invalidateCache();
+}
+
+void QgsLayoutMapLabelingWidget::labelMarginUnitsChanged()
+{
+ if ( !mMapItem )
+ return;
+
+ mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin );
+ mMapItem->setLabelMargin( QgsLayoutMeasurement( mLabelBoundarySpinBox->value(), mLabelBoundaryUnitsCombo->unit() ) );
+ mMapItem->layout()->undoStack()->endCommand();
+ mMapItem->invalidateCache();
+}
diff --git a/src/app/layout/qgslayoutmapwidget.h b/src/app/layout/qgslayoutmapwidget.h
index f35e25c0b6d..df9643ff3b9 100644
--- a/src/app/layout/qgslayoutmapwidget.h
+++ b/src/app/layout/qgslayoutmapwidget.h
@@ -19,6 +19,7 @@
#define QGSLAYOUTMAPWIDGET_H
#include "ui_qgslayoutmapwidgetbase.h"
+#include "ui_qgslayoutmaplabelingwidgetbase.h"
#include "qgslayoutitemwidget.h"
#include "qgslayoutitemmapgrid.h"
@@ -116,6 +117,7 @@ class QgsLayoutMapWidget: public QgsLayoutItemBaseWidget, private Ui::QgsLayoutM
void mapCrsChanged( const QgsCoordinateReferenceSystem &crs );
void overviewSymbolChanged();
+ void showLabelSettings();
private:
QPointer< QgsLayoutItemMap > mMapItem;
QgsLayoutItemPropertiesWidget *mItemPropertiesWidget = nullptr;
@@ -164,4 +166,24 @@ class QgsLayoutMapWidget: public QgsLayoutItemBaseWidget, private Ui::QgsLayoutM
};
+
+/**
+ * \ingroup app
+ * Allows configuration of layout map labeling settings.
+ * */
+class QgsLayoutMapLabelingWidget: public QgsLayoutItemBaseWidget, private Ui::QgsLayoutMapLabelingWidgetBase
+{
+ Q_OBJECT
+
+ public:
+ explicit QgsLayoutMapLabelingWidget( QgsLayoutItemMap *map );
+
+ private slots:
+ void labelMarginChanged( double val );
+ void labelMarginUnitsChanged();
+
+ private:
+ QPointer< QgsLayoutItemMap > mMapItem;
+};
+
#endif
diff --git a/src/core/layout/qgslayoutitemmap.cpp b/src/core/layout/qgslayoutitemmap.cpp
index 6399f82e492..4edcad80d75 100644
--- a/src/core/layout/qgslayoutitemmap.cpp
+++ b/src/core/layout/qgslayoutitemmap.cpp
@@ -742,7 +742,7 @@ bool QgsLayoutItemMap::readPropertiesFromElement( const QDomElement &itemElem, c
mAtlasMargin = atlasElem.attribute( QStringLiteral( "margin" ), QStringLiteral( "0.1" ) ).toDouble();
}
- mLabelMargin = QgsLayoutMeasurement::decodeMeasurement( itemElem.attribute( QStringLiteral( "labelMargin" ), QStringLiteral( "0" ) ) );
+ setLabelMargin( QgsLayoutMeasurement::decodeMeasurement( itemElem.attribute( QStringLiteral( "labelMargin" ), QStringLiteral( "0" ) ) ) );
updateBoundingRect();
@@ -1131,11 +1131,11 @@ QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF
// override the default text render format inherited from the labeling engine settings using the layout's render context setting
jobMapSettings.setTextRenderFormat( mLayout->renderContext().textRenderFormat() );
- if ( mLabelMargin.length() > 0 )
+ if ( mEvaluatedLabelMargin.length() > 0 )
{
QPolygonF visiblePoly = jobMapSettings.visiblePolygon();
visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
- const double layoutLabelMargin = mLayout->convertToLayoutUnits( mLabelMargin );
+ const double layoutLabelMargin = mLayout->convertToLayoutUnits( mEvaluatedLabelMargin );
const double layoutLabelMarginInMapUnits = layoutLabelMargin / rect().width() * jobMapSettings.extent().width();
QgsGeometry mapBoundaryGeom = QgsGeometry::fromQPolygonF( visiblePoly );
mapBoundaryGeom = mapBoundaryGeom.buffer( -layoutLabelMarginInMapUnits, 0 );
@@ -1335,6 +1335,10 @@ void QgsLayoutItemMap::refreshDataDefinedProperty( const QgsLayoutObject::DataDe
emit extentChanged();
}
}
+ if ( property == QgsLayoutObject::MapLabelMargin || property == QgsLayoutObject::AllProperties )
+ {
+ refreshLabelMargin( false );
+ }
//force redraw
mCacheInvalidated = true;
@@ -1436,6 +1440,7 @@ QgsLayoutMeasurement QgsLayoutItemMap::labelMargin() const
void QgsLayoutItemMap::setLabelMargin( const QgsLayoutMeasurement &margin )
{
mLabelMargin = margin;
+ refreshLabelMargin( false );
}
void QgsLayoutItemMap::updateToolTip()
@@ -1900,6 +1905,19 @@ void QgsLayoutItemMap::refreshMapExtents( const QgsExpressionContext *context )
}
}
+void QgsLayoutItemMap::refreshLabelMargin( bool updateItem )
+{
+ //data defined label margin set?
+ double labelMargin = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapLabelMargin, createExpressionContext(), mLabelMargin.length() );
+ mEvaluatedLabelMargin.setLength( labelMargin );
+ mEvaluatedLabelMargin.setUnits( mLabelMargin.units() );
+
+ if ( updateItem )
+ {
+ update();
+ }
+}
+
void QgsLayoutItemMap::updateAtlasFeature()
{
if ( !atlasDriven() || !mLayout->reportContext().layer() )
diff --git a/src/core/layout/qgslayoutitemmap.h b/src/core/layout/qgslayoutitemmap.h
index 2433c719256..1135a3601cf 100644
--- a/src/core/layout/qgslayoutitemmap.h
+++ b/src/core/layout/qgslayoutitemmap.h
@@ -646,6 +646,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
bool mPainterCancelWait = false;
QgsLayoutMeasurement mLabelMargin{ 0 };
+ QgsLayoutMeasurement mEvaluatedLabelMargin{ 0 };
void init();
@@ -694,6 +695,8 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
*/
void refreshMapExtents( const QgsExpressionContext *context = nullptr );
+ void refreshLabelMargin( bool updateItem );
+
void updateAtlasFeature();
QgsRectangle computeAtlasRectangle();
diff --git a/src/core/layout/qgslayoutobject.cpp b/src/core/layout/qgslayoutobject.cpp
index ecdeda57ecb..7f8b3b1b8d5 100644
--- a/src/core/layout/qgslayoutobject.cpp
+++ b/src/core/layout/qgslayoutobject.cpp
@@ -63,6 +63,7 @@ void QgsLayoutObject::initPropertyDefinitions()
{ QgsLayoutObject::MapYMin, QgsPropertyDefinition( "dataDefinedMapYMin", QObject::tr( "Extent minimum Y" ), QgsPropertyDefinition::Double ) },
{ QgsLayoutObject::MapXMax, QgsPropertyDefinition( "dataDefinedMapXMax", QObject::tr( "Extent maximum X" ), QgsPropertyDefinition::Double ) },
{ QgsLayoutObject::MapYMax, QgsPropertyDefinition( "dataDefinedMapYMax", QObject::tr( "Extent maximum Y" ), QgsPropertyDefinition::Double ) },
+ { QgsLayoutObject::MapLabelMargin, QgsPropertyDefinition( "dataDefinedMapLabelMargin", QObject::tr( "Label margin" ), QgsPropertyDefinition::DoublePositive ) },
{ QgsLayoutObject::MapAtlasMargin, QgsPropertyDefinition( "dataDefinedMapAtlasMargin", QObject::tr( "Atlas margin" ), QgsPropertyDefinition::DoublePositive ) },
{ QgsLayoutObject::MapLayers, QgsPropertyDefinition( "dataDefinedMapLayers", QgsPropertyDefinition::DataTypeString, QObject::tr( "Map Layers" ), tr( "list of map layer names separated by | characters" ) ) },
{ QgsLayoutObject::MapStylePreset, QgsPropertyDefinition( "dataDefinedMapStylePreset", QgsPropertyDefinition::DataTypeString, QObject::tr( "Map theme" ), tr( "name of an existing map theme (case-sensitive)" ) ) },
diff --git a/src/core/layout/qgslayoutobject.h b/src/core/layout/qgslayoutobject.h
index b9c1f0bd335..4a1d0d24ebe 100644
--- a/src/core/layout/qgslayoutobject.h
+++ b/src/core/layout/qgslayoutobject.h
@@ -163,6 +163,7 @@ class CORE_EXPORT QgsLayoutObject: public QObject, public QgsExpressionContextGe
MapAtlasMargin, //!< Map atlas margin
MapLayers, //!< Map layer set
MapStylePreset, //!< Layer and style map theme
+ MapLabelMargin, //!< Map label margin
//composer picture
PictureSource, //!< Picture source url
PictureSvgBackgroundColor, //!< SVG background color
diff --git a/src/ui/layout/qgslayoutmaplabelingwidgetbase.ui b/src/ui/layout/qgslayoutmaplabelingwidgetbase.ui
new file mode 100644
index 00000000000..d1135082b05
--- /dev/null
+++ b/src/ui/layout/qgslayoutmaplabelingwidgetbase.ui
@@ -0,0 +1,134 @@
+
+
+ QgsLayoutMapLabelingWidgetBase
+
+
+
+ 0
+ 0
+ 326
+ 424
+
+
+
+
+ 0
+ 0
+
+
+
+ Map Options
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Label Settings
+
+
+
-
+
+
+ of map edges
+
+
+ true
+
+
+
+ -
+
+
+
+
+
+ 0.000000000000000
+
+
+ 9999.000000000000000
+
+
+ 0.000000000000000
+
+
+ false
+
+
+
+ -
+
+
+ Avoid placing labels within
+
+
+ true
+
+
+
+ -
+
+
+ -
+
+
+ …
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+ QgsCollapsibleGroupBoxBasic
+ QGroupBox
+
+ 1
+
+
+ QgsDoubleSpinBox
+ QDoubleSpinBox
+
+
+
+ QgsPropertyOverrideButton
+ QToolButton
+ qgspropertyoverridebutton.h
+
+
+ QgsLayoutUnitsComboBox
+ QComboBox
+
+
+
+
+
+
diff --git a/src/ui/layout/qgslayoutmapwidgetbase.ui b/src/ui/layout/qgslayoutmapwidgetbase.ui
index a0bf3cdaf9f..3510a655b7c 100644
--- a/src/ui/layout/qgslayoutmapwidgetbase.ui
+++ b/src/ui/layout/qgslayoutmapwidgetbase.ui
@@ -63,9 +63,9 @@
0
- -1556
- 627
- 2265
+ 0
+ 368
+ 1373
@@ -396,20 +396,27 @@
-
- Set to map canvas extent
+ Set to Map Canvas Extent
-
- View extent in map canvas
+ View Extent in Map Canvas
+ -
+
+
+ Label Settings…
+
+
+
-
@@ -600,7 +607,7 @@
-
- Modify grid…
+ Modify Grid…
@@ -793,9 +800,10 @@
1
- QgsPropertyOverrideButton
- QToolButton
- qgspropertyoverridebutton.h
+ QgsCollapsibleGroupBoxBasic
+ QGroupBox
+
+ 1
QgsDoubleSpinBox
@@ -807,17 +815,21 @@
QSpinBox
-
- QgsCollapsibleGroupBoxBasic
- QGroupBox
-
- 1
-
QgsBlendModeComboBox
QComboBox
+
+ QgsSymbolButton
+ QToolButton
+
+
+
+ QgsPropertyOverrideButton
+ QToolButton
+ qgspropertyoverridebutton.h
+
QgsProjectionSelectionWidget
QWidget
@@ -829,11 +841,6 @@
QComboBox
-
- QgsSymbolButton
- QToolButton
-
-
scrollArea
@@ -863,6 +870,7 @@
mYMaxDDBtn
mSetToMapCanvasExtentButton
mViewExtentInCanvasButton
+ mLabelSettingsButton
mAtlasCheckBox
mAtlasMarginRadio
mAtlasMarginSpinBox
@@ -920,6 +928,11 @@
+
+
+
+
+
diff --git a/tests/src/python/test_qgslayoutmap.py b/tests/src/python/test_qgslayoutmap.py
index ea1ccba7ae2..144ce329ddb 100644
--- a/tests/src/python/test_qgslayoutmap.py
+++ b/tests/src/python/test_qgslayoutmap.py
@@ -38,7 +38,9 @@ from qgis.core import (QgsLayoutItemMap,
QgsVectorLayerSimpleLabeling,
QgsLabelingEngineSettings,
QgsLayoutMeasurement,
- QgsUnitTypes)
+ QgsUnitTypes,
+ QgsLayoutObject,
+ QgsProperty)
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
@@ -326,6 +328,17 @@ class TestQgsLayoutMap(unittest.TestCase, LayoutItemTestCase):
self.report += checker.report()
self.assertTrue(result, message)
+ # data defined
+ map.setMapRotation(0)
+ map.zoomToExtent(vl.extent())
+ map.dataDefinedProperties().setProperty(QgsLayoutObject.MapLabelMargin, QgsProperty.fromExpression('1+3'))
+ map.refresh()
+ checker = QgsLayoutChecker('composermap_dd_label_margin', layout)
+ checker.setControlPathPrefix("composer_map")
+ result, message = checker.testLayout()
+ self.report += checker.report()
+ self.assertTrue(result, message)
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/testdata/control_images/composer_map/expected_composermap_dd_label_margin/expected_composermap_dd_label_margin.png b/tests/testdata/control_images/composer_map/expected_composermap_dd_label_margin/expected_composermap_dd_label_margin.png
new file mode 100644
index 00000000000..7e394cb0110
Binary files /dev/null and b/tests/testdata/control_images/composer_map/expected_composermap_dd_label_margin/expected_composermap_dd_label_margin.png differ