Skip to content
Permalink
Browse files

Move map layer association for annotations to QgsAnnotation

Previously only some annotations had (incomplete) support for
attaching to a particular vector layer and synchronising their
visibility with the layer's visibility.

This handling has all been moved up to QgsAnnotation, so that
all annotation types can be attached to layers.

This will allow selective annotation visibility based on the
visible layers of a particular canvas, eg in multi-canvas
environments.

Additionally:
- show the attached layer in the annotation properties
dialog, and allow it to be cleared to always show the
annotation
- allow attaching annotations to non-vector layers
- add unit tests for visibility
  • Loading branch information
nyalldawson committed Jan 29, 2017
1 parent c853f4f commit aa14926ca858dadb8e2a862fef506072205e1943
@@ -45,6 +45,9 @@ class QgsAnnotation : QObject
void setMarkerSymbol( QgsMarkerSymbol* symbol /Transfer/ );
QgsMarkerSymbol* markerSymbol() const;

QgsMapLayer* mapLayer() const;
void setMapLayer( QgsMapLayer* layer );

signals:

void appearanceChanged();
@@ -5,7 +5,7 @@ class QgsHtmlAnnotation : QgsAnnotation
%End
public:

QgsHtmlAnnotation( QObject* parent /TransferThis/ = nullptr, QgsVectorLayer* vlayer = nullptr, bool hasFeature = false, int feature = 0 );
QgsHtmlAnnotation( QObject* parent /TransferThis/ = nullptr );

~QgsHtmlAnnotation();

@@ -17,8 +17,6 @@ class QgsHtmlAnnotation : QgsAnnotation
virtual void writeXml( QDomElement& elem, QDomDocument & doc ) const;
virtual void readXml( const QDomElement& itemElem, const QDomDocument& doc );

QgsVectorLayer* vectorLayer() const;

protected:

void renderAnnotation( QgsRenderContext& context, QSizeF size ) const;
@@ -31,6 +31,7 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsMapCanvasAnnotationItem* item, QWid
, mMarkerSymbol( nullptr )
{
setupUi( this );
mLayerComboBox->setAllowEmptyLayer( true );

if ( mItem && mItem->annotation() )
{
@@ -59,6 +60,8 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsMapCanvasAnnotationItem* item, QWid
mBackgroundColorButton->setNoColorString( tr( "Transparent" ) );
mBackgroundColorButton->setShowNoColor( true );

mLayerComboBox->setLayer( annotation->mapLayer() );

connect( mBackgroundColorButton, &QgsColorButton::colorChanged, this, &QgsAnnotationWidget::backgroundColorChanged );

const QgsMarkerSymbol* symbol = annotation->markerSymbol();
@@ -88,6 +91,7 @@ void QgsAnnotationWidget::apply()
annotation->setFrameColor( mFrameColorButton->color() );
annotation->setFrameBackgroundColor( mBackgroundColorButton->color() );
annotation->setMarkerSymbol( mMarkerSymbol->clone() );
annotation->setMapLayer( mLayerComboBox->currentLayer() );
}
mItem->update();
}
@@ -99,6 +103,7 @@ void QgsAnnotationWidget::blockAllSignals( bool block )
mMapMarkerButton->blockSignals( block );
mFrameWidthSpinBox->blockSignals( block );
mFrameColorButton->blockSignals( block );
mLayerComboBox->blockSignals( block );
}

