Skip to content

Commit ed25a3e

Browse files
committedDec 15, 2018
[FEATURE][layouts] Add setting for label margin for map items
This setting allows per-map control of how close labels are permitted to be placed to the map item's edges. Sizes can be set using mm/inches/pixels/etc, and data defined label margins are allowed. Fixes #10314
1 parent 35855b8 commit ed25a3e

File tree

11 files changed

+288
-24
lines changed

11 files changed

+288
-24
lines changed
 

‎python/core/auto_generated/layout/qgslayoutobject.sip.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ A base class for objects which belong to a layout.
135135
MapAtlasMargin,
136136
MapLayers,
137137
MapStylePreset,
138+
MapLabelMargin,
138139
//composer picture
139140
PictureSource,
140141
PictureSvgBackgroundColor,

‎src/app/layout/qgslayoutmapwidget.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ QgsLayoutMapWidget::QgsLayoutMapWidget( QgsLayoutItemMap *item )
7575
connect( mOverviewCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mOverviewCheckBox_toggled );
7676
connect( mOverviewListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged );
7777
connect( mOverviewListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_itemChanged );
78+
connect( mLabelSettingsButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::showLabelSettings );
79+
7880
setPanelTitle( tr( "Map Properties" ) );
7981
mMapRotationSpinBox->setClearValue( 0 );
8082

@@ -344,6 +346,12 @@ void QgsLayoutMapWidget::overviewSymbolChanged()
344346
mMapItem->update();
345347
}
346348

349+
void QgsLayoutMapWidget::showLabelSettings()
350+
{
351+
QgsLayoutMapLabelingWidget *w = new QgsLayoutMapLabelingWidget( mMapItem );
352+
openPanel( w );
353+
}
354+
347355
void QgsLayoutMapWidget::mAtlasCheckBox_toggled( bool checked )
348356
{
349357
if ( !mMapItem )
@@ -1635,3 +1643,53 @@ void QgsLayoutMapWidget::mOverviewCenterCheckbox_toggled( bool state )
16351643
mMapItem->update();
16361644
mMapItem->endCommand();
16371645
}
1646+
1647+
//
1648+
// QgsLayoutMapLabelingWidget
1649+
//
1650+
1651+
QgsLayoutMapLabelingWidget::QgsLayoutMapLabelingWidget( QgsLayoutItemMap *map )
1652+
: QgsLayoutItemBaseWidget( nullptr, map )
1653+
, mMapItem( map )
1654+
{
1655+
setupUi( this );
1656+
setPanelTitle( tr( "Label Settings" ) );
1657+
1658+
mLabelBoundarySpinBox->setClearValue( 0 );
1659+
mLabelBoundarySpinBox->setShowClearButton( true );
1660+
1661+
mLabelBoundaryUnitsCombo->linkToWidget( mLabelBoundarySpinBox );
1662+
mLabelBoundaryUnitsCombo->setConverter( &mMapItem->layout()->renderContext().measurementConverter() );
1663+
1664+
mLabelBoundarySpinBox->setValue( mMapItem->labelMargin().length() );
1665+
mLabelBoundaryUnitsCombo->setUnit( mMapItem->labelMargin().units() );
1666+
1667+
connect( mLabelBoundaryUnitsCombo, &QgsLayoutUnitsComboBox::changed, this, &QgsLayoutMapLabelingWidget::labelMarginUnitsChanged );
1668+
connect( mLabelBoundarySpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutMapLabelingWidget::labelMarginChanged );
1669+
1670+
registerDataDefinedButton( mLabelMarginDDBtn, QgsLayoutObject::MapLabelMargin );
1671+
1672+
updateDataDefinedButton( mLabelMarginDDBtn );
1673+
}
1674+
1675+
void QgsLayoutMapLabelingWidget::labelMarginChanged( double val )
1676+
{
1677+
if ( !mMapItem )
1678+
return;
1679+
1680+
mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin );
1681+
mMapItem->setLabelMargin( QgsLayoutMeasurement( val, mLabelBoundaryUnitsCombo->unit() ) );
1682+
mMapItem->layout()->undoStack()->endCommand();
1683+
mMapItem->invalidateCache();
1684+
}
1685+
1686+
void QgsLayoutMapLabelingWidget::labelMarginUnitsChanged()
1687+
{
1688+
if ( !mMapItem )
1689+
return;
1690+
1691+
mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin );
1692+
mMapItem->setLabelMargin( QgsLayoutMeasurement( mLabelBoundarySpinBox->value(), mLabelBoundaryUnitsCombo->unit() ) );
1693+
mMapItem->layout()->undoStack()->endCommand();
1694+
mMapItem->invalidateCache();
1695+
}

