Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[layouts] Keep a separate flag for whether only a subset of layers
are to be clipped from the project, instead of just tracking this
by the presence of any checked layers

Avoids inconsistencies between the layers which are visibly clipped
on the map vs the options which are set in the GUI.
  • Loading branch information
nyalldawson committed Sep 17, 2020
1 parent fce4b27 commit 49e9b61
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 11 deletions.
30 changes: 28 additions & 2 deletions python/core/auto_generated/layout/qgslayoutitemmap.sip.in
Expand Up @@ -67,13 +67,35 @@ Returns ``True`` if labels should only be placed inside the atlas feature geomet
Sets whether labels should only be placed inside the atlas feature geometry.

.. seealso:: :py:func:`forceLabelsInsideFeature`
%End

bool restrictToLayers() const;
%Docstring
Returns ``True`` if clipping should be restricted to a subset of layers.

.. seealso:: :py:func:`layersToClip`

.. seealso:: :py:func:`setRestrictToLayers`
%End

void setRestrictToLayers( bool enabled );
%Docstring
Sets whether clipping should be restricted to a subset of layers.

.. seealso:: :py:func:`setLayersToClip`

.. seealso:: :py:func:`restrictToLayers`
%End

QList< QgsMapLayer * > layersToClip() const;
%Docstring
Returns the list of map layers to clip to the atlas feature.

If the returned list is empty then all layers will be clipped.
.. note::

This setting is only used if :py:func:`~QgsLayoutItemMapAtlasClippingSettings.restrictToLayers` is ``True``.

.. seealso:: :py:func:`restrictedLayers`

.. seealso:: :py:func:`setLayersToClip`
%End
Expand All @@ -82,7 +104,11 @@ If the returned list is empty then all layers will be clipped.
%Docstring
Sets the list of map ``layers`` to clip to the atlas feature.

If the ``layers`` list is empty then all layers will be clipped.
.. note::

This setting is only used if :py:func:`~QgsLayoutItemMapAtlasClippingSettings.restrictToLayers` is ``True``.

.. seealso:: :py:func:`restrictedLayers`

.. seealso:: :py:func:`layersToClip`
%End
Expand Down
31 changes: 30 additions & 1 deletion python/core/auto_generated/qgsmapclippingregion.sip.in
Expand Up @@ -64,6 +64,24 @@ Sets the feature clipping ``type``.
This setting is only used while rendering vector layers, for other layer types it is ignored.

.. seealso:: :py:func:`featureClip`
%End

bool restrictToLayers() const;
%Docstring
Returns ``True`` if clipping should be restricted to a subset of layers.

.. seealso:: :py:func:`restrictedLayers`

.. seealso:: :py:func:`setRestrictToLayers`
%End

void setRestrictToLayers( bool enabled );
%Docstring
Sets whether clipping should be restricted to a subset of layers.

.. seealso:: :py:func:`setRestrictedLayers`

.. seealso:: :py:func:`restrictToLayers`
%End

void setRestrictedLayers( const QList< QgsMapLayer * > &layers );
Expand All @@ -72,17 +90,28 @@ Sets a list of ``layers`` to restrict the clipping region effects to.

By default the clipping region applies to all layers.

.. note::

This setting is only used if :py:func:`~QgsMapClippingRegion.restrictToLayers` is ``True``.

.. seealso:: :py:func:`restrictedLayers`
%End

.. seealso:: :py:func:`setRestrictToLayers`
%End

QList< QgsMapLayer * > restrictedLayers() const;
%Docstring
Returns the list of layers to restrict the clipping region effects to.

If the list is empty then the clipping will be applied to all layers.

.. note::

This setting is only used if :py:func:`~QgsMapClippingRegion.restrictToLayers` is ``True``.

.. seealso:: :py:func:`setRestrictedLayers`

.. seealso:: :py:func:`restrictToLayers`
%End

bool appliesToLayer( const QgsMapLayer *layer ) const;
Expand Down
17 changes: 17 additions & 0 deletions src/core/layout/qgslayoutitemmap.cpp
Expand Up @@ -1554,6 +1554,7 @@ QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF
QgsMapClippingRegion region( clipGeom );
region.setFeatureClip( mAtlasClippingSettings->featureClippingType() );
region.setRestrictedLayers( mAtlasClippingSettings->layersToClip() );
region.setRestrictToLayers( mAtlasClippingSettings->restrictToLayers() );
jobMapSettings.addClippingRegion( region );

