Skip to content

Commit

Permalink
[FEATURE][layouts] Add new setting to control whether map items
Browse files Browse the repository at this point in the history
should show partial labels

Layout map items no longer respect the default project setting
for "show partial labels", and instead have their own, per map
setting for this option. (Under the map item properties,
labeling settings button).

The map item setting always defaults to off (unlike the canvas
setting, which defaults to true for a new project) as layouts
should always default to the settings which produce the highest
quality cartographic outputs.

In general I suspect that most users would always want to avoid
rendering partial labels in layouts, but this setting was
previously so deeply hidden that most are unaware of how to
change it. (And previous discussion about changing the canvas
setting to hide partial labels deemed this default undesirable
for the canvas, where showing even a small part of a label
on the map border can help identify what sits just on/off
the edges of the map)
  • Loading branch information
nyalldawson committed Dec 18, 2018
1 parent 524bc79 commit 089a2f1
Show file tree
Hide file tree
Showing 15 changed files with 224 additions and 24 deletions.
28 changes: 28 additions & 0 deletions python/core/auto_generated/layout/qgslayoutitemmap.sip.in
Expand Up @@ -31,6 +31,13 @@ Layout graphical items for displaying a map.
Auto
};

enum MapItemFlag
{
ShowPartialLabels,
};
typedef QFlags<QgsLayoutItemMap::MapItemFlag> MapItemFlags;


explicit QgsLayoutItemMap( QgsLayout *layout );
%Docstring
Constructor for QgsLayoutItemMap, with the specified parent ``layout``.
Expand All @@ -44,6 +51,24 @@ Constructor for QgsLayoutItemMap, with the specified parent ``layout``.
virtual QgsLayoutItem::Flags itemFlags() const;


QgsLayoutItemMap::MapItemFlags mapFlags() const;
%Docstring
Returns the map item's flags, which control how the map content is drawn.

.. seealso:: :py:func:`setMapFlags`

.. versionadded:: 3.6
%End

void setMapFlags( QgsLayoutItemMap::MapItemFlags flags );
%Docstring
Sets the map item's ``flags``, which control how the map content is drawn.

.. seealso:: :py:func:`mapFlags`

.. versionadded:: 3.6
%End

void assignFreeId();
%Docstring
Sets the map id() to a number not yet used in the layout. The existing id() is kept if it is not in use.
Expand Down Expand Up @@ -583,6 +608,9 @@ Updates the bounding rect of this item. Call this function before doing any chan

};

QFlags<QgsLayoutItemMap::MapItemFlag> operator|(QgsLayoutItemMap::MapItemFlag f1, QFlags<QgsLayoutItemMap::MapItemFlag> f2);


/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
57 changes: 52 additions & 5 deletions src/app/layout/qgslayoutmapwidget.cpp
Expand Up @@ -186,6 +186,8 @@ bool QgsLayoutMapWidget::setNewItem( QgsLayoutItem *item )

mMapItem = qobject_cast< QgsLayoutItemMap * >( item );
mItemPropertiesWidget->setItem( mMapItem );
if ( mLabelWidget )
mLabelWidget->setItem( mMapItem );

if ( mMapItem )
{
Expand Down Expand Up @@ -359,8 +361,8 @@ void QgsLayoutMapWidget::overviewSymbolChanged()

void QgsLayoutMapWidget::showLabelSettings()
{
QgsLayoutMapLabelingWidget *w = new QgsLayoutMapLabelingWidget( mMapItem );
openPanel( w );
mLabelWidget = new QgsLayoutMapLabelingWidget( mMapItem );
openPanel( mLabelWidget );
}

void QgsLayoutMapWidget::switchToMoveContentTool()
Expand Down Expand Up @@ -1703,14 +1705,43 @@ QgsLayoutMapLabelingWidget::QgsLayoutMapLabelingWidget( QgsLayoutItemMap *map )
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 );
connect( mShowPartialLabelsCheckBox, &QCheckBox::toggled, this, &QgsLayoutMapLabelingWidget::showPartialsToggled );

registerDataDefinedButton( mLabelMarginDDBtn, QgsLayoutObject::MapLabelMargin );

setNewItem( map );
}