‎src/app/layout/qgslayoutmapwidget.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define QGSLAYOUTMAPWIDGET_H
2020

2121
#include "ui_qgslayoutmapwidgetbase.h"
22+
#include "ui_qgslayoutmaplabelingwidgetbase.h"
2223
#include "qgslayoutitemwidget.h"
2324
#include "qgslayoutitemmapgrid.h"
2425

@@ -116,6 +117,7 @@ class QgsLayoutMapWidget: public QgsLayoutItemBaseWidget, private Ui::QgsLayoutM
116117

117118
void mapCrsChanged( const QgsCoordinateReferenceSystem &crs );
118119
void overviewSymbolChanged();
120+
void showLabelSettings();
119121
private:
120122
QPointer< QgsLayoutItemMap > mMapItem;
121123
QgsLayoutItemPropertiesWidget *mItemPropertiesWidget = nullptr;
@@ -164,4 +166,24 @@ class QgsLayoutMapWidget: public QgsLayoutItemBaseWidget, private Ui::QgsLayoutM
164166

165167
};
166168

169+
170+
/**
171+
* \ingroup app
172+
* Allows configuration of layout map labeling settings.
173+
* */
174+
class QgsLayoutMapLabelingWidget: public QgsLayoutItemBaseWidget, private Ui::QgsLayoutMapLabelingWidgetBase
175+
{
176+
Q_OBJECT
177+
178+
public:
179+
explicit QgsLayoutMapLabelingWidget( QgsLayoutItemMap *map );
180+
181+
private slots:
182+
void labelMarginChanged( double val );
183+
void labelMarginUnitsChanged();
184+
185+
private:
186+
QPointer< QgsLayoutItemMap > mMapItem;
187+
};
188+
167189
#endif

‎src/core/layout/qgslayoutitemmap.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ bool QgsLayoutItemMap::readPropertiesFromElement( const QDomElement &itemElem, c
742742
mAtlasMargin = atlasElem.attribute( QStringLiteral( "margin" ), QStringLiteral( "0.1" ) ).toDouble();
743743
}
744744

745-
mLabelMargin = QgsLayoutMeasurement::decodeMeasurement( itemElem.attribute( QStringLiteral( "labelMargin" ), QStringLiteral( "0" ) ) );
745+
setLabelMargin( QgsLayoutMeasurement::decodeMeasurement( itemElem.attribute( QStringLiteral( "labelMargin" ), QStringLiteral( "0" ) ) ) );
746746

747747
updateBoundingRect();
748748

@@ -1131,11 +1131,11 @@ QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF
11311131
// override the default text render format inherited from the labeling engine settings using the layout's render context setting
11321132
jobMapSettings.setTextRenderFormat( mLayout->renderContext().textRenderFormat() );
11331133

1134-
if ( mLabelMargin.length() > 0 )
1134+
if ( mEvaluatedLabelMargin.length() > 0 )
11351135
{
11361136
QPolygonF visiblePoly = jobMapSettings.visiblePolygon();
11371137
visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
1138-
const double layoutLabelMargin = mLayout->convertToLayoutUnits( mLabelMargin );
1138+
const double layoutLabelMargin = mLayout->convertToLayoutUnits( mEvaluatedLabelMargin );
11391139
const double layoutLabelMarginInMapUnits = layoutLabelMargin / rect().width() * jobMapSettings.extent().width();
11401140
QgsGeometry mapBoundaryGeom = QgsGeometry::fromQPolygonF( visiblePoly );
11411141
mapBoundaryGeom = mapBoundaryGeom.buffer( -layoutLabelMarginInMapUnits, 0 );
@@ -1335,6 +1335,10 @@ void QgsLayoutItemMap::refreshDataDefinedProperty( const QgsLayoutObject::DataDe
13351335
emit extentChanged();
13361336
}
13371337
}
1338+
if ( property == QgsLayoutObject::MapLabelMargin || property == QgsLayoutObject::AllProperties )
1339+
{
1340+
refreshLabelMargin( false );
1341+
}
13381342