if ( mAtlasClippingSettings->forceLabelsInsideFeature() )
Expand Down Expand Up @@ -2784,6 +2785,20 @@ void QgsLayoutItemMapAtlasClippingSettings::setForceLabelsInsideFeature( bool fo
emit changed();
}

bool QgsLayoutItemMapAtlasClippingSettings::restrictToLayers() const
{
return mRestrictToLayers;
}

void QgsLayoutItemMapAtlasClippingSettings::setRestrictToLayers( bool enabled )
{
if ( mRestrictToLayers == enabled )
return;

mRestrictToLayers = enabled;
emit changed();
}

QList<QgsMapLayer *> QgsLayoutItemMapAtlasClippingSettings::layersToClip() const
{
return _qgis_listRefToRaw( mLayersToClip );
Expand All @@ -2801,6 +2816,7 @@ bool QgsLayoutItemMapAtlasClippingSettings::writeXml( QDomElement &element, QDom
settingsElem.setAttribute( QStringLiteral( "enabled" ), mClipToAtlasFeature ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
settingsElem.setAttribute( QStringLiteral( "forceLabelsInside" ), mForceLabelsInsideFeature ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
settingsElem.setAttribute( QStringLiteral( "clippingType" ), QString::number( static_cast<int>( mFeatureClippingType ) ) );
settingsElem.setAttribute( QStringLiteral( "restrictLayers" ), mRestrictToLayers ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );

//layer set
QDomElement layerSetElem = document.createElement( QStringLiteral( "layersToClip" ) );
Expand Down Expand Up @@ -2831,6 +2847,7 @@ bool QgsLayoutItemMapAtlasClippingSettings::readXml( const QDomElement &element,
mClipToAtlasFeature = settingsElem.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt();
mForceLabelsInsideFeature = settingsElem.attribute( QStringLiteral( "forceLabelsInside" ), QStringLiteral( "0" ) ).toInt();
mFeatureClippingType = static_cast< QgsMapClippingRegion::FeatureClippingType >( settingsElem.attribute( QStringLiteral( "clippingType" ), QStringLiteral( "0" ) ).toInt() );
mRestrictToLayers = settingsElem.attribute( QStringLiteral( "restrictLayers" ), QStringLiteral( "0" ) ).toInt();

mLayersToClip.clear();
QDomNodeList layerSetNodeList = settingsElem.elementsByTagName( QStringLiteral( "layersToClip" ) );
Expand Down
23 changes: 21 additions & 2 deletions src/core/layout/qgslayoutitemmap.h
Expand Up @@ -89,20 +89,38 @@ class CORE_EXPORT QgsLayoutItemMapAtlasClippingSettings : public QObject
*/
void setForceLabelsInsideFeature( bool forceInside );

/**
* Returns TRUE if clipping should be restricted to a subset of layers.
*
* \see layersToClip()
* \see setRestrictToLayers()
*/
bool restrictToLayers() const;

/**
* Sets whether clipping should be restricted to a subset of layers.
*
* \see setLayersToClip()
* \see restrictToLayers()
*/
void setRestrictToLayers( bool enabled );

/**
* Returns the list of map layers to clip to the atlas feature.
*
* If the returned list is empty then all layers will be clipped.
* \note This setting is only used if restrictToLayers() is TRUE.
*
* \see restrictedLayers()
* \see setLayersToClip()
*/
QList< QgsMapLayer * > layersToClip() const;

/**
* Sets the list of map \a layers to clip to the atlas feature.
*
* If the \a layers list is empty then all layers will be clipped.
* \note This setting is only used if restrictToLayers() is TRUE.
*
* \see restrictedLayers()
* \see layersToClip()
*/
void setLayersToClip( const QList< QgsMapLayer * > &layers );
Expand Down Expand Up @@ -135,6 +153,7 @@ class CORE_EXPORT QgsLayoutItemMapAtlasClippingSettings : public QObject

QgsLayoutItemMap *mMap = nullptr;
bool mClipToAtlasFeature = false;
bool mRestrictToLayers = false;
QList< QgsMapLayerRef > mLayersToClip;
QgsMapClippingRegion::FeatureClippingType mFeatureClippingType = QgsMapClippingRegion::FeatureClippingType::ClipPainterOnly;
bool mForceLabelsInsideFeature = false;
Expand Down
15 changes: 14 additions & 1 deletion src/core/qgsmapclippingregion.cpp
Expand Up @@ -39,14 +39,27 @@ QList<QgsMapLayer *> QgsMapClippingRegion::restrictedLayers() const

bool QgsMapClippingRegion::appliesToLayer( const QgsMapLayer *layer ) const
{
if ( mRestrictToLayersList.empty() )
if ( !mRestrictToLayers )
return true;

if ( mRestrictToLayersList.empty() )
return false;

auto it = std::find_if( mRestrictToLayersList.begin(), mRestrictToLayersList.end(), [layer]( const QgsWeakMapLayerPointer & item ) -> bool
{
return item == layer;
} );
return it != mRestrictToLayersList.end();
}

bool QgsMapClippingRegion::restrictToLayers() const
{
return mRestrictToLayers;
}

void QgsMapClippingRegion::setRestrictToLayers( bool enabled )
{
mRestrictToLayers = enabled;
}


24 changes: 23 additions & 1 deletion src/core/qgsmapclippingregion.h
Expand Up @@ -88,22 +88,43 @@ class CORE_EXPORT QgsMapClippingRegion
mFeatureClip = type;
}

/**
* Returns TRUE if clipping should be restricted to a subset of layers.
*
* \see restrictedLayers()
* \see setRestrictToLayers()
*/
bool restrictToLayers() const;

/**
* Sets whether clipping should be restricted to a subset of layers.
*
* \see setRestrictedLayers()
* \see restrictToLayers()
*/
void setRestrictToLayers( bool enabled );

/**
* Sets a list of \a layers to restrict the clipping region effects to.
*
* By default the clipping region applies to all layers.
*
* \note This setting is only used if restrictToLayers() is TRUE.
*
* \see restrictedLayers()
* \see setRestrictToLayers()
*/
void setRestrictedLayers( const QList< QgsMapLayer * > &layers );


/**
* Returns the list of layers to restrict the clipping region effects to.
*
* If the list is empty then the clipping will be applied to all layers.
*
* \note This setting is only used if restrictToLayers() is TRUE.
*
* \see setRestrictedLayers()
* \see restrictToLayers()
*/
QList< QgsMapLayer * > restrictedLayers() const;

Expand All @@ -117,6 +138,7 @@ class CORE_EXPORT QgsMapClippingRegion
//! Geometry of clipping region (in destination map coordinates and CRS)
QgsGeometry mGeometry;

bool mRestrictToLayers = false;
QgsWeakMapLayerPointerList mRestrictToLayersList;

FeatureClippingType mFeatureClip = FeatureClippingType::ClipToIntersection;
Expand Down
8 changes: 4 additions & 4 deletions src/gui/layout/qgslayoutmapwidget.cpp
Expand Up @@ -2016,7 +2016,7 @@ QgsLayoutMapClippingWidget::QgsLayoutMapClippingWidget( QgsLayoutItemMap *map )
{
mBlockUpdates = true;
mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
mMapItem->atlasClippingSettings()->setLayersToClip( mLayerModel->layersChecked( ) );
mMapItem->atlasClippingSettings()->setRestrictToLayers( true );
mMapItem->endCommand();
mBlockUpdates = false;
}
Expand All @@ -2027,7 +2027,7 @@ QgsLayoutMapClippingWidget::QgsLayoutMapClippingWidget( QgsLayoutItemMap *map )
{
mBlockUpdates = true;
mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
mMapItem->atlasClippingSettings()->setLayersToClip( QList< QgsMapLayer * >() );
mMapItem->atlasClippingSettings()->setRestrictToLayers( false );
mMapItem->endCommand();
mBlockUpdates = false;
}
Expand Down Expand Up @@ -2136,8 +2136,8 @@ void QgsLayoutMapClippingWidget::updateGuiElements()
mAtlasClippingTypeComboBox->setCurrentIndex( mAtlasClippingTypeComboBox->findData( static_cast< int >( mMapItem->atlasClippingSettings()->featureClippingType() ) ) );
mForceLabelsInsideCheckBox->setChecked( mMapItem->atlasClippingSettings()->forceLabelsInsideFeature() );

mRadioClipAllLayers->setChecked( mMapItem->atlasClippingSettings()->layersToClip().isEmpty() );
mRadioClipSelectedLayers->setChecked( !mMapItem->atlasClippingSettings()->layersToClip().isEmpty() );
mRadioClipAllLayers->setChecked( !mMapItem->atlasClippingSettings()->restrictToLayers() );
mRadioClipSelectedLayers->setChecked( mMapItem->atlasClippingSettings()->restrictToLayers() );
mLayerModel->setLayersChecked( mMapItem->atlasClippingSettings()->layersToClip() );

mClipToItemCheckBox->setChecked( mMapItem->itemClippingSettings()->enabled() );
Expand Down
10 changes: 10 additions & 0 deletions tests/src/python/test_qgslayoutatlasclippingsettings.py
Expand Up @@ -74,6 +74,14 @@ def testSettings(self):
p.removeMapLayer(l1.id())
self.assertCountEqual(settings.layersToClip(), [l2])

settings.setRestrictToLayers(False)
self.assertFalse(settings.restrictToLayers())
self.assertEqual(len(spy), 4)

settings.setRestrictToLayers(True)
self.assertTrue(settings.restrictToLayers())
self.assertEqual(len(spy), 5)

def testSaveRestore(self):
p = QgsProject()
l1 = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
Expand All @@ -89,6 +97,7 @@ def testSaveRestore(self):
settings.setEnabled(True)
settings.setFeatureClippingType(QgsMapClippingRegion.FeatureClippingType.NoClipping)
settings.setForceLabelsInsideFeature(True)
settings.setRestrictToLayers(True)
settings.setLayersToClip([l2])

# save map to xml
Expand All @@ -107,6 +116,7 @@ def testSaveRestore(self):
self.assertEqual(map2.atlasClippingSettings().featureClippingType(), QgsMapClippingRegion.FeatureClippingType.NoClipping)
self.assertTrue(map2.atlasClippingSettings().forceLabelsInsideFeature())
self.assertEqual(map2.atlasClippingSettings().layersToClip(), [l2])
self.assertTrue(map2.atlasClippingSettings().restrictToLayers())


if __name__ == '__main__':
Expand Down
14 changes: 14 additions & 0 deletions tests/src/python/test_qgsmapclippingregion.py
Expand Up @@ -40,6 +40,10 @@ def testGetSet(self):
self.assertEqual(len(region.restrictedLayers()), 0)
region.setRestrictedLayers([layer, layer2])
self.assertCountEqual(region.restrictedLayers(), [layer, layer2])
region.setRestrictToLayers(False)
self.assertFalse(region.restrictToLayers())
region.setRestrictToLayers(True)
self.assertTrue(region.restrictToLayers())

def testAppliesToLayer(self):
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
Expand All @@ -58,6 +62,16 @@ def testAppliesToLayer(self):
region.setRestrictedLayers([layer, layer2])
self.assertTrue(region.appliesToLayer(layer))
self.assertTrue(region.appliesToLayer(layer2))
self.assertTrue(region.appliesToLayer(layer3))

region.setRestrictToLayers(True)
self.assertTrue(region.appliesToLayer(layer))
self.assertTrue(region.appliesToLayer(layer2))
self.assertFalse(region.appliesToLayer(layer3))

region.setRestrictedLayers([])
self.assertFalse(region.appliesToLayer(layer))
self.assertFalse(region.appliesToLayer(layer2))
self.assertFalse(region.appliesToLayer(layer3))


Expand Down
1 change: 1 addition & 0 deletions tests/src/python/test_qgsmapclippingutils.py
Expand Up @@ -43,6 +43,7 @@ def testClippingRegionsForLayer(self):
region = QgsMapClippingRegion(QgsGeometry.fromWkt('Polygon((0 0, 1 0, 1 1, 0 1, 0 0))'))
region2 = QgsMapClippingRegion(QgsGeometry.fromWkt('Polygon((0 0, 0.1 0, 0.1 2, 0 2, 0 0))'))
region2.setRestrictedLayers([layer])
region2.setRestrictToLayers(True)
ms = QgsMapSettings()
ms.addClippingRegion(region)
ms.addClippingRegion(region2)
Expand Down

0 comments on commit 49e9b61

Please sign in to comment.