bool QgsLayoutMapLabelingWidget::setNewItem( QgsLayoutItem *item )
{
if ( item->type() != QgsLayoutItemRegistry::LayoutMap )
return false;

if ( mMapItem )
{
disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapLabelingWidget::updateGuiElements );
}

mMapItem = qobject_cast< QgsLayoutItemMap * >( item );

if ( mMapItem )
{
connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapLabelingWidget::updateGuiElements );
}

updateGuiElements();

return true;
}

void QgsLayoutMapLabelingWidget::updateGuiElements()
{
whileBlocking( mLabelBoundarySpinBox )->setValue( mMapItem->labelMargin().length() );
whileBlocking( mLabelBoundaryUnitsCombo )->setUnit( mMapItem->labelMargin().units() );
whileBlocking( mShowPartialLabelsCheckBox )->setChecked( mMapItem->mapFlags() & QgsLayoutItemMap::ShowPartialLabels );

updateDataDefinedButton( mLabelMarginDDBtn );
}

Expand All @@ -1735,3 +1766,19 @@ void QgsLayoutMapLabelingWidget::labelMarginUnitsChanged()
mMapItem->layout()->undoStack()->endCommand();
mMapItem->invalidateCache();
}

void QgsLayoutMapLabelingWidget::showPartialsToggled( bool checked )
{
if ( !mMapItem )
return;

mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Visibility" ) );
QgsLayoutItemMap::MapItemFlags flags = mMapItem->mapFlags();
if ( checked )
flags |= QgsLayoutItemMap::ShowPartialLabels;
else
flags &= ~QgsLayoutItemMap::ShowPartialLabels;
mMapItem->setMapFlags( flags );
mMapItem->layout()->undoStack()->endCommand();
mMapItem->invalidateCache();
}
7 changes: 7 additions & 0 deletions src/app/layout/qgslayoutmapwidget.h
Expand Up @@ -26,6 +26,7 @@
class QgsMapLayer;
class QgsLayoutItemMap;
class QgsLayoutItemMapOverview;
class QgsLayoutMapLabelingWidget;

/**
* \ingroup app
Expand Down Expand Up @@ -127,6 +128,7 @@ class QgsLayoutMapWidget: public QgsLayoutItemBaseWidget, private Ui::QgsLayoutM
QPointer< QgsLayoutItemMap > mMapItem;
QgsLayoutItemPropertiesWidget *mItemPropertiesWidget = nullptr;
QgsLayoutDesignerInterface *mInterface = nullptr;
QPointer< QgsLayoutMapLabelingWidget > mLabelWidget;

//! Sets extent of composer map from line edits
void updateComposerExtentFromGui();
Expand Down Expand Up @@ -184,9 +186,14 @@ class QgsLayoutMapLabelingWidget: public QgsLayoutItemBaseWidget, private Ui::Qg
public:
explicit QgsLayoutMapLabelingWidget( QgsLayoutItemMap *map );

protected:
bool setNewItem( QgsLayoutItem *item ) override;

private slots:
void updateGuiElements();
void labelMarginChanged( double val );
void labelMarginUnitsChanged();
void showPartialsToggled( bool checked );

private:
QPointer< QgsLayoutItemMap > mMapItem;
Expand Down
20 changes: 19 additions & 1 deletion src/core/layout/qgslayoutitemmap.cpp
Expand Up @@ -608,6 +608,7 @@ bool QgsLayoutItemMap::writePropertiesToElement( QDomElement &mapElem, QDomDocum
mapElem.appendChild( atlasElem );

mapElem.setAttribute( QStringLiteral( "labelMargin" ), mLabelMargin.encodeMeasurement() );
mapElem.setAttribute( QStringLiteral( "mapFlags" ), static_cast< int>( mMapFlags ) );

return true;
}
Expand Down Expand Up @@ -744,6 +745,8 @@ bool QgsLayoutItemMap::readPropertiesFromElement( const QDomElement &itemElem, c

setLabelMargin( QgsLayoutMeasurement::decodeMeasurement( itemElem.attribute( QStringLiteral( "labelMargin" ), QStringLiteral( "0" ) ) ) );

mMapFlags = static_cast< MapItemFlags>( itemElem.attribute( QStringLiteral( "mapFlags" ), nullptr ).toInt() );

updateBoundingRect();

mUpdatesEnabled = true;
Expand Down Expand Up @@ -1070,6 +1073,16 @@ void QgsLayoutItemMap::recreateCachedImageInBackground()
mDrawingPreview = false;
}

QgsLayoutItemMap::MapItemFlags QgsLayoutItemMap::mapFlags() const
{
return mMapFlags;
}

void QgsLayoutItemMap::setMapFlags( QgsLayoutItemMap::MapItemFlags mapFlags )
{
mMapFlags = mapFlags;
}

QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF size, double dpi, bool includeLayerSettings ) const
{
QgsExpressionContext expressionContext = createExpressionContext();
Expand Down Expand Up @@ -1127,7 +1140,12 @@ QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF
jobMapSettings.setTransformContext( mLayout->project()->transformContext() );
jobMapSettings.setPathResolver( mLayout->project()->pathResolver() );

jobMapSettings.setLabelingEngineSettings( mLayout->project()->labelingEngineSettings() );
QgsLabelingEngineSettings labelSettings = mLayout->project()->labelingEngineSettings();

// override project "show partial labels" setting with this map's setting
labelSettings.setFlag( QgsLabelingEngineSettings::UsePartialCandidates, mMapFlags & ShowPartialLabels );
jobMapSettings.setLabelingEngineSettings( labelSettings );

// override the default text render format inherited from the labeling engine settings using the layout's render context setting
jobMapSettings.setTextRenderFormat( mLayout->renderContext().textRenderFormat() );

Expand Down
27 changes: 27 additions & 0 deletions src/core/layout/qgslayoutitemmap.h
Expand Up @@ -63,6 +63,16 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
Auto
};

/**
* Various flags that affect drawing of map items.
* \since QGIS 3.6
*/
enum MapItemFlag
{
ShowPartialLabels = 1 << 0, //!< Whether to draw labels which are partially outside of the map view
};
Q_DECLARE_FLAGS( MapItemFlags, MapItemFlag )