void QgsAnnotationWidget::on_mMapMarkerButton_clicked()
@@ -60,7 +60,7 @@ void QgsFormAnnotationDialog::applySettingsToItem()
{
QgsFormAnnotation* annotation = static_cast< QgsFormAnnotation* >( mItem->annotation() );
annotation->setDesignerForm( mFileLineEdit->text() );
QgsVectorLayer* layer = annotation->vectorLayer();
QgsVectorLayer* layer = qobject_cast< QgsVectorLayer* >( annotation->mapLayer() );
if ( layer )
{
//set last used annotation form as default for the layer
@@ -61,12 +61,6 @@ void QgsHtmlAnnotationDialog::applySettingsToItem()
{
QgsHtmlAnnotation* annotation = static_cast< QgsHtmlAnnotation* >( mItem->annotation() );
annotation->setSourceFile( mFileLineEdit->text() );
QgsVectorLayer* layer = annotation->vectorLayer();
if ( layer )
{
//set last used annotation form as default for the layer
//layer->setAnnotationForm( mFileLineEdit->text() );
}
mItem->update();
}
}
@@ -34,17 +34,6 @@ QgsMapToolFormAnnotation::~QgsMapToolFormAnnotation()

QgsAnnotation* QgsMapToolFormAnnotation::createItem() const
{
//try to associate the current vector layer and a feature to the form item
QgsVectorLayer* currentVectorLayer = nullptr;
if ( mCanvas )
{
QgsMapLayer* mLayer = mCanvas->currentLayer();
if ( mLayer )
{
currentVectorLayer = dynamic_cast<QgsVectorLayer*>( mLayer );
}
}

return new QgsFormAnnotation( currentVectorLayer );
return new QgsFormAnnotation();
}

@@ -34,17 +34,6 @@ QgsMapToolHtmlAnnotation::~QgsMapToolHtmlAnnotation()

QgsAnnotation* QgsMapToolHtmlAnnotation::createItem() const
{
//try to associate the current vector layer and a feature to the form item
QgsVectorLayer* currentVectorLayer = nullptr;
if ( mCanvas )
{
QgsMapLayer* mLayer = mCanvas->currentLayer();
if ( mLayer )
{
currentVectorLayer = dynamic_cast<QgsVectorLayer*>( mLayer );
}
}

return new QgsHtmlAnnotation( nullptr, currentVectorLayer );
return new QgsHtmlAnnotation();
}

@@ -17,6 +17,8 @@

#include "qgsannotation.h"
#include "qgssymbollayerutils.h"
#include "qgsmaplayer.h"
#include "qgsproject.h"
#include <QPen>
#include <QPainter>

@@ -135,6 +137,12 @@ void QgsAnnotation::setMarkerSymbol( QgsMarkerSymbol* symbol )
emit appearanceChanged();
}

void QgsAnnotation::setMapLayer( QgsMapLayer* layer )
{
mMapLayer = layer;
emit mapLayerChanged();
}

void QgsAnnotation::updateBalloon()
{
//first test if the point is in the frame. In that case we don't need a balloon.
@@ -313,6 +321,10 @@ void QgsAnnotation::_writeXml( QDomElement& itemElem, QDomDocument& doc ) const
annotationElem.setAttribute( QStringLiteral( "frameBackgroundColor" ), mFrameBackgroundColor.name() );
annotationElem.setAttribute( QStringLiteral( "frameBackgroundColorAlpha" ), mFrameBackgroundColor.alpha() );
annotationElem.setAttribute( QStringLiteral( "visible" ), isVisible() );
if ( mMapLayer )
{
annotationElem.setAttribute( QStringLiteral( "mapLayer" ), mMapLayer->id() );
}
if ( mMarkerSymbol )
{
QDomElement symbolElem = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "marker symbol" ), mMarkerSymbol.data(), doc );
@@ -356,6 +368,10 @@ void QgsAnnotation::_readXml( const QDomElement& annotationElem, const QDomDocum
mOffsetFromReferencePoint.setY( annotationElem.attribute( QStringLiteral( "offsetY" ), QStringLiteral( "0" ) ).toDouble() );
mHasFixedMapPosition = annotationElem.attribute( QStringLiteral( "mapPositionFixed" ), QStringLiteral( "1" ) ).toInt();
mVisible = annotationElem.attribute( QStringLiteral( "visible" ), QStringLiteral( "1" ) ).toInt();
if ( annotationElem.hasAttribute( QStringLiteral( "mapLayer" ) ) )
{
mMapLayer = QgsProject::instance()->mapLayer( annotationElem.attribute( QStringLiteral( "mapLayer" ) ) );
}

//marker symbol
QDomElement symbolElem = annotationElem.firstChildElement( QStringLiteral( "symbol" ) );
@@ -369,5 +385,6 @@ void QgsAnnotation::_readXml( const QDomElement& annotationElem, const QDomDocum
}

updateBalloon();
emit mapLayerChanged();
}

@@ -23,6 +23,7 @@
#include "qgscoordinatereferencesystem.h"
#include "qgsrendercontext.h"
#include "qgssymbol.h"
#include "qgsmaplayer.h"

/** \ingroup core
* \class QgsAnnotation
@@ -225,6 +226,22 @@ class CORE_EXPORT QgsAnnotation : public QObject
*/
QgsMarkerSymbol* markerSymbol() const { return mMarkerSymbol.data(); }

