Skip to content

Commit

Permalink
Add "mainAnnotationLayer" to QgsProject
Browse files Browse the repository at this point in the history
This returns a reference to a main/default annotation layer for use
by the project. It forms the default location to place new annotation
items which should appear above all map layers.
  • Loading branch information
nyalldawson committed Sep 1, 2020
1 parent aefd777 commit 39fbc2a
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 3 deletions.
14 changes: 14 additions & 0 deletions python/core/auto_generated/qgsproject.sip.in
Expand Up @@ -13,6 +13,7 @@




class QgsProject : QObject, QgsExpressionContextGenerator, QgsExpressionContextScopeGenerator, QgsProjectTranslator
{
%Docstring
Expand Down Expand Up @@ -1077,6 +1078,19 @@ the layer and is responsible for deleting it.
.. seealso:: :py:func:`removeMapLayer`

.. versionadded:: 3.0
%End

QgsAnnotationLayer *mainAnnotationLayer();
%Docstring
Returns the main annotation layer associated with the project.

This layer is always present in projects, and will always be rendered
above any other map layers during map render jobs.

It forms the default location to place new annotation items which
should appear above all map layers.

.. versionadded:: 3.16
%End

void removeAllMapLayers();
Expand Down
17 changes: 17 additions & 0 deletions src/core/qgsproject.cpp
Expand Up @@ -433,6 +433,8 @@ QgsProject::QgsProject( QObject *parent )
Q_NOWARN_DEPRECATED_PUSH
connect( mViewSettings, &QgsProjectViewSettings::mapScalesChanged, this, &QgsProject::mapScalesChanged );
Q_NOWARN_DEPRECATED_POP

mMainAnnotationLayer = new QgsAnnotationLayer( QObject::tr( "Annotations" ), QgsAnnotationLayer::LayerOptions( mTransformContext ) );
}