/**
* Constructor for QgsLayoutItemMap, with the specified parent \a layout.
*/
Expand All @@ -73,6 +83,20 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
QIcon icon() const override;
QgsLayoutItem::Flags itemFlags() const override;

/**
* Returns the map item's flags, which control how the map content is drawn.
* \see setMapFlags()
* \since QGIS 3.6
*/
QgsLayoutItemMap::MapItemFlags mapFlags() const;

/**
* Sets the map item's \a flags, which control how the map content is drawn.
* \see mapFlags()
* \since QGIS 3.6
*/
void setMapFlags( QgsLayoutItemMap::MapItemFlags flags );

/**
* Sets the map id() to a number not yet used in the layout. The existing id() is kept if it is not in use.
*/
Expand Down Expand Up @@ -521,6 +545,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem

private:

QgsLayoutItemMap::MapItemFlags mMapFlags = nullptr;

//! Unique identifier
int mMapId = 1;
Expand Down Expand Up @@ -709,4 +734,6 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem

};

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLayoutItemMap::MapItemFlags )

#endif //QGSLAYOUTITEMMAP_H
7 changes: 7 additions & 0 deletions src/ui/layout/qgslayoutmaplabelingwidgetbase.ui
Expand Up @@ -90,6 +90,13 @@
</property>
</widget>
</item>
<item row="3" column="0" colspan="4">
<widget class="QCheckBox" name="mShowPartialLabelsCheckBox">
<property name="text">
<string>Allow truncated labels on edges of map</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
Expand Down
63 changes: 63 additions & 0 deletions tests/src/python/test_qgslayoutmap.py
Expand Up @@ -339,6 +339,69 @@ def testLabelMargin(self):
self.report += checker.report()
self.assertTrue(result, message)

def testPartialLabels(self):
"""
Test rendering map item with a show partial labels flag
"""
format = QgsTextFormat()
format.setFont(QgsFontUtils.getStandardTestFont("Bold"))
format.setSize(20)
format.setNamedStyle("Bold")
format.setColor(QColor(0, 0, 0))
settings = QgsPalLayerSettings()
settings.setFormat(format)
settings.fieldName = "'X'"
settings.isExpression = True
settings.placement = QgsPalLayerSettings.OverPoint