/**
* Returns the map layer associated with the annotation. Annotations can be
* associated with a map layer if their visibility should be synchronized
* with the layer's visibility.
* @see setMapLayer()
*/
QgsMapLayer* mapLayer() const { return mMapLayer.data(); }

/**
* Sets the map layer associated with the annotation. Annotations can be
* associated with a map layer if their visibility should be synchronized
* with the layer's visibility.
* @see mapLayer()
*/
void setMapLayer( QgsMapLayer* layer );

signals:

//! Emitted whenever the annotation's appearance changes
@@ -236,6 +253,11 @@ class CORE_EXPORT QgsAnnotation : public QObject
*/
void moved();

/**
* Emitted when the map layer associated with the annotation changes.
*/
void mapLayerChanged();

protected:

virtual void renderAnnotation( QgsRenderContext& context, QSizeF size ) const = 0;
@@ -320,6 +342,9 @@ class CORE_EXPORT QgsAnnotation : public QObject
//! Second segment point for drawing the balloon connection (ccw direction)
QPointF mBalloonSegmentPoint2;

//! Associated layer (or nullptr if not attached to a layer)
QPointer<QgsMapLayer> mMapLayer;

};

#endif // QGSANNOTATION_H
@@ -36,12 +36,11 @@
#include <QWidget>


QgsHtmlAnnotation::QgsHtmlAnnotation(QObject* parent, QgsVectorLayer* vlayer, bool hasFeature, int feature )
QgsHtmlAnnotation::QgsHtmlAnnotation( QObject* parent )
: QgsAnnotation( parent )
, mWebPage( nullptr )
, mVectorLayer( vlayer )
, mHasAssociatedFeature( hasFeature )
, mFeatureId( feature )
, mHasAssociatedFeature( false )
, mFeatureId( -1 )
{
mWebPage = new QgsWebPage();
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
@@ -50,15 +49,6 @@ QgsHtmlAnnotation::QgsHtmlAnnotation(QObject* parent, QgsVectorLayer* vlayer, bo

connect( mWebPage->mainFrame(), &QWebFrame::javaScriptWindowObjectCleared, this, &QgsHtmlAnnotation::javascript );

if ( mVectorLayer )
{
connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( setFeatureForMapPosition() ) );
#if 0
QObject::connect( mMapCanvas, SIGNAL( renderComplete( QPainter* ) ), this, SLOT( setFeatureForMapPosition() ) );
QObject::connect( mMapCanvas, SIGNAL( layersChanged() ), this, SLOT( updateVisibility() ) );
#endif
}

setFeatureForMapPosition();
}

@@ -118,10 +108,6 @@ QSizeF QgsHtmlAnnotation::minimumFrameSize() const
void QgsHtmlAnnotation::writeXml( QDomElement& elem, QDomDocument & doc ) const
{
QDomElement formAnnotationElem = doc.createElement( QStringLiteral( "HtmlAnnotationItem" ) );
if ( mVectorLayer )
{
formAnnotationElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer->id() );
}
formAnnotationElem.setAttribute( QStringLiteral( "hasFeature" ), mHasAssociatedFeature );
formAnnotationElem.setAttribute( QStringLiteral( "feature" ), mFeatureId );
formAnnotationElem.setAttribute( QStringLiteral( "htmlfile" ), sourceFile() );
@@ -132,19 +118,6 @@ void QgsHtmlAnnotation::writeXml( QDomElement& elem, QDomDocument & doc ) const

void QgsHtmlAnnotation::readXml( const QDomElement& itemElem, const QDomDocument& doc )
{
mVectorLayer = nullptr;
if ( itemElem.hasAttribute( QStringLiteral( "vectorLayer" ) ) )
{
mVectorLayer = dynamic_cast<QgsVectorLayer*>( QgsProject::instance()->mapLayer( itemElem.attribute( QStringLiteral( "vectorLayer" ), QLatin1String( "" ) ) ) );
if ( mVectorLayer )
{
connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( setFeatureForMapPosition() ) );
#if 0
QObject::connect( mMapCanvas, SIGNAL( renderComplete( QPainter* ) ), this, SLOT( setFeatureForMapPosition() ) );
QObject::connect( mMapCanvas, SIGNAL( layersChanged() ), this, SLOT( updateVisibility() ) );
#endif
}
}
mHasAssociatedFeature = itemElem.attribute( QStringLiteral( "hasFeature" ), QStringLiteral( "0" ) ).toInt();
mFeatureId = itemElem.attribute( QStringLiteral( "feature" ), QStringLiteral( "0" ) ).toInt();
mHtmlFile = itemElem.attribute( QStringLiteral( "htmlfile" ), QLatin1String( "" ) );
@@ -154,23 +127,28 @@ void QgsHtmlAnnotation::readXml( const QDomElement& itemElem, const QDomDocument
_readXml( annotationElem, doc );
}

