mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-27 00:33:48 -05:00
[feature][layouts] Add automatic clipping settings for atlas maps
This feature allows users to enable map clipping for layout map items so that the maps are clipped to the boundary of the current atlas feature. (It's available for polygon atlas coverage layers only, for obvious reasons!) Options exist for: - Enabling or disabling the clipping on a per-map basis - Specifying the clipping type: - "Clip During Render Only": applies a painter based clip, so that portions of vector features which sit outside the atlas feature become invisible - "Clip Feature Before Render": applies the clip before rendering features, so borders of features which fall partially outside the atlas feature will still be visible on the boundary of the atlas feature - "Render Intersecting Features Unchanged": just renders all features which intersect the current atlas feature, but without clipping their geometry - Controlling whether labels should be forced placed inside the atlas feature, or whether they may be placed outside the feature - Restricting the clip to a subset of the layers in the project, so that only some are clipped Sponsored by City of Canning
This commit is contained in:
parent
329a0fc110
commit
c30c769ef5
@ -9,6 +9,110 @@
|
||||
|
||||
|
||||
|
||||
class QgsLayoutItemMapAtlasClippingSettings : QObject
|
||||
{
|
||||
%Docstring
|
||||
Contains settings relating to clipping a layout map by the current atlas feature.
|
||||
|
||||
.. versionadded:: 3.16
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslayoutitemmap.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsLayoutItemMapAtlasClippingSettings( QgsLayoutItemMap *map /TransferThis/ = 0 );
|
||||
%Docstring
|
||||
Constructor for QgsLayoutItemMapAtlasClippingSettings, with the specified ``map`` parent.
|
||||
%End
|
||||
|
||||
bool enabled() const;
|
||||
%Docstring
|
||||
Returns ``True`` if the map content should be clipped to the current atlas feature.
|
||||
|
||||
.. seealso:: :py:func:`setEnabled`
|
||||
%End
|
||||
|
||||
void setEnabled( bool enabled );
|
||||
%Docstring
|
||||
Sets whether the map content should be clipped to the current atlas feature.
|
||||
|
||||
.. seealso:: :py:func:`enabled`
|
||||
%End
|
||||
|
||||
QgsMapClippingRegion::FeatureClippingType featureClippingType() const;
|
||||
%Docstring
|
||||
Returns the feature clipping type to apply when clipping to the current atlas feature.
|
||||
|
||||
.. seealso:: :py:func:`setFeatureClippingType`
|
||||
%End
|
||||
|
||||
void setFeatureClippingType( QgsMapClippingRegion::FeatureClippingType type );
|
||||
%Docstring
|
||||
Sets the feature clipping ``type`` to apply when clipping to the current atlas feature.
|
||||
|
||||
.. seealso:: :py:func:`featureClippingType`
|
||||
%End
|
||||
|
||||
bool forceLabelsInsideFeature() const;
|
||||
%Docstring
|
||||
Returns ``True`` if labels should only be placed inside the atlas feature geometry.
|
||||
|
||||
.. seealso:: :py:func:`setForceLabelsInsideFeature`
|
||||
%End
|
||||
|
||||
void setForceLabelsInsideFeature( bool forceInside );
|
||||
%Docstring
|
||||
Sets whether labels should only be placed inside the atlas feature geometry.
|
||||
|
||||
.. seealso:: :py:func:`forceLabelsInsideFeature`
|
||||
%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.
|
||||
|
||||
.. seealso:: :py:func:`setLayersToClip`
|
||||
%End
|
||||
|
||||
void setLayersToClip( const QList< QgsMapLayer * > &layers );
|
||||
%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.
|
||||
|
||||
.. seealso:: :py:func:`layersToClip`
|
||||
%End
|
||||
|
||||
bool writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const;
|
||||
%Docstring
|
||||
Stores settings in a DOM element, where ``element`` is the DOM element
|
||||
corresponding to a 'LayoutMap' tag.
|
||||
|
||||
.. seealso:: :py:func:`readXml`
|
||||
%End
|
||||
|
||||
bool readXml( const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context );
|
||||
%Docstring
|
||||
Sets the setting's state from a DOM document, where ``element`` is the DOM
|
||||
node corresponding to a 'LayoutMap' tag.
|
||||
|
||||
.. seealso:: :py:func:`writeXml`
|
||||
%End
|
||||
|
||||
signals:
|
||||
|
||||
void changed();
|
||||
%Docstring
|
||||
Emitted when the atlas clipping settings are changed.
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
|
||||
class QgsLayoutItemMap : QgsLayoutItem, QgsTemporalRangeObject
|
||||
{
|
||||
%Docstring
|
||||
@ -621,6 +725,13 @@ Removes a previously added rendered feature ``handler``.
|
||||
QTransform layoutToMapCoordsTransform() const;
|
||||
%Docstring
|
||||
Creates a transform from layout coordinates to map coordinates.
|
||||
%End
|
||||
|
||||
QgsLayoutItemMapAtlasClippingSettings *atlasClippingSettings();
|
||||
%Docstring
|
||||
Returns the map's atlas clipping settings.
|
||||
|
||||
.. versionadded:: 3.16
|
||||
%End
|
||||
|
||||
protected:
|
||||
|
@ -119,6 +119,12 @@ Returns ``True`` if the model includes layer's CRS in the display role.
|
||||
%Docstring
|
||||
layersChecked returns the list of layers which are checked (or unchecked)
|
||||
%End
|
||||
|
||||
void setLayersChecked( const QList< QgsMapLayer * > &layers );
|
||||
%Docstring
|
||||
Sets which layers are checked in the model.
|
||||
%End
|
||||
|
||||
bool itemsCheckable() const;
|
||||
%Docstring
|
||||
returns if the items can be checked or not
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
QgsLayoutItemMap::QgsLayoutItemMap( QgsLayout *layout )
|
||||
: QgsLayoutItem( layout )
|
||||
, mAtlasClippingSettings( new QgsLayoutItemMapAtlasClippingSettings( this ) )
|
||||
{
|
||||
mBackgroundUpdateTimer = new QTimer( this );
|
||||
mBackgroundUpdateTimer->setSingleShot( true );
|
||||
@ -56,6 +57,11 @@ QgsLayoutItemMap::QgsLayoutItemMap( QgsLayout *layout )
|
||||
mGridStack = qgis::make_unique< QgsLayoutItemMapGridStack >( this );
|
||||
mOverviewStack = qgis::make_unique< QgsLayoutItemMapOverviewStack >( this );
|
||||
|
||||
connect( mAtlasClippingSettings, &QgsLayoutItemMapAtlasClippingSettings::changed, this, [ = ]
|
||||
{
|
||||
refresh();
|
||||
} );
|
||||
|
||||
if ( layout )
|
||||
connectUpdateSlot();
|
||||
}
|
||||
@ -657,6 +663,8 @@ bool QgsLayoutItemMap::writePropertiesToElement( QDomElement &mapElem, QDomDocum
|
||||
mapElem.setAttribute( QStringLiteral( "temporalRangeEnd" ), temporalRange().end().toString( Qt::ISODate ) );
|
||||
}
|
||||
|
||||
mAtlasClippingSettings->writeXml( mapElem, doc, context );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -811,6 +819,8 @@ bool QgsLayoutItemMap::readPropertiesFromElement( const QDomElement &itemElem, c
|
||||
}
|
||||
}
|
||||
|
||||
mAtlasClippingSettings->readXml( itemElem, doc, context );
|
||||
|
||||
updateBoundingRect();
|
||||
|
||||
//temporal settings
|
||||
@ -1477,6 +1487,24 @@ QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF
|
||||
if ( isTemporal() )
|
||||
jobMapSettings.setTemporalRange( temporalRange() );
|
||||
|
||||
if ( mAtlasClippingSettings->enabled() && mLayout->reportContext().feature().isValid() )
|
||||
{
|
||||
QgsGeometry clipGeom( mLayout->reportContext().currentGeometry( jobMapSettings.destinationCrs() ) );
|
||||
QgsMapClippingRegion region( clipGeom );
|
||||
region.setFeatureClip( mAtlasClippingSettings->featureClippingType() );
|
||||
region.setRestrictedLayers( mAtlasClippingSettings->layersToClip() );
|
||||
jobMapSettings.addClippingRegion( region );
|
||||
|
||||
if ( mAtlasClippingSettings->forceLabelsInsideFeature() )
|
||||
{
|
||||
if ( !jobMapSettings.labelBoundaryGeometry().isEmpty() )
|
||||
{
|
||||
clipGeom = clipGeom.intersection( jobMapSettings.labelBoundaryGeometry() );
|
||||
}
|
||||
jobMapSettings.setLabelBoundaryGeometry( clipGeom );
|
||||
}
|
||||
}
|
||||
|
||||
return jobMapSettings;
|
||||
}
|
||||
|
||||
@ -2606,3 +2634,140 @@ void QgsLayoutItemMap::createStagedRenderJob( const QgsRectangle &extent, const
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsLayoutItemMapAtlasClippingSettings
|
||||
//
|
||||
|
||||
QgsLayoutItemMapAtlasClippingSettings::QgsLayoutItemMapAtlasClippingSettings( QgsLayoutItemMap *map )
|
||||
: QObject( map )
|
||||
, mMap( map )
|
||||
{
|
||||
if ( mMap->layout() && mMap->layout()->project() )
|
||||
{
|
||||
connect( mMap->layout()->project(), static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *>& layers ) > ( &QgsProject::layersWillBeRemoved ),
|
||||
this, &QgsLayoutItemMapAtlasClippingSettings::layersAboutToBeRemoved );
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsLayoutItemMapAtlasClippingSettings::enabled() const
|
||||
{
|
||||
return mClipToAtlasFeature;
|
||||
}
|
||||
|
||||
void QgsLayoutItemMapAtlasClippingSettings::setEnabled( bool enabled )
|
||||
{
|
||||
if ( enabled == mClipToAtlasFeature )
|
||||
return;
|
||||
|
||||
mClipToAtlasFeature = enabled;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QgsMapClippingRegion::FeatureClippingType QgsLayoutItemMapAtlasClippingSettings::featureClippingType() const
|
||||
{
|
||||
return mFeatureClippingType;
|
||||
}
|
||||
|
||||
void QgsLayoutItemMapAtlasClippingSettings::setFeatureClippingType( QgsMapClippingRegion::FeatureClippingType type )
|
||||
{
|
||||
if ( mFeatureClippingType == type )
|
||||
return;
|
||||
|
||||
mFeatureClippingType = type;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
bool QgsLayoutItemMapAtlasClippingSettings::forceLabelsInsideFeature() const
|
||||
{
|
||||
return mForceLabelsInsideFeature;
|
||||
}
|
||||
|
||||
void QgsLayoutItemMapAtlasClippingSettings::setForceLabelsInsideFeature( bool forceInside )
|
||||
{
|
||||
if ( forceInside == mForceLabelsInsideFeature )
|
||||
return;
|
||||
|
||||
mForceLabelsInsideFeature = forceInside;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QList<QgsMapLayer *> QgsLayoutItemMapAtlasClippingSettings::layersToClip() const
|
||||
{
|
||||
return _qgis_listRefToRaw( mLayersToClip );
|
||||
}
|
||||
|
||||
void QgsLayoutItemMapAtlasClippingSettings::setLayersToClip( const QList< QgsMapLayer * > &layersToClip )
|
||||
{
|
||||
mLayersToClip = _qgis_listRawToRef( layersToClip );
|
||||
emit changed();
|
||||
}
|
||||
|
||||
bool QgsLayoutItemMapAtlasClippingSettings::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
|
||||
{
|
||||
QDomElement settingsElem = document.createElement( QStringLiteral( "atlasClippingSettings" ) );
|
||||
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 ) ) );
|
||||
|
||||
//layer set
|
||||
QDomElement layerSetElem = document.createElement( QStringLiteral( "layersToClip" ) );
|
||||
for ( const QgsMapLayerRef &layerRef : mLayersToClip )
|
||||
{
|
||||
if ( !layerRef )
|
||||
continue;
|
||||
QDomElement layerElem = document.createElement( QStringLiteral( "Layer" ) );
|
||||
QDomText layerIdText = document.createTextNode( layerRef.layerId );
|
||||
layerElem.appendChild( layerIdText );
|
||||
|
||||
layerElem.setAttribute( QStringLiteral( "name" ), layerRef.name );
|
||||
layerElem.setAttribute( QStringLiteral( "source" ), layerRef.source );
|
||||
layerElem.setAttribute( QStringLiteral( "provider" ), layerRef.provider );
|
||||
|
||||
layerSetElem.appendChild( layerElem );
|
||||
}
|
||||
settingsElem.appendChild( layerSetElem );
|
||||
|
||||
element.appendChild( settingsElem );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsLayoutItemMapAtlasClippingSettings::readXml( const QDomElement &element, const QDomDocument &, const QgsReadWriteContext & )
|
||||
{
|
||||
const QDomElement settingsElem = element.firstChildElement( QStringLiteral( "atlasClippingSettings" ) );
|
||||
|
||||
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() );
|
||||
|
||||
mLayersToClip.clear();
|
||||
QDomNodeList layerSetNodeList = settingsElem.elementsByTagName( QStringLiteral( "layersToClip" ) );
|
||||
if ( !layerSetNodeList.isEmpty() )
|
||||
{
|
||||
QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
|
||||
QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( QStringLiteral( "Layer" ) );
|
||||
mLayersToClip.reserve( layerIdNodeList.size() );
|
||||
for ( int i = 0; i < layerIdNodeList.size(); ++i )
|
||||
{
|
||||
QDomElement layerElem = layerIdNodeList.at( i ).toElement();
|
||||
QString layerId = layerElem.text();
|
||||
QString layerName = layerElem.attribute( QStringLiteral( "name" ) );
|
||||
QString layerSource = layerElem.attribute( QStringLiteral( "source" ) );
|
||||
QString layerProvider = layerElem.attribute( QStringLiteral( "provider" ) );
|
||||
|
||||
QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
|
||||
if ( mMap->layout() && mMap->layout()->project() )
|
||||
ref.resolveWeakly( mMap->layout()->project() );
|
||||
mLayersToClip << ref;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsLayoutItemMapAtlasClippingSettings::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
|
||||
{
|
||||
if ( !mLayersToClip.isEmpty() )
|
||||
{
|
||||
_qgis_removeLayers( mLayersToClip, layers );
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,117 @@
|
||||
class QgsAnnotation;
|
||||
class QgsRenderedFeatureHandlerInterface;
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsLayoutItemMapAtlasClippingSettings
|
||||
* \brief Contains settings relating to clipping a layout map by the current atlas feature.
|
||||
* \since QGIS 3.16
|
||||
*/
|
||||
class CORE_EXPORT QgsLayoutItemMapAtlasClippingSettings : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsLayoutItemMapAtlasClippingSettings, with the specified \a map parent.
|
||||
*/
|
||||
QgsLayoutItemMapAtlasClippingSettings( QgsLayoutItemMap *map SIP_TRANSFERTHIS = nullptr );
|
||||
|
||||
/**
|
||||
* Returns TRUE if the map content should be clipped to the current atlas feature.
|
||||
*
|
||||
* \see setEnabled()
|
||||
*/
|
||||
bool enabled() const;
|
||||
|
||||
/**
|
||||
* Sets whether the map content should be clipped to the current atlas feature.
|
||||
*
|
||||
* \see enabled()
|
||||
*/
|
||||
void setEnabled( bool enabled );
|
||||
|
||||
/**
|
||||
* Returns the feature clipping type to apply when clipping to the current atlas feature.
|
||||
*
|
||||
* \see setFeatureClippingType()
|
||||
*/
|
||||
QgsMapClippingRegion::FeatureClippingType featureClippingType() const;
|
||||
|
||||
/**
|
||||
* Sets the feature clipping \a type to apply when clipping to the current atlas feature.
|
||||
*
|
||||
* \see featureClippingType()
|
||||
*/
|
||||
void setFeatureClippingType( QgsMapClippingRegion::FeatureClippingType type );
|
||||
|
||||
/**
|
||||
* Returns TRUE if labels should only be placed inside the atlas feature geometry.
|
||||
*
|
||||
* \see setForceLabelsInsideFeature()
|
||||
*/
|
||||
bool forceLabelsInsideFeature() const;
|
||||
|
||||
/**
|
||||
* Sets whether labels should only be placed inside the atlas feature geometry.
|
||||
*
|
||||
* \see forceLabelsInsideFeature()
|
||||
*/
|
||||
void setForceLabelsInsideFeature( bool forceInside );
|
||||
|
||||
/**
|
||||
* Returns the list of map layers to clip to the atlas feature.
|
||||
*
|
||||
* If the returned list is empty then all layers will be clipped.
|
||||
*
|
||||
* \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.
|
||||
*
|
||||
* \see layersToClip()
|
||||
*/
|
||||
void setLayersToClip( const QList< QgsMapLayer * > &layers );
|
||||
|
||||
/**
|
||||
* Stores settings in a DOM element, where \a element is the DOM element
|
||||
* corresponding to a 'LayoutMap' tag.
|
||||
* \see readXml()
|
||||
*/
|
||||
bool writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const;
|
||||
|
||||
/**
|
||||
* Sets the setting's state from a DOM document, where \a element is the DOM
|
||||
* node corresponding to a 'LayoutMap' tag.
|
||||
* \see writeXml()
|
||||
*/
|
||||
bool readXml( const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context );
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
* Emitted when the atlas clipping settings are changed.
|
||||
*/
|
||||
void changed();
|
||||
|
||||
private slots:
|
||||
void layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers );
|
||||
|
||||
private:
|
||||
|
||||
QgsLayoutItemMap *mMap = nullptr;
|
||||
bool mClipToAtlasFeature = false;
|
||||
QList< QgsMapLayerRef > mLayersToClip;
|
||||
QgsMapClippingRegion::FeatureClippingType mFeatureClippingType = QgsMapClippingRegion::FeatureClippingType::ClipPainterOnly;
|
||||
bool mForceLabelsInsideFeature = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsLayoutItemMap
|
||||
@ -559,6 +670,13 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem, public QgsTemporalRan
|
||||
*/
|
||||
QTransform layoutToMapCoordsTransform() const;
|
||||
|
||||
/**
|
||||
* Returns the map's atlas clipping settings.
|
||||
*
|
||||
* \since QGIS 3.16
|
||||
*/
|
||||
QgsLayoutItemMapAtlasClippingSettings *atlasClippingSettings() { return mAtlasClippingSettings; }
|
||||
|
||||
protected:
|
||||
|
||||
void draw( QgsLayoutItemRenderContext &context ) override;
|
||||
@ -843,6 +961,8 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem, public QgsTemporalRan
|
||||
QStringList mExportThemes;
|
||||
QStringList::iterator mExportThemeIt;
|
||||
|
||||
QgsLayoutItemMapAtlasClippingSettings *mAtlasClippingSettings = nullptr;
|
||||
|
||||
/**
|
||||
* Refresh the map's extents, considering data defined extent, scale and rotation
|
||||
* \param context expression context for evaluating data defined map parameters
|
||||
|
@ -105,6 +105,20 @@ QList<QgsMapLayer *> QgsMapLayerModel::layersChecked( Qt::CheckState checkState
|
||||
return layers;
|
||||
}
|
||||
|
||||
void QgsMapLayerModel::setLayersChecked( const QList<QgsMapLayer *> &layers )
|
||||
{
|
||||
QMap<QString, Qt::CheckState>::iterator i = mLayersChecked.begin();
|
||||
for ( ; i != mLayersChecked.end(); ++i )
|
||||
{
|
||||
*i = Qt::Unchecked;
|
||||
}
|
||||
for ( const QgsMapLayer *layer : layers )
|
||||
{
|
||||
mLayersChecked[ layer->id() ] = Qt::Checked;
|
||||
}
|
||||
emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ), QVector<int>() << Qt::CheckStateRole );
|
||||
}
|
||||
|
||||
QModelIndex QgsMapLayerModel::indexFromLayer( QgsMapLayer *layer ) const
|
||||
{
|
||||
int r = mLayers.indexOf( layer );
|
||||
@ -569,7 +583,7 @@ bool QgsMapLayerModel::setData( const QModelIndex &index, const QVariant &value,
|
||||
{
|
||||
QgsMapLayer *layer = static_cast<QgsMapLayer *>( index.internalPointer() );
|
||||
mLayersChecked[layer->id()] = ( Qt::CheckState )value.toInt();
|
||||
emit dataChanged( index, index );
|
||||
emit dataChanged( index, index, QVector< int >() << Qt::CheckStateRole );
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
@ -128,6 +128,12 @@ class CORE_EXPORT QgsMapLayerModel : public QAbstractItemModel
|
||||
* \brief layersChecked returns the list of layers which are checked (or unchecked)
|
||||
*/
|
||||
QList<QgsMapLayer *> layersChecked( Qt::CheckState checkState = Qt::Checked );
|
||||
|
||||
/**
|
||||
* Sets which layers are checked in the model.
|
||||
*/
|
||||
void setLayersChecked( const QList< QgsMapLayer * > &layers );
|
||||
|
||||
//! returns if the items can be checked or not
|
||||
bool itemsCheckable() const { return mItemCheckable; }
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "qgsbookmarkmodel.h"
|
||||
#include "qgsreferencedgeometry.h"
|
||||
#include "qgsprojectviewsettings.h"
|
||||
#include "qgsmaplayermodel.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
@ -85,6 +86,8 @@ QgsLayoutMapWidget::QgsLayoutMapWidget( QgsLayoutItemMap *item, QgsMapCanvas *ma
|
||||
connect( mOverviewListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged );
|
||||
connect( mOverviewListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_itemChanged );
|
||||
connect( mActionLabelSettings, &QAction::triggered, this, &QgsLayoutMapWidget::showLabelSettings );
|
||||
connect( mActionClipSettings, &QAction::triggered, this, &QgsLayoutMapWidget::showClipSettings );
|
||||
|
||||
connect( mTemporalCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mTemporalCheckBox_toggled );
|
||||
connect( mStartDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsLayoutMapWidget::updateTemporalExtent );
|
||||
connect( mEndDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsLayoutMapWidget::updateTemporalExtent );
|
||||
@ -207,8 +210,14 @@ void QgsLayoutMapWidget::setMasterLayout( QgsMasterLayoutInterface *masterLayout
|
||||
|
||||
void QgsLayoutMapWidget::setReportTypeString( const QString &string )
|
||||
{
|
||||
mReportTypeString = string;
|
||||
mAtlasCheckBox->setTitle( tr( "Controlled by %1" ).arg( string == tr( "atlas" ) ? tr( "Atlas" ) : tr( "Report" ) ) );
|
||||
mAtlasPredefinedScaleRadio->setToolTip( tr( "Use one of the predefined scales of the project where the %1 feature best fits." ).arg( string ) );
|
||||
|
||||
if ( mClipWidget )
|
||||
mClipWidget->setReportTypeString( string );
|
||||
if ( mLabelWidget )
|
||||
mLabelWidget->setReportTypeString( string );
|
||||
}
|
||||
|
||||
void QgsLayoutMapWidget::setDesignerInterface( QgsLayoutDesignerInterface *iface )
|
||||
@ -233,6 +242,8 @@ bool QgsLayoutMapWidget::setNewItem( QgsLayoutItem *item )
|
||||
mItemPropertiesWidget->setItem( mMapItem );
|
||||
if ( mLabelWidget )
|
||||
mLabelWidget->setItem( mMapItem );
|
||||
if ( mClipWidget )
|
||||
mClipWidget->setItem( mMapItem );
|
||||
|
||||
if ( mMapItem )
|
||||
{
|
||||
@ -413,9 +424,21 @@ void QgsLayoutMapWidget::overviewSymbolChanged()
|
||||
void QgsLayoutMapWidget::showLabelSettings()
|
||||
{
|
||||
mLabelWidget = new QgsLayoutMapLabelingWidget( mMapItem );
|
||||
|
||||
if ( !mReportTypeString.isEmpty() )
|
||||
mLabelWidget->setReportTypeString( mReportTypeString );
|
||||
|
||||
openPanel( mLabelWidget );
|
||||
}
|
||||
|
||||
void QgsLayoutMapWidget::showClipSettings()
|
||||
{
|
||||
mClipWidget = new QgsLayoutMapClippingWidget( mMapItem );
|
||||
if ( !mReportTypeString.isEmpty() )
|
||||
mClipWidget->setReportTypeString( mReportTypeString );
|
||||
openPanel( mClipWidget );
|
||||
}
|
||||
|
||||
void QgsLayoutMapWidget::switchToMoveContentTool()
|
||||
{
|
||||
if ( mInterface )
|
||||
@ -1925,3 +1948,180 @@ bool QgsLayoutMapItemBlocksLabelsModel::filterAcceptsRow( int source_row, const
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// QgsLayoutMapClippingWidget
|
||||
//
|
||||
|
||||
QgsLayoutMapClippingWidget::QgsLayoutMapClippingWidget( QgsLayoutItemMap *map )
|
||||
: QgsLayoutItemBaseWidget( nullptr, map )
|
||||
, mMapItem( map )
|
||||
{
|
||||
setupUi( this );
|
||||
setPanelTitle( tr( "Clipping Settings" ) );
|
||||
|
||||
mLayerModel = new QgsMapLayerModel( this );
|
||||
mLayerModel->setItemsCheckable( true );
|
||||
mLayersTreeView->setModel( mLayerModel );
|
||||
|
||||
mAtlasClippingTypeComboBox->addItem( tr( "Clip During Render Only" ), static_cast< int >( QgsMapClippingRegion::FeatureClippingType::ClipPainterOnly ) );
|
||||
mAtlasClippingTypeComboBox->addItem( tr( "Clip Feature Before Render" ), static_cast< int >( QgsMapClippingRegion::FeatureClippingType::ClipToIntersection ) );
|
||||
mAtlasClippingTypeComboBox->addItem( tr( "Render Intersecting Features Unchanged" ), static_cast< int >( QgsMapClippingRegion::FeatureClippingType::NoClipping ) );
|
||||
|
||||
connect( mRadioClipSelectedLayers, &QRadioButton::toggled, mLayersTreeView, &QWidget::setEnabled );
|
||||
mLayersTreeView->setEnabled( false );
|
||||
mRadioClipAllLayers->setChecked( true );
|
||||
|
||||
connect( mClipToAtlasCheckBox, &QGroupBox::toggled, this, [ = ]( bool active )
|
||||
{
|
||||
if ( !mBlockUpdates )
|
||||
{
|
||||
mMapItem->beginCommand( tr( "Toggle Atlas Clipping" ) );
|
||||
mMapItem->atlasClippingSettings()->setEnabled( active );
|
||||
mMapItem->endCommand();
|
||||
}
|
||||
} );
|
||||
connect( mForceLabelsInsideCheckBox, &QCheckBox::toggled, this, [ = ]( bool active )
|
||||
{
|
||||
if ( !mBlockUpdates )
|
||||
{
|
||||
mMapItem->beginCommand( tr( "Change Atlas Clipping Label Behavior" ) );
|
||||
mMapItem->atlasClippingSettings()->setForceLabelsInsideFeature( active );
|
||||
mMapItem->endCommand();
|
||||
}
|
||||
} );
|
||||
connect( mAtlasClippingTypeComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, [ = ]
|
||||
{
|
||||
if ( !mBlockUpdates )
|
||||
{
|
||||
mMapItem->beginCommand( tr( "Change Atlas Clipping Behavior" ) );
|
||||
mMapItem->atlasClippingSettings()->setFeatureClippingType( static_cast< QgsMapClippingRegion::FeatureClippingType >( mAtlasClippingTypeComboBox->currentData().toInt() ) );
|
||||
mMapItem->endCommand();
|
||||
}
|
||||
} );
|
||||
|
||||
connect( mRadioClipSelectedLayers, &QCheckBox::toggled, this, [ = ]( bool active )
|
||||
{
|
||||
if ( active && !mBlockUpdates )
|
||||
{
|
||||
mBlockUpdates = true;
|
||||
mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
|
||||
mMapItem->atlasClippingSettings()->setLayersToClip( mLayerModel->layersChecked( ) );
|
||||
mMapItem->endCommand();
|
||||
mBlockUpdates = false;
|
||||
}
|
||||
} );
|
||||
connect( mRadioClipAllLayers, &QCheckBox::toggled, this, [ = ]( bool active )
|
||||
{
|
||||
if ( active && !mBlockUpdates )
|
||||
{
|
||||
mBlockUpdates = true;
|
||||
mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
|
||||
mMapItem->atlasClippingSettings()->setLayersToClip( QList< QgsMapLayer * >() );
|
||||
mMapItem->endCommand();
|
||||
mBlockUpdates = false;
|
||||
}
|
||||
} );
|
||||
connect( mLayerModel, &QgsMapLayerModel::dataChanged, this, [ = ]( const QModelIndex &, const QModelIndex &, const QVector<int> &roles = QVector<int>() )
|
||||
{
|
||||
if ( !roles.contains( Qt::CheckStateRole ) )
|
||||
return;
|
||||
|
||||
if ( !mBlockUpdates )
|
||||
{
|
||||
mBlockUpdates = true;
|
||||
mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
|
||||
mMapItem->atlasClippingSettings()->setLayersToClip( mLayerModel->layersChecked() );
|
||||
mMapItem->endCommand();
|
||||
mBlockUpdates = false;
|
||||
}
|
||||
} );
|
||||
|
||||
setNewItem( map );
|
||||
|
||||
connect( &map->layout()->reportContext(), &QgsLayoutReportContext::layerChanged,
|
||||
this, &QgsLayoutMapClippingWidget::atlasLayerChanged );
|
||||
if ( QgsLayoutAtlas *atlas = layoutAtlas() )
|
||||
{
|
||||
connect( atlas, &QgsLayoutAtlas::toggled, this, &QgsLayoutMapClippingWidget::atlasToggled );
|
||||
atlasToggled( atlas->enabled() );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLayoutMapClippingWidget::setReportTypeString( const QString &string )
|
||||
{
|
||||
mClipToAtlasCheckBox->setTitle( tr( "Clip to %1 feature" ).arg( string ) );
|
||||
mClipToAtlasLabel->setText( tr( "<b>When enabled, map layers will be automatically clipped to the boundary of the current %1 feature.</b>" ).arg( string ) );
|
||||
mForceLabelsInsideCheckBox->setText( tr( "Force labels inside %1 feature" ).arg( string ) );
|
||||
}
|
||||
|
||||
bool QgsLayoutMapClippingWidget::setNewItem( QgsLayoutItem *item )
|
||||
{
|
||||
if ( item->type() != QgsLayoutItemRegistry::LayoutMap )
|
||||
return false;
|
||||
|
||||
if ( mMapItem )
|
||||
{
|
||||
disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapClippingWidget::updateGuiElements );
|
||||
}
|
||||
|
||||
mMapItem = qobject_cast< QgsLayoutItemMap * >( item );
|
||||
|
||||
if ( mMapItem )
|
||||
{
|
||||
connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapClippingWidget::updateGuiElements );
|
||||
}
|
||||
|
||||
updateGuiElements();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsLayoutMapClippingWidget::updateGuiElements()
|
||||
{
|
||||
if ( mBlockUpdates )
|
||||
return;
|
||||
|
||||
mBlockUpdates = true;
|
||||
mClipToAtlasCheckBox->setChecked( mMapItem->atlasClippingSettings()->enabled() );
|
||||
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() );
|
||||
mLayerModel->setLayersChecked( mMapItem->atlasClippingSettings()->layersToClip() );
|
||||
|
||||
mBlockUpdates = false;
|
||||
}
|
||||
|
||||
void QgsLayoutMapClippingWidget::atlasLayerChanged( QgsVectorLayer *layer )
|
||||
{
|
||||
if ( !layer || layer->geometryType() != QgsWkbTypes::PolygonGeometry )
|
||||
{
|
||||
//non-polygon layer, disable atlas control
|
||||
mClipToAtlasCheckBox->setChecked( false );
|
||||
mClipToAtlasCheckBox->setEnabled( false );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
mClipToAtlasCheckBox->setEnabled( true );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLayoutMapClippingWidget::atlasToggled( bool atlasEnabled )
|
||||
{
|
||||
if ( atlasEnabled &&
|
||||
mMapItem && mMapItem->layout() && mMapItem->layout()->reportContext().layer()
|
||||
&& mMapItem->layout()->reportContext().layer()->geometryType() == QgsWkbTypes::PolygonGeometry )
|
||||
{
|
||||
mClipToAtlasCheckBox->setEnabled( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
mClipToAtlasCheckBox->setEnabled( false );
|
||||
mClipToAtlasCheckBox->setChecked( false );
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "qgis_gui.h"
|
||||
#include "ui_qgslayoutmapwidgetbase.h"
|
||||
#include "ui_qgslayoutmaplabelingwidgetbase.h"
|
||||
#include "ui_qgslayoutmapclippingwidgetbase.h"
|
||||
#include "qgslayoutitemwidget.h"
|
||||
#include "qgslayoutitemmapgrid.h"
|
||||
|
||||
@ -31,6 +32,7 @@ class QgsMapLayer;
|
||||
class QgsLayoutItemMap;
|
||||
class QgsLayoutItemMapOverview;
|
||||
class QgsLayoutMapLabelingWidget;
|
||||
class QgsLayoutMapClippingWidget;
|
||||
class QgsBookmarkManagerProxyModel;
|
||||
|
||||
/**
|
||||
@ -136,6 +138,7 @@ class GUI_EXPORT QgsLayoutMapWidget: public QgsLayoutItemBaseWidget, private Ui:
|
||||
void mapCrsChanged( const QgsCoordinateReferenceSystem &crs );
|
||||
void overviewSymbolChanged();
|
||||
void showLabelSettings();
|
||||
void showClipSettings();
|
||||
void switchToMoveContentTool();
|
||||
void aboutToShowBookmarkMenu();
|
||||
|
||||
@ -145,8 +148,10 @@ class GUI_EXPORT QgsLayoutMapWidget: public QgsLayoutItemBaseWidget, private Ui:
|
||||
QgsLayoutItemPropertiesWidget *mItemPropertiesWidget = nullptr;
|
||||
QgsLayoutDesignerInterface *mInterface = nullptr;
|
||||
QPointer< QgsLayoutMapLabelingWidget > mLabelWidget;
|
||||
QPointer< QgsLayoutMapClippingWidget > mClipWidget;
|
||||
QMenu *mBookmarkMenu = nullptr;
|
||||
QgsBookmarkManagerProxyModel *mBookmarkModel = nullptr;
|
||||
QString mReportTypeString;
|
||||
|
||||
//! Sets extent of composer map from line edits
|
||||
void updateComposerExtentFromGui();
|
||||
@ -242,4 +247,36 @@ class GUI_EXPORT QgsLayoutMapLabelingWidget: public QgsLayoutItemBaseWidget, pri
|
||||
QPointer< QgsLayoutItemMap > mMapItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* \ingroup gui
|
||||
* Allows configuration of layout map clipping settings.
|
||||
*
|
||||
* \note This class is not a part of public API
|
||||
* \since QGIS 3.16
|
||||
*/
|
||||
class GUI_EXPORT QgsLayoutMapClippingWidget: public QgsLayoutItemBaseWidget, private Ui::QgsLayoutMapClippingWidgetBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//! constructor
|
||||
explicit QgsLayoutMapClippingWidget( QgsLayoutItemMap *map );
|
||||
|
||||
void setReportTypeString( const QString &string ) override;
|
||||
|
||||
protected:
|
||||
bool setNewItem( QgsLayoutItem *item ) override;
|
||||
|
||||
private slots:
|
||||
void updateGuiElements();
|
||||
void atlasLayerChanged( QgsVectorLayer *layer );
|
||||
void atlasToggled( bool atlasEnabled );
|
||||
|
||||
private:
|
||||
QPointer< QgsLayoutItemMap > mMapItem;
|
||||
QgsMapLayerModel *mLayerModel = nullptr;
|
||||
|
||||
bool mBlockUpdates = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
127
src/ui/layout/qgslayoutmapclippingwidgetbase.ui
Normal file
127
src/ui/layout/qgslayoutmapclippingwidgetbase.ui
Normal file
@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QgsLayoutMapClippingWidgetBase</class>
|
||||
<widget class="QWidget" name="QgsLayoutMapClippingWidgetBase">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>318</width>
|
||||
<height>408</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="QgsCollapsibleGroupBox" name="mClipToAtlasCheckBox">
|
||||
<property name="title">
|
||||
<string>Clip to atlas feature</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="8" column="0">
|
||||
<widget class="QTreeView" name="mLayersTreeView">
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>26</number>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QComboBox" name="mAtlasClippingTypeComboBox"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>The clipping mode determines how features from vector layers will be clipped.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="mClipToAtlasLabel">
|
||||
<property name="text">
|
||||
<string><b>When enabled, map layers will be automatically clipped to the boundary of the current atlas feature.</b></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QRadioButton" name="mRadioClipSelectedLayers">
|
||||
<property name="text">
|
||||
<string>Clip selected layers:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QRadioButton" name="mRadioClipAllLayers">
|
||||
<property name="text">
|
||||
<string>Clip all layers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="mForceLabelsInsideCheckBox">
|
||||
<property name="text">
|
||||
<string>Force labels inside atlas feature</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>QgsCollapsibleGroupBox</class>
|
||||
<extends>QGroupBox</extends>
|
||||
<header>qgscollapsiblegroupbox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -73,6 +73,7 @@
|
||||
<addaction name="mActionMoveContent"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="mActionLabelSettings"/>
|
||||
<addaction name="mActionClipSettings"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -88,8 +89,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>545</width>
|
||||
<height>1113</height>
|
||||
<width>548</width>
|
||||
<height>1284</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -174,7 +175,7 @@
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QgsProjectionSelectionWidget" name="mCrsSelector">
|
||||
<widget class="QgsProjectionSelectionWidget" name="mCrsSelector" native="true">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
@ -997,18 +998,59 @@
|
||||
<string>Labeling Settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="mActionClipSettings">
|
||||
<property name="icon">
|
||||
<iconset resource="../../../images/images.qrc">
|
||||
<normaloff>:/images/themes/default/algorithms/mAlgorithmClip.svg</normaloff>:/images/themes/default/algorithms/mAlgorithmClip.svg</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clipping Settings</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QgsScrollArea</class>
|
||||
<extends>QScrollArea</extends>
|
||||
<header>qgsscrollarea.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsDoubleSpinBox</class>
|
||||
<extends>QDoubleSpinBox</extends>
|
||||
<header>qgsdoublespinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>qgsspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsCollapsibleGroupBoxBasic</class>
|
||||
<extends>QGroupBox</extends>
|
||||
<header location="global">qgscollapsiblegroupbox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsLayoutItemComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>qgslayoutitemcombobox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsPropertyOverrideButton</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>qgspropertyoverridebutton.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsMapLayerComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>qgsmaplayercombobox.h</header>
|
||||
<header location="global">qgsmaplayercombobox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsBlendModeComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>qgsblendmodecombobox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsProjectionSelectionWidget</class>
|
||||
@ -1016,43 +1058,11 @@
|
||||
<header>qgsprojectionselectionwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsPropertyOverrideButton</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>qgspropertyoverridebutton.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>qgsspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsSymbolButton</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>qgssymbolbutton.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsScrollArea</class>
|
||||
<extends>QScrollArea</extends>
|
||||
<header>qgsscrollarea.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsCollapsibleGroupBoxBasic</class>
|
||||
<extends>QGroupBox</extends>
|
||||
<header>qgscollapsiblegroupbox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsBlendModeComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>qgsblendmodecombobox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsLayoutItemComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>qgslayoutitemcombobox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>scrollArea</tabstop>
|
||||
@ -1110,6 +1120,8 @@
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../../images/images.qrc"/>
|
||||
<include location="../../../images/images.qrc"/>
|
||||
<include location="../../../images/images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -125,6 +125,7 @@ ADD_PYTHON_TEST(PyQgsLayoutItemPropertiesDialog test_qgslayoutitempropertiesdial
|
||||
ADD_PYTHON_TEST(PyQgsLayoutLabel test_qgslayoutlabel.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutLegend test_qgslayoutlegend.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutMap test_qgslayoutmap.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutItemMapAtlasClippingSettings test_qgslayoutatlasclippingsettings.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutMapGrid test_qgslayoutmapgrid.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutMapOverview test_qgslayoutmapoverview.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayoutMarker test_qgslayoutmarker.py)
|
||||
|
@ -516,6 +516,62 @@ class TestQgsLayoutAtlas(unittest.TestCase):
|
||||
self.assertTrue(myTestResult, myMessage)
|
||||
self.atlas.endRender()
|
||||
|
||||
def test_clipping(self):
|
||||
vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp")
|
||||
vectorLayer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")
|
||||
|
||||
p = QgsProject()
|
||||
p.addMapLayers([vectorLayer])
|
||||
|
||||
# create layout with layout map
|
||||
|
||||
# select epsg:2154
|
||||
crs = QgsCoordinateReferenceSystem('epsg:2154')
|
||||
p.setCrs(crs)
|
||||
|
||||
layout = QgsPrintLayout(p)
|
||||
layout.initializeDefaults()
|
||||
|
||||
# fix the renderer, fill with green
|
||||
props = {"color": "0,127,0", 'outline_style': 'no'}
|
||||
fillSymbol = QgsFillSymbol.createSimple(props)
|
||||
renderer = QgsSingleSymbolRenderer(fillSymbol)
|
||||
vectorLayer.setRenderer(renderer)
|
||||
|
||||
# the atlas map
|
||||
atlas_map = QgsLayoutItemMap(layout)
|
||||
atlas_map.attemptSetSceneRect(QRectF(20, 20, 130, 130))
|
||||
atlas_map.setFrameEnabled(False)
|
||||
atlas_map.setLayers([vectorLayer])
|
||||
layout.addLayoutItem(atlas_map)
|
||||
|
||||
# the atlas
|
||||
atlas = layout.atlas()
|
||||
atlas.setCoverageLayer(vectorLayer)
|
||||
atlas.setEnabled(True)
|
||||
|
||||
atlas_map.setExtent(
|
||||
QgsRectangle(332719.06221504929, 6765214.5887386119, 560957.85090677091, 6993453.3774303338))
|
||||
|
||||
atlas_map.setAtlasDriven(True)
|
||||
atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Auto)
|
||||
atlas_map.setAtlasMargin(0.10)
|
||||
|
||||
atlas_map.atlasClippingSettings().setEnabled(True)
|
||||
|
||||
atlas.beginRender()
|
||||
|
||||
for i in range(0, 2):
|
||||
atlas.seekTo(i)
|
||||
|
||||
checker = QgsLayoutChecker('atlas_clipping%d' % (i + 1), layout)
|
||||
checker.setControlPathPrefix("atlas")
|
||||
myTestResult, myMessage = checker.testLayout(0, 200)
|
||||
self.report += checker.report()
|
||||
|
||||
self.assertTrue(myTestResult, myMessage)
|
||||
atlas.endRender()
|
||||
|
||||
def legend_test(self):
|
||||
self.atlas_map.setAtlasDriven(True)
|
||||
self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Auto)
|
||||
|
113
tests/src/python/test_qgslayoutatlasclippingsettings.py
Normal file
113
tests/src/python/test_qgslayoutatlasclippingsettings.py
Normal file
@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsLayoutItemMapAtlasClippingSettings.
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
__author__ = '(C) 2020 Nyall Dawson'
|
||||
__date__ = '03/07/2020'
|
||||
__copyright__ = 'Copyright 2020, The QGIS Project'
|
||||
|
||||
import qgis # NOQA
|
||||
|
||||
|
||||
from qgis.core import (QgsLayoutItemMap,
|
||||
QgsLayout,
|
||||
QgsProject,
|
||||
QgsLayoutItemMapAtlasClippingSettings,
|
||||
QgsMapClippingRegion,
|
||||
QgsVectorLayer,
|
||||
QgsReadWriteContext)
|
||||
|
||||
from qgis.testing import start_app, unittest
|
||||
from utilities import unitTestDataPath
|
||||
from qgis.PyQt.QtTest import QSignalSpy
|
||||
from qgis.PyQt.QtXml import QDomDocument
|
||||
|
||||
|
||||
start_app()
|
||||
TEST_DATA_DIR = unitTestDataPath()
|
||||
|
||||
|
||||
class TestQgsLayoutItemMapAtlasClippingSettings(unittest.TestCase):
|
||||
|
||||
def testSettings(self):
|
||||
p = QgsProject()
|
||||
l = QgsLayout(p)
|
||||
map = QgsLayoutItemMap(l)
|
||||
|
||||
settings = QgsLayoutItemMapAtlasClippingSettings(map)
|
||||
spy = QSignalSpy(settings.changed)
|
||||
|
||||
self.assertFalse(settings.enabled())
|
||||
settings.setEnabled(True)
|
||||
self.assertTrue(settings.enabled())
|
||||
self.assertEqual(len(spy), 1)
|
||||
settings.setEnabled(True)
|
||||
self.assertEqual(len(spy), 1)
|
||||
|
||||
settings.setFeatureClippingType(QgsMapClippingRegion.FeatureClippingType.NoClipping)
|
||||
self.assertEqual(settings.featureClippingType(), QgsMapClippingRegion.FeatureClippingType.NoClipping)
|
||||
self.assertEqual(len(spy), 2)
|
||||
settings.setFeatureClippingType(QgsMapClippingRegion.FeatureClippingType.NoClipping)
|
||||
self.assertEqual(len(spy), 2)
|
||||
|
||||
self.assertFalse(settings.forceLabelsInsideFeature())
|
||||
settings.setForceLabelsInsideFeature(True)
|
||||
self.assertTrue(settings.forceLabelsInsideFeature())
|
||||
self.assertEqual(len(spy), 3)
|
||||
settings.setForceLabelsInsideFeature(True)
|
||||
self.assertEqual(len(spy), 3)
|
||||
|
||||
l1 = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
|
||||
"addfeat", "memory")
|
||||
l2 = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
|
||||
"addfeat", "memory")
|
||||
p.addMapLayers([l1, l2])
|
||||
self.assertFalse(settings.layersToClip())
|
||||
settings.setLayersToClip([l1, l2])
|
||||
self.assertCountEqual(settings.layersToClip(), [l1, l2])
|
||||
self.assertEqual(len(spy), 4)
|
||||
|
||||
p.removeMapLayer(l1.id())
|
||||
self.assertCountEqual(settings.layersToClip(), [l2])
|
||||
|
||||
def testSaveRestore(self):
|
||||
p = QgsProject()
|
||||
l1 = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
|
||||
"addfeat", "memory")
|
||||
l2 = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
|
||||
"addfeat", "memory")
|
||||
p.addMapLayers([l1, l2])
|
||||
|
||||
l = QgsLayout(p)
|
||||
map = QgsLayoutItemMap(l)
|
||||
|
||||
settings = map.atlasClippingSettings()
|
||||
settings.setEnabled(True)
|
||||
settings.setFeatureClippingType(QgsMapClippingRegion.FeatureClippingType.NoClipping)
|
||||
settings.setForceLabelsInsideFeature(True)
|
||||
settings.setLayersToClip([l2])
|
||||
|
||||
# save map to xml
|
||||
doc = QDomDocument("testdoc")
|
||||
elem = doc.createElement("test")
|
||||
self.assertTrue(map.writeXml(elem, doc, QgsReadWriteContext()))
|
||||
|
||||
layout2 = QgsLayout(p)
|
||||
map2 = QgsLayoutItemMap(layout2)
|
||||
self.assertFalse(map2.atlasClippingSettings().enabled())
|
||||
|
||||
# restore from xml
|
||||
self.assertTrue(map2.readXml(elem.firstChildElement(), doc, QgsReadWriteContext()))
|
||||
|
||||
self.assertTrue(map2.atlasClippingSettings().enabled())
|
||||
self.assertEqual(map2.atlasClippingSettings().featureClippingType(), QgsMapClippingRegion.FeatureClippingType.NoClipping)
|
||||
self.assertTrue(map2.atlasClippingSettings().forceLabelsInsideFeature())
|
||||
self.assertEqual(map2.atlasClippingSettings().layersToClip(), [l2])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
BIN
tests/testdata/control_images/atlas/expected_atlas_clipping1/expected_atlas_clipping1.png
vendored
Normal file
BIN
tests/testdata/control_images/atlas/expected_atlas_clipping1/expected_atlas_clipping1.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
tests/testdata/control_images/atlas/expected_atlas_clipping2/expected_atlas_clipping2.png
vendored
Normal file
BIN
tests/testdata/control_images/atlas/expected_atlas_clipping2/expected_atlas_clipping2.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
Loading…
x
Reference in New Issue
Block a user