13391343
//force redraw
13401344
mCacheInvalidated = true;
@@ -1436,6 +1440,7 @@ QgsLayoutMeasurement QgsLayoutItemMap::labelMargin() const
14361440
void QgsLayoutItemMap::setLabelMargin( const QgsLayoutMeasurement &margin )
14371441
{
14381442
mLabelMargin = margin;
1443+
refreshLabelMargin( false );
14391444
}
14401445

14411446
void QgsLayoutItemMap::updateToolTip()
@@ -1900,6 +1905,19 @@ void QgsLayoutItemMap::refreshMapExtents( const QgsExpressionContext *context )
19001905
}
19011906
}
19021907

1908+
void QgsLayoutItemMap::refreshLabelMargin( bool updateItem )
1909+
{
1910+
//data defined label margin set?
1911+
double labelMargin = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::MapLabelMargin, createExpressionContext(), mLabelMargin.length() );
1912+
mEvaluatedLabelMargin.setLength( labelMargin );
1913+
mEvaluatedLabelMargin.setUnits( mLabelMargin.units() );
1914+
1915+
if ( updateItem )
1916+
{
1917+
update();
1918+
}
1919+
}
1920+
19031921
void QgsLayoutItemMap::updateAtlasFeature()
19041922
{
19051923
if ( !atlasDriven() || !mLayout->reportContext().layer() )

‎src/core/layout/qgslayoutitemmap.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
646646
bool mPainterCancelWait = false;
647647

648648
QgsLayoutMeasurement mLabelMargin{ 0 };
649+
QgsLayoutMeasurement mEvaluatedLabelMargin{ 0 };
649650

650651
void init();
651652

@@ -694,6 +695,8 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
694695
*/
695696
void refreshMapExtents( const QgsExpressionContext *context = nullptr );
696697

698+
void refreshLabelMargin( bool updateItem );
699+
697700
void updateAtlasFeature();
698701

699702
QgsRectangle computeAtlasRectangle();

‎src/core/layout/qgslayoutobject.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ void QgsLayoutObject::initPropertyDefinitions()
6363
{ QgsLayoutObject::MapYMin, QgsPropertyDefinition( "dataDefinedMapYMin", QObject::tr( "Extent minimum Y" ), QgsPropertyDefinition::Double ) },
6464
{ QgsLayoutObject::MapXMax, QgsPropertyDefinition( "dataDefinedMapXMax", QObject::tr( "Extent maximum X" ), QgsPropertyDefinition::Double ) },
6565
{ QgsLayoutObject::MapYMax, QgsPropertyDefinition( "dataDefinedMapYMax", QObject::tr( "Extent maximum Y" ), QgsPropertyDefinition::Double ) },
66+
{ QgsLayoutObject::MapLabelMargin, QgsPropertyDefinition( "dataDefinedMapLabelMargin", QObject::tr( "Label margin" ), QgsPropertyDefinition::DoublePositive ) },
6667
{ QgsLayoutObject::MapAtlasMargin, QgsPropertyDefinition( "dataDefinedMapAtlasMargin", QObject::tr( "Atlas margin" ), QgsPropertyDefinition::DoublePositive ) },
6768
{ QgsLayoutObject::MapLayers, QgsPropertyDefinition( "dataDefinedMapLayers", QgsPropertyDefinition::DataTypeString, QObject::tr( "Map Layers" ), tr( "list of map layer names separated by | characters" ) ) },
6869
{ QgsLayoutObject::MapStylePreset, QgsPropertyDefinition( "dataDefinedMapStylePreset", QgsPropertyDefinition::DataTypeString, QObject::tr( "Map theme" ), tr( "name of an existing map theme (case-sensitive)" ) ) },

‎src/core/layout/qgslayoutobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ class CORE_EXPORT QgsLayoutObject: public QObject, public QgsExpressionContextGe
163163
MapAtlasMargin, //!< Map atlas margin
164164
MapLayers, //!< Map layer set
165165
MapStylePreset, //!< Layer and style map theme
166+
MapLabelMargin, //!< Map label margin
166167
//composer picture
167168
PictureSource, //!< Picture source url
168169
PictureSvgBackgroundColor, //!< SVG background color
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>QgsLayoutMapLabelingWidgetBase</class>
4+
<widget class="QWidget" name="QgsLayoutMapLabelingWidgetBase">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>326</width>
10+
<height>424</height>
11+
</rect>
12+
</property>
13+
<property name="sizePolicy">
14+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
15+
<horstretch>0</horstretch>
16+
<verstretch>0</verstretch>
17+
</sizepolicy>
18+
</property>
19+
<property name="windowTitle">
20+
<string>Map Options</string>
21+
</property>
22+
<layout class="QVBoxLayout" name="verticalLayout">
23+
<property name="leftMargin">
24+
<number>0</number>
25+
</property>
26+
<property name="topMargin">
27+
<number>0</number>
28+
</property>
29+
<property name="rightMargin">
30+
<number>0</number>
31+
</property>
32+
<property name="bottomMargin">
33+
<number>0</number>
34+
</property>
35+
<item>
36+
<widget class="QgsCollapsibleGroupBoxBasic" name="qgsCollapsibleGroupBoxBasic">
37+
<property name="title">
38+
<string>Label Settings</string>
39+
</property>
40+
<layout class="QGridLayout" name="gridLayout_6" columnstretch="0,0,0,0">
41+
<item row="3" column="0" colspan="2">
42+
<widget class="QLabel" name="label_10">
43+
<property name="text">
44+
<string>of map edges</string>
45+
</property>
46+
<property name="wordWrap">
47+
<bool>true</bool>
48+
</property>
49+
</widget>
50+
</item>
51+
<item row="2" column="0" colspan="2">
52+
<widget class="QgsDoubleSpinBox" name="mLabelBoundarySpinBox">
53+
<property name="prefix">
54+
<string/>
55+
</property>
56+
<property name="minimum">
57+
<double>0.000000000000000</double>
58+
</property>
59+
<property name="maximum">
60+
<double>9999.000000000000000</double>
61+
</property>
62+
<property name="value">
63+
<double>0.000000000000000</double>
64+
</property>
65+
<property name="showClearButton" stdset="0">
66+
<bool>false</bool>
67+
</property>
68+
</widget>
69+
</item>
70+
<item row="1" column="0" colspan="2">
71+
<widget class="QLabel" name="label_8">
72+
<property name="text">
73+
<string>Avoid placing labels within</string>
74+
</property>
75+
<property name="wordWrap">
76+
<bool>true</bool>
77+
</property>
78+
</widget>
79+
</item>
80+
<item row="2" column="2">
81+
<widget class="QgsLayoutUnitsComboBox" name="mLabelBoundaryUnitsCombo"/>
82+
</item>
83+
<item row="2" column="3">
84+
<widget class="QgsPropertyOverrideButton" name="mLabelMarginDDBtn">
85+
<property name="text">
86+
<string>…</string>
87+
</property>
88+
</widget>
89+
</item>
90+
</layout>
91+
</widget>
92+
</item>
93+
<item>
94+
<spacer name="verticalSpacer">
95+
<property name="orientation">
96+
<enum>Qt::Vertical</enum>
97+
</property>
98+
<property name="sizeHint" stdset="0">
99+
<size>
100+
<width>20</width>
101+
<height>40</height>
102+
</size>
103+
</property>
104+
</spacer>
105+
</item>
106+
</layout>
107+
</widget>
108+
<layoutdefault spacing="6" margin="11"/>
109+
<customwidgets>
110+
<customwidget>
111+
<class>QgsCollapsibleGroupBoxBasic</class>
112+
<extends>QGroupBox</extends>
113+
<header>qgscollapsiblegroupbox.h</header>
114+
<container>1</container>
115+
</customwidget>
116+
<customwidget>
117+
<class>QgsDoubleSpinBox</class>
118+
<extends>QDoubleSpinBox</extends>
119+
<header>qgsdoublespinbox.h</header>
120+
</customwidget>
121+
<customwidget>
122+
<class>QgsPropertyOverrideButton</class>
123+
<extends>QToolButton</extends>
124+
<header>qgspropertyoverridebutton.h</header>
125+
</customwidget>
126+
<customwidget>
127+
<class>QgsLayoutUnitsComboBox</class>
128+
<extends>QComboBox</extends>
129+
<header>qgslayoutunitscombobox.h</header>
130+
</customwidget>
131+
</customwidgets>
132+
<resources/>
133+
<connections/>
134+
</ui>

0 commit comments

Comments
 (0)
Please sign in to comment.