vl = QgsVectorLayer("Point?crs=epsg:4326&field=id:integer", "vl", "memory")
vl.setRenderer(QgsNullSymbolRenderer())
f = QgsFeature(vl.fields(), 1)
for x in range(15):
for y in range(15):
f.setGeometry(QgsPoint(x, y))
vl.dataProvider().addFeature(f)

vl.setLabeling(QgsVectorLayerSimpleLabeling(settings))
vl.setLabelsEnabled(True)

p = QgsProject()

engine_settings = QgsLabelingEngineSettings()
engine_settings.setFlag(QgsLabelingEngineSettings.UsePartialCandidates, False)
engine_settings.setFlag(QgsLabelingEngineSettings.DrawLabelRectOnly, True)
p.setLabelingEngineSettings(engine_settings)

p.addMapLayer(vl)
layout = QgsLayout(p)
layout.initializeDefaults()
p.setCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
map = QgsLayoutItemMap(layout)
map.attemptSetSceneRect(QRectF(10, 10, 180, 180))
map.setFrameEnabled(True)
map.zoomToExtent(vl.extent())
map.setLayers([vl])
layout.addLayoutItem(map)

# default should always be to hide partial labels
self.assertFalse(map.mapFlags() & QgsLayoutItemMap.ShowPartialLabels)

# hiding partial labels (the default)
map.setMapFlags(QgsLayoutItemMap.MapItemFlags())
checker = QgsLayoutChecker('composermap_label_nomargin', layout)
checker.setControlPathPrefix("composer_map")
result, message = checker.testLayout()
self.report += checker.report()
self.assertTrue(result, message)

# showing partial labels
map.setMapFlags(QgsLayoutItemMap.ShowPartialLabels)
checker = QgsLayoutChecker('composermap_show_partial_labels', layout)
checker.setControlPathPrefix("composer_map")
result, message = checker.testLayout()
self.report += checker.report()
self.assertTrue(result, message)


if __name__ == '__main__':
unittest.main()
5 changes: 4 additions & 1 deletion tests/src/python/test_qgspallabeling_layout.py
Expand Up @@ -35,7 +35,8 @@
QgsLayoutExporter,
QgsMapSettings,
QgsProject,
QgsVectorLayerSimpleLabeling)
QgsVectorLayerSimpleLabeling,
QgsLabelingEngineSettings)


