Skip to content
Permalink
Browse files

[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
  • Loading branch information
nyalldawson committed Dec 11, 2018
1 parent 35855b8 commit ed25a3e2ee51e38ef5ab64f1753ed3e5f1d8c892
@@ -135,6 +135,7 @@ A base class for objects which belong to a layout.
MapAtlasMargin,
MapLayers,
MapStylePreset,
MapLabelMargin,
//composer picture
PictureSource,
PictureSvgBackgroundColor,
@@ -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();
}
@@ -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
@@ -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() )
@@ -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();
@@ -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)" ) ) },
@@ -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
@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsLayoutMapLabelingWidgetBase</class>
<widget class="QWidget" name="QgsLayoutMapLabelingWidgetBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>326</width>
<height>424</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Map Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QgsCollapsibleGroupBoxBasic" name="qgsCollapsibleGroupBoxBasic">
<property name="title">
<string>Label Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_6" columnstretch="0,0,0,0">
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="label_10">
<property name="text">
<string>of map edges</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QgsDoubleSpinBox" name="mLabelBoundarySpinBox">
<property name="prefix">
<string/>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>9999.000000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
<property name="showClearButton" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Avoid placing labels within</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QgsLayoutUnitsComboBox" name="mLabelBoundaryUnitsCombo"/>
</item>
<item row="2" column="3">
<widget class="QgsPropertyOverrideButton" name="mLabelMarginDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>QgsCollapsibleGroupBoxBasic</class>
<extends>QGroupBox</extends>
<header>qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
<customwidget>
<class>QgsPropertyOverrideButton</class>
<extends>QToolButton</extends>
<header>qgspropertyoverridebutton.h</header>
</customwidget>
<customwidget>
<class>QgsLayoutUnitsComboBox</class>
<extends>QComboBox</extends>
<header>qgslayoutunitscombobox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

0 comments on commit ed25a3e

Please sign in to comment.
You can’t perform that action at this time.