// upgrade old layer
if ( !mapLayer() && itemElem.hasAttribute( QStringLiteral( "vectorLayer" ) ) )
{
setMapLayer( QgsProject::instance()->mapLayer( itemElem.attribute( QStringLiteral( "vectorLayer" ) ) ) );
}

if ( mWebPage )
{
setSourceFile( mHtmlFile );
}
updateVisibility();
}

void QgsHtmlAnnotation::setFeatureForMapPosition()
{
QString newText;
if ( mVectorLayer )
if ( QgsVectorLayer* vectorLayer = qobject_cast< QgsVectorLayer* >( mapLayer() ) )
{
double halfIdentifyWidth = 0; // QgsMapTool::searchRadiusMU( mMapCanvas );
QgsRectangle searchRect( mapPosition().x() - halfIdentifyWidth, mapPosition().y() - halfIdentifyWidth,
mapPosition().x() + halfIdentifyWidth, mapPosition().y() + halfIdentifyWidth );

QgsFeatureIterator fit = mVectorLayer->getFeatures( QgsFeatureRequest().setFilterRect( searchRect ).setFlags( QgsFeatureRequest::NoGeometry | QgsFeatureRequest::ExactIntersect ) );
QgsFeatureIterator fit = vectorLayer->getFeatures( QgsFeatureRequest().setFilterRect( searchRect ).setFlags( QgsFeatureRequest::NoGeometry | QgsFeatureRequest::ExactIntersect ) );

QgsFeature currentFeature;
QgsFeatureId currentFeatureId = 0;
@@ -187,7 +165,7 @@ void QgsHtmlAnnotation::setFeatureForMapPosition()
mFeatureId = currentFeatureId;
mFeature = currentFeature;

QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mVectorLayer ) );
QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( vectorLayer ) );
context.setFeature( mFeature );
newText = QgsExpression::replaceExpressionText( mHtmlSource, &context );
}
@@ -199,22 +177,10 @@ void QgsHtmlAnnotation::setFeatureForMapPosition()
emit appearanceChanged();
}

void QgsHtmlAnnotation::updateVisibility()
{
bool visible = true;
#if 0
if ( mVectorLayer && mMapCanvas )
{
visible = mMapCanvas->layers().contains( mVectorLayer );
}
#endif
setVisible( visible );
}

void QgsHtmlAnnotation::javascript()
{
QWebFrame *frame = mWebPage->mainFrame();
frame->addToJavaScriptWindowObject( QStringLiteral( "layer" ), mVectorLayer );
frame->addToJavaScriptWindowObject( QStringLiteral( "layer" ), mapLayer() );
}


@@ -40,7 +40,7 @@ class CORE_EXPORT QgsHtmlAnnotation: public QgsAnnotation
/**
* Constructor for QgsHtmlAnnotation.
*/
QgsHtmlAnnotation( QObject* parent = nullptr, QgsVectorLayer* vlayer = nullptr, bool hasFeature = false, int feature = 0 );
QgsHtmlAnnotation( QObject* parent = nullptr );

~QgsHtmlAnnotation();

@@ -64,27 +64,18 @@ class CORE_EXPORT QgsHtmlAnnotation: public QgsAnnotation
virtual void writeXml( QDomElement& elem, QDomDocument & doc ) const override;
virtual void readXml( const QDomElement& itemElem, const QDomDocument& doc ) override;

/**
* Returns the vector layer associated with the annotation.
*/
QgsVectorLayer* vectorLayer() const { return mVectorLayer; }

protected:

void renderAnnotation( QgsRenderContext& context, QSizeF size ) const override;

private slots:
//! Sets a feature for the current map position and updates the dialog
void setFeatureForMapPosition();
//! Sets visibility status based on mVectorLayer visibility
void updateVisibility();

void javascript();

private:
QgsWebPage* mWebPage;
//! Associated vectorlayer (or 0 if attributes are not supposed to be replaced)
QgsVectorLayer* mVectorLayer;
//! True if the item is related to a vector feature
bool mHasAssociatedFeature;
//! Associated feature

0 comments on commit aa14926

Please sign in to comment.
You can’t perform that action at this time.