Expand Down Expand Up @@ -761,6 +763,7 @@ void QgsProject::setTransformContext( const QgsCoordinateTransformContext &conte
mTransformContext = context;
mProjectScope.reset();

mMainAnnotationLayer->setTransformContext( context );
for ( auto &layer : mLayerStore.get()->mapLayers() )
{
layer->setTransformContext( context );
Expand Down Expand Up @@ -850,6 +853,8 @@ void QgsProject::clear()

removeAllMapLayers();
mRootGroup->clear();
if ( mMainAnnotationLayer )
mMainAnnotationLayer->reset();

setDirty( false );
emit homePathChanged();
Expand Down Expand Up @@ -1543,6 +1548,9 @@ bool QgsProject::readProjectFile( const QString &filename, QgsProject::ReadFlags
mBadLayerHandler->handleBadLayers( brokenNodes );
}

mMainAnnotationLayer->readLayerXml( doc->documentElement().firstChildElement( QStringLiteral( "main-annotation-layer" ) ), context );
mMainAnnotationLayer->setTransformContext( mTransformContext );

// Resolve references to other layers
// Needs to be done here once all dependent layers are loaded
profile.switchTask( tr( "Resolving layer references" ) );
Expand Down Expand Up @@ -2210,6 +2218,10 @@ bool QgsProject::writeProjectFile( const QString &filename )
// within top level node save list of layers
const QMap<QString, QgsMapLayer *> &layers = mapLayers();

QDomElement annotationLayerNode = doc->createElement( QStringLiteral( "main-annotation-layer" ) );
mMainAnnotationLayer->writeLayerXml( annotationLayerNode, *doc, context );
qgisNode.appendChild( annotationLayerNode );

// Iterate over layers in zOrder
// Call writeXml() on each
QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
Expand Down Expand Up @@ -3378,6 +3390,11 @@ QgsMapLayer *QgsProject::takeMapLayer( QgsMapLayer *layer )
return mLayerStore->takeMapLayer( layer );
}

QgsAnnotationLayer *QgsProject::mainAnnotationLayer()
{
return mMainAnnotationLayer;
}

void QgsProject::removeAllMapLayers()
{
mProjectScope.reset();
Expand Down
17 changes: 17 additions & 0 deletions src/core/qgsproject.h
Expand Up @@ -75,6 +75,8 @@ class QgsBookmarkManager;
class QgsProjectViewSettings;
class QgsProjectDisplaySettings;
class QgsProjectTimeSettings;
class QgsAnnotationLayer;


/**
* \ingroup core
Expand Down Expand Up @@ -1154,6 +1156,19 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
*/
QgsMapLayer *takeMapLayer( QgsMapLayer *layer ) SIP_TRANSFERBACK;

/**
* Returns the main annotation layer associated with the project.
*
* This layer is always present in projects, and will always be rendered
* above any other map layers during map render jobs.
*
* It forms the default location to place new annotation items which
* should appear above all map layers.
*
* \since QGIS 3.16
*/
QgsAnnotationLayer *mainAnnotationLayer();

/**
* Removes all registered layers. If the registry has ownership
* of any layers these layers will also be deleted.
Expand Down Expand Up @@ -1927,6 +1942,8 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera

QgsLayerTreeRegistryBridge *mLayerTreeRegistryBridge = nullptr;

QgsAnnotationLayer *mMainAnnotationLayer = nullptr;

//! map of transaction group: QPair( providerKey, connString ) -> transactionGroup
QMap< QPair< QString, QString>, QgsTransactionGroup *> mTransactionGroups;

Expand Down
32 changes: 29 additions & 3 deletions tests/src/python/test_qgsannotationlayer.py
Expand Up @@ -13,7 +13,8 @@
import qgis # NOQA

from qgis.PyQt.QtCore import (QSize,
QDir)
QDir,
QTemporaryDir)
from qgis.PyQt.QtGui import (QImage,
QPainter,
QColor)
Expand All @@ -33,9 +34,8 @@
QgsAnnotationLayer,
QgsAnnotationLineItem,
QgsAnnotationMarkerItem,
QgsPointXY,
QgsLineSymbol,
QgsMarkerSymbol
QgsMarkerSymbol,
)
from qgis.PyQt.QtXml import QDomDocument

Expand Down Expand Up @@ -171,6 +171,32 @@ def testClone(self):
self.assertIsInstance(layer2.items()[linestring_item_id], QgsAnnotationLineItem)
self.assertIsInstance(layer2.items()[marker_item_id], QgsAnnotationMarkerItem)

def testProjectMainAnnotationLayer(self):
p = QgsProject()
self.assertIsNotNone(p.mainAnnotationLayer())

# add some items to project annotation layer
polygon_item_id = p.mainAnnotationLayer().addItem(QgsAnnotationPolygonItem(QgsPolygon(QgsLineString([QgsPoint(12, 13), QgsPoint(14, 13), QgsPoint(14, 15), QgsPoint(12, 13)]))))
linestring_item_id = p.mainAnnotationLayer().addItem(QgsAnnotationLineItem(QgsLineString([QgsPoint(11, 13), QgsPoint(12, 13), QgsPoint(12, 15)])))
marker_item_id = p.mainAnnotationLayer().addItem(QgsAnnotationMarkerItem(QgsPoint(12, 13)))

# save project to xml
tmpDir = QTemporaryDir()
tmpFile = "{}/project.qgs".format(tmpDir.path())
self.assertTrue(p.write(tmpFile))

# test that annotation layer is cleared with project
p.clear()
self.assertEqual(len(p.mainAnnotationLayer().items()), 0)

# check that main annotation layer is restored on project read
p2 = QgsProject()
self.assertTrue(p2.read(tmpFile))
self.assertEqual(len(p2.mainAnnotationLayer().items()), 3)
self.assertIsInstance(p2.mainAnnotationLayer().items()[polygon_item_id], QgsAnnotationPolygonItem)
self.assertIsInstance(p2.mainAnnotationLayer().items()[linestring_item_id], QgsAnnotationLineItem)
self.assertIsInstance(p2.mainAnnotationLayer().items()[marker_item_id], QgsAnnotationMarkerItem)

def testRenderLayer(self):
layer = QgsAnnotationLayer('test', QgsAnnotationLayer.LayerOptions(QgsProject.instance().transformContext()))
layer.setCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
Expand Down

0 comments on commit 39fbc2a

Please sign in to comment.