Skip to content

Commit

Permalink
Move map layer association for annotations to QgsAnnotation
Browse files Browse the repository at this point in the history
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 aa14926
Show file tree
Hide file tree
Showing 17 changed files with 188 additions and 176 deletions.
3 changes: 3 additions & 0 deletions python/core/annotations/qgsannotation.sip
Expand Up @@ -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();
Expand Down
4 changes: 1 addition & 3 deletions python/core/annotations/qgshtmlannotation.sip
Expand Up @@ -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();

Expand All @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/app/qgsannotationwidget.cpp
Expand Up @@ -31,6 +31,7 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsMapCanvasAnnotationItem* item, QWid
, mMarkerSymbol( nullptr )
{
setupUi( this );
mLayerComboBox->setAllowEmptyLayer( true );

if ( mItem && mItem->annotation() )
{
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
}
Expand All @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsformannotationdialog.cpp
Expand Up @@ -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
Expand Down
6 changes: 0 additions & 6 deletions src/app/qgshtmlannotationdialog.cpp
Expand Up @@ -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();
}
}
Expand Down
13 changes: 1 addition & 12 deletions src/app/qgsmaptoolformannotation.cpp
Expand Up @@ -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();
}

13 changes: 1 addition & 12 deletions src/app/qgsmaptoolhtmlannotation.cpp
Expand Up @@ -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 changes: 17 additions & 0 deletions src/core/annotations/qgsannotation.cpp
Expand Up @@ -17,6 +17,8 @@

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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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" ) );
Expand All @@ -369,5 +385,6 @@ void QgsAnnotation::_readXml( const QDomElement& annotationElem, const QDomDocum
}

updateBalloon();
emit mapLayerChanged();
}

25 changes: 25 additions & 0 deletions src/core/annotations/qgsannotation.h
Expand Up @@ -23,6 +23,7 @@
#include "qgscoordinatereferencesystem.h"
#include "qgsrendercontext.h"
#include "qgssymbol.h"
#include "qgsmaplayer.h"

/** \ingroup core
* \class QgsAnnotation
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down
60 changes: 13 additions & 47 deletions src/core/annotations/qgshtmlannotation.cpp
Expand Up @@ -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 );
Expand All @@ -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();
}

Expand Down Expand Up @@ -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() );
Expand All @@ -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( "" ) );
Expand All @@ -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;
Expand All @@ -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 );
}
Expand All @@ -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() );
}


Expand Down
11 changes: 1 addition & 10 deletions src/core/annotations/qgshtmlannotation.h
Expand Up @@ -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();

Expand All @@ -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
Expand Down

0 comments on commit aa14926

Please sign in to comment.