From 1b309c68100eba2fbd872aa8791e8e94c95473b7 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 28 Dec 2018 16:39:50 +1000 Subject: [PATCH] [layouts] Add setters/getters for map item stacking positions --- .../layout/qgslayoutitemmapitem.sip.in | 67 ++++++++++++++++++- src/core/layout/qgslayoutitemmapitem.cpp | 31 +++++++++ src/core/layout/qgslayoutitemmapitem.h | 67 ++++++++++++++++++- tests/src/core/testqgslayout.cpp | 40 +++++++++++ tests/src/python/test_qgslayoutmapoverview.py | 20 ++++++ 5 files changed, 221 insertions(+), 4 deletions(-) diff --git a/python/core/auto_generated/layout/qgslayoutitemmapitem.sip.in b/python/core/auto_generated/layout/qgslayoutitemmapitem.sip.in index 99e41f4462f..523ca86c8ed 100644 --- a/python/core/auto_generated/layout/qgslayoutitemmapitem.sip.in +++ b/python/core/auto_generated/layout/qgslayoutitemmapitem.sip.in @@ -22,6 +22,15 @@ An item which is drawn inside a QgsLayoutItemMap, e.g., a grid or map overview. %End public: + enum StackingPosition + { + StackBelowMap, + StackBelowMapLayer, + StackAboveMapLayer, + StackBelowMapLabels, + StackAboveMapLabels, + }; + QgsLayoutItemMapItem( const QString &name, QgsLayoutItemMap *map ); %Docstring Constructor for QgsLayoutItemMapItem, attached to the specified ``map``. @@ -111,6 +120,60 @@ Returns whether the item will be drawn. virtual bool usesAdvancedEffects() const; %Docstring Returns true if the item is drawn using advanced effects, such as blend modes. +%End + + StackingPosition stackingPosition() const; +%Docstring +Returns the item's stacking position, which specifies where the in the map's +stack the item should be rendered. + +.. seealso:: :py:func:`setStackingPosition` + +.. seealso:: :py:func:`stackingLayer` + +.. versionadded:: 3.6 +%End + + void setStackingPosition( StackingPosition position ); +%Docstring +Sets the item's stacking ``position``, which specifies where the in the map's +stack the item should be rendered. + +.. seealso:: :py:func:`stackingPosition` + +.. seealso:: :py:func:`setStackingLayer` + +.. versionadded:: 3.6 +%End + + QgsMapLayer *stackingLayer() const; +%Docstring +Returns the item's stacking layer, which specifies where the in the map's +stack the item should be rendered. + +This setting is only used when stackingPosition() is StackBelowMapLayer or +StackAboveMapLayer. + +.. seealso:: :py:func:`setStackingLayer` + +.. seealso:: :py:func:`stackingPosition` + +.. versionadded:: 3.6 +%End + + void setStackingLayer( QgsMapLayer *layer ); +%Docstring +Sets the item's stacking ``layer``, which specifies where the in the map's +stack the item should be rendered. + +This setting is only used when stackingPosition() is StackBelowMapLayer or +StackAboveMapLayer. + +.. seealso:: :py:func:`stackingLayer` + +.. seealso:: :py:func:`setStackingPosition` + +.. versionadded:: 3.6 %End protected: @@ -119,10 +182,10 @@ Returns true if the item is drawn using advanced effects, such as blend modes. + + }; - - class QgsLayoutItemMapItemStack { %Docstring diff --git a/src/core/layout/qgslayoutitemmapitem.cpp b/src/core/layout/qgslayoutitemmapitem.cpp index 907a52605ad..4f9793aeb00 100644 --- a/src/core/layout/qgslayoutitemmapitem.cpp +++ b/src/core/layout/qgslayoutitemmapitem.cpp @@ -17,6 +17,7 @@ #include "qgslayoutitemmapitem.h" #include "qgslayoutitemmap.h" +#include "qgslayout.h" #include QgsLayoutItemMapItem::QgsLayoutItemMapItem( const QString &name, QgsLayoutItemMap *map ) @@ -35,6 +36,16 @@ bool QgsLayoutItemMapItem::writeXml( QDomElement &element, QDomDocument &documen element.setAttribute( QStringLiteral( "uuid" ), mUuid ); element.setAttribute( QStringLiteral( "name" ), mName ); element.setAttribute( QStringLiteral( "show" ), mEnabled ); + element.setAttribute( QStringLiteral( "position" ), static_cast< int >( mStackingPosition ) ); + + if ( mStackingLayer ) + { + element.setAttribute( QStringLiteral( "stackingLayer" ), mStackingLayer.layerId ); + element.setAttribute( QStringLiteral( "stackingLayerName" ), mStackingLayer.name ); + element.setAttribute( QStringLiteral( "stackingLayerSource" ), mStackingLayer.source ); + element.setAttribute( QStringLiteral( "stackingLayerProvider" ), mStackingLayer.provider ); + } + return true; } @@ -44,6 +55,16 @@ bool QgsLayoutItemMapItem::readXml( const QDomElement &itemElem, const QDomDocum mUuid = itemElem.attribute( QStringLiteral( "uuid" ) ); mName = itemElem.attribute( QStringLiteral( "name" ) ); mEnabled = ( itemElem.attribute( QStringLiteral( "show" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) ); + mStackingPosition = static_cast< StackingPosition >( itemElem.attribute( QStringLiteral( "position" ), QString::number( QgsLayoutItemMapItem::StackBelowMapLabels ) ).toInt() ); + + const QString layerId = itemElem.attribute( QStringLiteral( "stackingLayer" ) ); + const QString layerName = itemElem.attribute( QStringLiteral( "stackingLayerName" ) ); + const QString layerSource = itemElem.attribute( QStringLiteral( "stackingLayerSource" ) ); + const QString layerProvider = itemElem.attribute( QStringLiteral( "stackingLayerProvider" ) ); + mStackingLayer = QgsMapLayerRef( layerId, layerName, layerSource, layerProvider ); + if ( mMap && mMap->layout() ) + mStackingLayer.resolveWeakly( mMap->layout()->project() ); + return true; } @@ -86,6 +107,16 @@ bool QgsLayoutItemMapItem::usesAdvancedEffects() const return false; } +QgsMapLayer *QgsLayoutItemMapItem::stackingLayer() const +{ + return mStackingLayer.get(); +} + +void QgsLayoutItemMapItem::setStackingLayer( QgsMapLayer *layer ) +{ + mStackingLayer.setLayer( layer ); +} + // // QgsLayoutItemMapItemStack // diff --git a/src/core/layout/qgslayoutitemmapitem.h b/src/core/layout/qgslayoutitemmapitem.h index e037501ad58..db9bb5b9067 100644 --- a/src/core/layout/qgslayoutitemmapitem.h +++ b/src/core/layout/qgslayoutitemmapitem.h @@ -20,6 +20,7 @@ #include "qgis_core.h" #include "qgis_sip.h" #include "qgslayoutobject.h" +#include "qgsmaplayerref.h" class QgsLayoutItemMap; @@ -35,6 +36,16 @@ class CORE_EXPORT QgsLayoutItemMapItem : public QgsLayoutObject public: + //! Item stacking position, specifies where the in the map's stack the item should be rendered + enum StackingPosition + { + StackBelowMap, //!< Render below all map layers + StackBelowMapLayer, //!< Render below a specific map layer (see stackingLayer()) + StackAboveMapLayer, //!< Render above a specific map layer (see stackingLayer()) + StackBelowMapLabels, //!< Render above all map layers, but below map labels + StackAboveMapLabels, //!< Render above all map layers and labels + }; + /** * Constructor for QgsLayoutItemMapItem, attached to the specified \a map. * @@ -117,6 +128,56 @@ class CORE_EXPORT QgsLayoutItemMapItem : public QgsLayoutObject */ virtual bool usesAdvancedEffects() const; + /** + * Returns the item's stacking position, which specifies where the in the map's + * stack the item should be rendered. + * + * \see setStackingPosition() + * \see stackingLayer() + * + * \since QGIS 3.6 + */ + StackingPosition stackingPosition() const { return mStackingPosition; } + + /** + * Sets the item's stacking \a position, which specifies where the in the map's + * stack the item should be rendered. + * + * \see stackingPosition() + * \see setStackingLayer() + * + * \since QGIS 3.6 + */ + void setStackingPosition( StackingPosition position ) { mStackingPosition = position; } + + /** + * Returns the item's stacking layer, which specifies where the in the map's + * stack the item should be rendered. + * + * This setting is only used when stackingPosition() is StackBelowMapLayer or + * StackAboveMapLayer. + * + * \see setStackingLayer() + * \see stackingPosition() + * + * \since QGIS 3.6 + */ + QgsMapLayer *stackingLayer() const; + + /** + * Sets the item's stacking \a layer, which specifies where the in the map's + * stack the item should be rendered. + * + * This setting is only used when stackingPosition() is StackBelowMapLayer or + * StackAboveMapLayer. + * + * \see stackingLayer() + * \see setStackingPosition + * + * \since QGIS 3.6 + */ + void setStackingLayer( QgsMapLayer *layer ); + protected: //! Friendly display name @@ -131,10 +192,12 @@ class CORE_EXPORT QgsLayoutItemMapItem : public QgsLayoutObject //! True if item is to be displayed on map bool mEnabled; + StackingPosition mStackingPosition = StackBelowMapLabels; + + QgsMapLayerRef mStackingLayer; + }; - - /** * \ingroup core * \class QgsLayoutItemMapItemStack diff --git a/tests/src/core/testqgslayout.cpp b/tests/src/core/testqgslayout.cpp index 3628f4f0e7d..cb9b6b8b67f 100644 --- a/tests/src/core/testqgslayout.cpp +++ b/tests/src/core/testqgslayout.cpp @@ -69,6 +69,7 @@ class TestQgsLayout: public QObject void mapLayersRestoredFromTemplate(); void mapLayersStyleOverrideRestoredFromTemplate(); void atlasLayerRestoredFromTemplate(); + void overviewStackingLayerRestoredFromTemplate(); private: QString mReport; @@ -1264,6 +1265,45 @@ void TestQgsLayout::atlasLayerRestoredFromTemplate() QCOMPARE( c2.atlas()->coverageLayer(), layer2 ); } +void TestQgsLayout::overviewStackingLayerRestoredFromTemplate() +{ + // load some layers + QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); + QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QgsProject p; + p.addMapLayer( layer ); + + QgsPrintLayout c( &p ); + QgsLayoutItemMap *map = new QgsLayoutItemMap( &c ); + map->attemptSetSceneRect( QRectF( 1, 1, 10, 10 ) ); + c.addLayoutItem( map ); + map->overview()->setStackingLayer( layer ); + + // save composition to template + QDomDocument doc; + doc.appendChild( c.writeXml( doc, QgsReadWriteContext() ) ); + + // new project + QgsProject p2; + QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + p2.addMapLayer( layer2 ); + + // make a new layout from template + QgsPrintLayout c2( &p2 ); + c2.loadFromTemplate( doc, QgsReadWriteContext() ); + // get legend from new composition + QList< QgsLayoutItemMap * > maps2; + c2.layoutItems( maps2 ); + QgsLayoutItemMap *map2 = maps2.at( 0 ); + QVERIFY( map2 ); + + QCOMPARE( map2->overview()->stackingLayer(), layer2 ); +} + QGSTEST_MAIN( TestQgsLayout ) #include "testqgslayout.moc" diff --git a/tests/src/python/test_qgslayoutmapoverview.py b/tests/src/python/test_qgslayoutmapoverview.py index c3eedb6a6cc..dc76ba9c19a 100644 --- a/tests/src/python/test_qgslayoutmapoverview.py +++ b/tests/src/python/test_qgslayoutmapoverview.py @@ -20,6 +20,7 @@ from qgis.PyQt.QtCore import QFileInfo, QRectF, QDir from qgis.PyQt.QtGui import QPainter from qgis.core import (QgsLayoutItemMap, + QgsLayoutItemMapItem, QgsRectangle, QgsRasterLayer, QgsVectorLayer, @@ -217,6 +218,25 @@ class TestQgsLayoutMap(unittest.TestCase, LayoutItemTestCase): layer = overviewMap.overview().asMapLayer() self.assertEqual([f.geometry().asWkt(0) for f in layer.getFeatures()], ['Polygon ((-53 -128, 128 53, 309 -128, 128 -309, -53 -128),(93 -129, 101 -160, 163 -143, 155 -112, 93 -129))']) + def test_StackingPosition(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + + overviewMap = QgsLayoutItemMap(l) + overviewMap.attemptSetSceneRect(QRectF(20, 130, 70, 70)) + l.addLayoutItem(overviewMap) + overviewMap.overview().setStackingPosition(QgsLayoutItemMapItem.StackBelowMap) + self.assertEqual(overviewMap.overview().stackingPosition(), QgsLayoutItemMapItem.StackBelowMap) + overviewMap.overview().setStackingPosition(QgsLayoutItemMapItem.StackBelowMapLayer) + self.assertEqual(overviewMap.overview().stackingPosition(), QgsLayoutItemMapItem.StackBelowMapLayer) + + overviewMap.overview().setStackingLayer(self.raster_layer) + self.assertEqual(overviewMap.overview().stackingLayer(), self.raster_layer) + overviewMap.overview().setStackingLayer(self.vector_layer) + self.assertEqual(overviewMap.overview().stackingLayer(), self.vector_layer) + overviewMap.overview().setStackingLayer(None) + self.assertIsNone(overviewMap.overview().stackingLayer()) + if __name__ == '__main__': unittest.main()