from utilities import (
Expand Down Expand Up @@ -132,6 +133,8 @@ def _set_up_composition(self, width, height, dpi, engine_settings):
""":type: QgsLayoutItemMap"""
self._cmap.setFrameEnabled(False)
self._cmap.setLayers(self._TestMapSettings.layers())
if self._TestMapSettings.labelingEngineSettings().flags() & QgsLabelingEngineSettings.UsePartialCandidates:
self._cmap.setMapFlags(QgsLayoutItemMap.ShowPartialLabels)
self._c.addLayoutItem(self._cmap)
# now expand map to fill page and set its extent
self._cmap.attemptSetSceneRect(QRectF(0, 0, paperw, paperw))
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions tests/testdata/qgis_server/test_project.qgs
Expand Up @@ -1130,7 +1130,7 @@
</LayoutItem>
<GuideCollection visible="1"/>
</PageCollection>
<LayoutItem type="65639" followPreset="false" followPresetName="" positionLock="false" visibility="1" itemRotation="0" positionOnPage="126,143,mm" blendMode="0" zValue="2" frameJoinStyle="miter" frame="false" background="true" excludeFromExports="0" size="61,26,mm" outlineWidthM="0.3,mm" uuid="{5ed7a90f-8af2-4535-a15e-e18a7f6c5d1f}" groupUuid="" drawCanvasItems="true" referencePoint="0" id="" keepLayerSet="false" templateUuid="{5ed7a90f-8af2-4535-a15e-e18a7f6c5d1f}" position="126,143,mm" mapRotation="0" opacity="1">
<LayoutItem type="65639" followPreset="false" followPresetName="" positionLock="false" visibility="1" itemRotation="0" positionOnPage="126,143,mm" blendMode="0" zValue="2" frameJoinStyle="miter" frame="false" background="true" excludeFromExports="0" size="61,26,mm" outlineWidthM="0.3,mm" uuid="{5ed7a90f-8af2-4535-a15e-e18a7f6c5d1f}" groupUuid="" drawCanvasItems="true" referencePoint="0" id="" keepLayerSet="false" templateUuid="{5ed7a90f-8af2-4535-a15e-e18a7f6c5d1f}" position="126,143,mm" mapRotation="0" mapFlags="1" opacity="1">
<FrameColor blue="0" green="0" red="0" alpha="255"/>
<BackgroundColor blue="255" green="255" red="255" alpha="255"/>
<LayoutObject>
Expand Down Expand Up @@ -1209,7 +1209,7 @@
</ComposerMapGrid>
<AtlasMap margin="0.10000000000000001" atlasDriven="0" scalingMode="2"/>
</LayoutItem>
<LayoutItem type="65639" followPreset="false" followPresetName="" positionLock="false" visibility="1" itemRotation="0" positionOnPage="98.7716,20.1872,mm" blendMode="0" zValue="1" frameJoinStyle="miter" frame="false" background="true" excludeFromExports="0" size="87,103,mm" outlineWidthM="0.3,mm" uuid="{8fec18d6-8ba0-47d6-914e-3daffe8a8633}" groupUuid="" drawCanvasItems="true" referencePoint="0" id="" keepLayerSet="false" templateUuid="{8fec18d6-8ba0-47d6-914e-3daffe8a8633}" position="98.7716,20.1872,mm" mapRotation="0" opacity="1">
<LayoutItem type="65639" followPreset="false" followPresetName="" positionLock="false" visibility="1" itemRotation="0" positionOnPage="98.7716,20.1872,mm" blendMode="0" zValue="1" frameJoinStyle="miter" frame="false" background="true" excludeFromExports="0" size="87,103,mm" outlineWidthM="0.3,mm" uuid="{8fec18d6-8ba0-47d6-914e-3daffe8a8633}" groupUuid="" drawCanvasItems="true" referencePoint="0" id="" keepLayerSet="false" templateUuid="{8fec18d6-8ba0-47d6-914e-3daffe8a8633}" position="98.7716,20.1872,mm" mapRotation="0" mapFlags="1" opacity="1">
<FrameColor blue="0" green="0" red="0" alpha="255"/>
<BackgroundColor blue="255" green="255" red="255" alpha="255"/>
<LayoutObject>
Expand Down
Expand Up @@ -2921,7 +2921,7 @@ def my_form_open(dialog, layer, feature):
<LabelFont style="" description="DejaVu Sans,10,-1,5,50,0,0,0,0,0"/>
<FontColor red="0" green="0" blue="0"/>
</LayoutItem>
<LayoutItem itemRotation="0" referencePoint="0" drawCanvasItems="true" followPreset="false" position="21.3911,46,mm" positionOnPage="21.3911,46,mm" followPresetName="" frame="false" uuid="{e2138dff-8012-45c7-9a52-a543ca96ac60}" keepLayerSet="false" background="true" positionLock="false" blendMode="0" size="242,104,mm" opacity="1" outlineWidthM="0.3,mm" groupUuid="" zValue="3" excludeFromExports="0" type="65639" templateUuid="{e2138dff-8012-45c7-9a52-a543ca96ac60}" frameJoinStyle="miter" visibility="1" mapRotation="0" id="">
<LayoutItem itemRotation="0" referencePoint="0" drawCanvasItems="true" followPreset="false" position="21.3911,46,mm" positionOnPage="21.3911,46,mm" followPresetName="" frame="false" uuid="{e2138dff-8012-45c7-9a52-a543ca96ac60}" keepLayerSet="false" background="true" positionLock="false" blendMode="0" size="242,104,mm" opacity="1" outlineWidthM="0.3,mm" groupUuid="" zValue="3" excludeFromExports="0" type="65639" templateUuid="{e2138dff-8012-45c7-9a52-a543ca96ac60}" frameJoinStyle="miter" visibility="1" mapRotation="0" mapFlags="1" id="">
<FrameColor alpha="255" red="0" green="0" blue="0"/>
<BackgroundColor alpha="255" red="255" green="255" blue="255"/>
<LayoutObject>
Expand Down

0 comments on commit 089a2f1

Please sign in to comment.