Skip to content

Commit

Permalink
Port annotation feature handling to QgsAnnotation
Browse files Browse the repository at this point in the history
Not an ideal implementation (too much logic resides in the
gui QgsMapCanvasAnnotationItem class), but any approach
using current api will be dependant on some hacks.

Ideally we need a QgsVectorDataProvider method for finding
the closest feature(s) to a point(/line/polygon) within a
certain tolerance, with provider specific implementations
which offload this to the data store's spatial indices.
Then this handling could be bumped up to reside in
QgsAnnotation.
  • Loading branch information
nyalldawson committed Jan 29, 2017
1 parent 3d372e6 commit c22f5de
Show file tree
Hide file tree
Showing 14 changed files with 199 additions and 138 deletions.
3 changes: 3 additions & 0 deletions python/core/annotations/qgsannotation.sip
Expand Up @@ -48,6 +48,9 @@ class QgsAnnotation : QObject
QgsMapLayer* mapLayer() const;
void setMapLayer( QgsMapLayer* layer );

QgsFeature associatedFeature() const;
virtual void setAssociatedFeature( const QgsFeature& feature );

signals:

void appearanceChanged();
Expand Down
2 changes: 2 additions & 0 deletions python/core/annotations/qgshtmlannotation.sip
Expand Up @@ -17,6 +17,8 @@ class QgsHtmlAnnotation : QgsAnnotation
virtual void writeXml( QDomElement& elem, QDomDocument & doc ) const;
virtual void readXml( const QDomElement& itemElem, const QDomDocument& doc );

virtual void setAssociatedFeature( const QgsFeature& feature );

protected:

void renderAnnotation( QgsRenderContext& context, QSizeF size ) const;
Expand Down
16 changes: 16 additions & 0 deletions src/core/annotations/qgsannotation.h
Expand Up @@ -242,6 +242,19 @@ class CORE_EXPORT QgsAnnotation : public QObject
*/
void setMapLayer( QgsMapLayer* layer );

/**
* Returns the feature associated with the annotation, or an invalid
* feature if none has been set.
* @see setAssociatedFeature()
*/
QgsFeature associatedFeature() const { return mFeature; }

/**
* Sets the feature associated with the annotation.
* @see associatedFeature()
*/
virtual void setAssociatedFeature( const QgsFeature& feature ) { mFeature = feature; }

signals:

//! Emitted whenever the annotation's appearance changes
Expand Down Expand Up @@ -345,6 +358,9 @@ class CORE_EXPORT QgsAnnotation : public QObject
//! Associated layer (or nullptr if not attached to a layer)
QPointer<QgsMapLayer> mMapLayer;

//! Associated feature, or invalid feature if no feature associated
QgsFeature mFeature;

};

#endif // QGSANNOTATION_H
Expand Down
48 changes: 7 additions & 41 deletions src/core/annotations/qgshtmlannotation.cpp
Expand Up @@ -38,18 +38,13 @@

QgsHtmlAnnotation::QgsHtmlAnnotation( QObject* parent )
: QgsAnnotation( parent )
, mWebPage( nullptr )
, mHasAssociatedFeature( false )
, mFeatureId( -1 )
{
mWebPage = new QgsWebPage();
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
mWebPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );

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

setFeatureForMapPosition();
}

QgsHtmlAnnotation::~QgsHtmlAnnotation()
Expand All @@ -71,16 +66,10 @@ void QgsHtmlAnnotation::setSourceFile( const QString& htmlFile )
}

file.close();
setFeatureForMapPosition();
setAssociatedFeature( associatedFeature() );
emit appearanceChanged();
}
#if 0
void QgsHtmlAnnotation::setMapPosition( const QgsPoint& pos )
{
QgsAnnotationItem::setMapPosition( pos );
setFeatureForMapPosition();
}
#endif

void QgsHtmlAnnotation::renderAnnotation( QgsRenderContext& context, QSizeF size ) const
{
if ( !context.painter() )
Expand Down Expand Up @@ -108,8 +97,6 @@ QSizeF QgsHtmlAnnotation::minimumFrameSize() const
void QgsHtmlAnnotation::writeXml( QDomElement& elem, QDomDocument & doc ) const
{
QDomElement formAnnotationElem = doc.createElement( QStringLiteral( "HtmlAnnotationItem" ) );
formAnnotationElem.setAttribute( QStringLiteral( "hasFeature" ), mHasAssociatedFeature );
formAnnotationElem.setAttribute( QStringLiteral( "feature" ), mFeatureId );
formAnnotationElem.setAttribute( QStringLiteral( "htmlfile" ), sourceFile() );

_writeXml( formAnnotationElem, doc );
Expand All @@ -118,8 +105,6 @@ void QgsHtmlAnnotation::writeXml( QDomElement& elem, QDomDocument & doc ) const

void QgsHtmlAnnotation::readXml( const QDomElement& itemElem, const QDomDocument& doc )
{
mHasAssociatedFeature = itemElem.attribute( QStringLiteral( "hasFeature" ), QStringLiteral( "0" ) ).toInt();
mFeatureId = itemElem.attribute( QStringLiteral( "feature" ), QStringLiteral( "0" ) ).toInt();
mHtmlFile = itemElem.attribute( QStringLiteral( "htmlfile" ), QLatin1String( "" ) );
QDomElement annotationElem = itemElem.firstChildElement( QStringLiteral( "AnnotationItem" ) );
if ( !annotationElem.isNull() )
Expand All @@ -139,34 +124,15 @@ void QgsHtmlAnnotation::readXml( const QDomElement& itemElem, const QDomDocument
}
}

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

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

QgsFeature currentFeature;
QgsFeatureId currentFeatureId = 0;
bool featureFound = false;

while ( fit.nextFeature( currentFeature ) )
{
currentFeatureId = currentFeature.id();
featureFound = true;
break;
}

mHasAssociatedFeature = featureFound;
mFeatureId = currentFeatureId;
mFeature = currentFeature;

QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( vectorLayer ) );
context.setFeature( mFeature );
context.setFeature( feature );
newText = QgsExpression::replaceExpressionText( mHtmlSource, &context );
}
else
Expand Down
15 changes: 3 additions & 12 deletions src/core/annotations/qgshtmlannotation.h
Expand Up @@ -45,9 +45,6 @@ class CORE_EXPORT QgsHtmlAnnotation: public QgsAnnotation
~QgsHtmlAnnotation();

QSizeF minimumFrameSize() const override;
#if 0
void setMapPosition( const QgsPoint& pos ) override;
#endif

/**
* Sets the file path for the source HTML file.
Expand All @@ -64,27 +61,21 @@ 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;

void setAssociatedFeature( const QgsFeature& feature ) override;

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();

void javascript();

private:
QgsWebPage* mWebPage;
//! True if the item is related to a vector feature
bool mHasAssociatedFeature;
//! Associated feature
QgsFeatureId mFeatureId;
QgsFeature mFeature;
QgsWebPage* mWebPage = nullptr;
QString mHtmlFile;
QString mHtmlSource;

QString replaceText( QString displayText, QgsVectorLayer *layer, QgsFeature &feat );
};

#endif // QGSHTMLANNOTATION_H
84 changes: 16 additions & 68 deletions src/gui/qgsformannotation.cpp
Expand Up @@ -38,23 +38,12 @@

QgsFormAnnotation::QgsFormAnnotation( QObject* parent )
: QgsAnnotation( parent )
, mDesignerWidget( nullptr )
, mHasAssociatedFeature( false )
, mFeature( -1 )
{
setFeatureForMapPosition();
}

QgsFormAnnotation::~QgsFormAnnotation()
{
delete mDesignerWidget;
}
{}

void QgsFormAnnotation::setDesignerForm( const QString& uiFile )
{
mDesignerForm = uiFile;
delete mDesignerWidget;
mDesignerWidget = createDesignerWidget( uiFile );
mDesignerWidget.reset( createDesignerWidget( uiFile ) );
if ( mDesignerWidget )
{
mMinimumSize = mDesignerWidget->minimumSize();
Expand All @@ -81,25 +70,21 @@ QWidget* QgsFormAnnotation::createDesignerWidget( const QString& filePath )
//get feature and set attribute information
QgsAttributeEditorContext context;
QgsVectorLayer* vectorLayer = qobject_cast< QgsVectorLayer* >( mapLayer() );
if ( vectorLayer && mHasAssociatedFeature )
if ( vectorLayer && associatedFeature().isValid() )
{
QgsFeature f;
if ( vectorLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeature ).setFlags( QgsFeatureRequest::NoGeometry ) ).nextFeature( f ) )
QgsFields fields = vectorLayer->fields();
QgsAttributes attrs = associatedFeature().attributes();
for ( int i = 0; i < attrs.count(); ++i )
{
const QgsFields& fields = vectorLayer->fields();
QgsAttributes attrs = f.attributes();
for ( int i = 0; i < attrs.count(); ++i )
if ( i < fields.count() )
{
if ( i < fields.count() )
QWidget* attWidget = widget->findChild<QWidget*>( fields.at( i ).name() );
if ( attWidget )
{
QWidget* attWidget = widget->findChild<QWidget*>( fields.at( i ).name() );
if ( attWidget )
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( vectorLayer, i, attWidget, widget, context );
if ( eww )
{
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( vectorLayer, i, attWidget, widget, context );
if ( eww )
{
eww->setValue( attrs.at( i ) );
}
eww->setValue( attrs.at( i ) );
}
}
}
Expand All @@ -108,14 +93,6 @@ QWidget* QgsFormAnnotation::createDesignerWidget( const QString& filePath )
return widget;
}

#if 0
void QgsFormAnnotation::setMapPosition( const QgsPoint& pos )
{
QgsAnnotationItem::setMapPosition( pos );
setFeatureForMapPosition();
}
#endif

void QgsFormAnnotation::renderAnnotation( QgsRenderContext& context, QSizeF size ) const
{
if ( !mDesignerWidget )
Expand Down Expand Up @@ -155,17 +132,13 @@ QSizeF QgsFormAnnotation::preferredFrameSize() const
void QgsFormAnnotation::writeXml( QDomElement& elem, QDomDocument & doc ) const
{
QDomElement formAnnotationElem = doc.createElement( QStringLiteral( "FormAnnotationItem" ) );
formAnnotationElem.setAttribute( QStringLiteral( "hasFeature" ), mHasAssociatedFeature );
formAnnotationElem.setAttribute( QStringLiteral( "feature" ), mFeature );
formAnnotationElem.setAttribute( QStringLiteral( "designerForm" ), mDesignerForm );
_writeXml( formAnnotationElem, doc );
elem.appendChild( formAnnotationElem );
}

void QgsFormAnnotation::readXml( const QDomElement& itemElem, const QDomDocument& doc )
{
mHasAssociatedFeature = itemElem.attribute( QStringLiteral( "hasFeature" ), QStringLiteral( "0" ) ).toInt();
mFeature = itemElem.attribute( QStringLiteral( "feature" ), QStringLiteral( "0" ) ).toInt();
mDesignerForm = itemElem.attribute( QStringLiteral( "designerForm" ), QLatin1String( "" ) );
QDomElement annotationElem = itemElem.firstChildElement( QStringLiteral( "AnnotationItem" ) );
if ( !annotationElem.isNull() )
Expand All @@ -178,44 +151,19 @@ void QgsFormAnnotation::readXml( const QDomElement& itemElem, const QDomDocument
setMapLayer( QgsProject::instance()->mapLayer( itemElem.attribute( QStringLiteral( "vectorLayer" ) ) ) );
}

mDesignerWidget = createDesignerWidget( mDesignerForm );
mDesignerWidget.reset( createDesignerWidget( mDesignerForm ) );
if ( mDesignerWidget )
{
setFrameBackgroundColor( mDesignerWidget->palette().color( QPalette::Window ) );
}
}

void QgsFormAnnotation::setFeatureForMapPosition()
void QgsFormAnnotation::setAssociatedFeature( const QgsFeature& feature )
{
QgsVectorLayer* vectorLayer = qobject_cast< QgsVectorLayer* >( mapLayer() );
if ( !vectorLayer )
{
return;
}

double halfIdentifyWidth = 0; //QgsMapTool::searchRadiusMU( mMapCanvas );
QgsRectangle searchRect( mapPosition().x() - halfIdentifyWidth, mapPosition().y() - halfIdentifyWidth,
mapPosition().x() + halfIdentifyWidth, mapPosition().y() + halfIdentifyWidth );

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

QgsFeature currentFeature;
QgsFeatureId currentFeatureId = 0;
bool featureFound = false;

while ( fit.nextFeature( currentFeature ) )
{
currentFeatureId = currentFeature.id();
featureFound = true;
break;
}

mHasAssociatedFeature = featureFound;
mFeature = currentFeatureId;
QgsAnnotation::setAssociatedFeature( feature );

//create new embedded widget
delete mDesignerWidget;
mDesignerWidget = createDesignerWidget( mDesignerForm );
mDesignerWidget.reset( createDesignerWidget( mDesignerForm ) );
if ( mDesignerWidget )
{
setFrameBackgroundColor( mDesignerWidget->palette().color( QPalette::Window ) );
Expand Down
20 changes: 5 additions & 15 deletions src/gui/qgsformannotation.h
Expand Up @@ -20,47 +20,37 @@

#include "qgsannotation.h"
#include "qgsfeature.h"
#include <QWidget>
#include "qgis_gui.h"

class QGraphicsProxyWidget;

/** \ingroup gui
* An annotation item that embedds a designer form showing the feature attribute*/
class GUI_EXPORT QgsFormAnnotation: public QgsAnnotation
{
Q_OBJECT
public:
QgsFormAnnotation( QObject* parent = nullptr );
~QgsFormAnnotation();

QSizeF minimumFrameSize() const override;
//! Returns the optimal frame size
QSizeF preferredFrameSize() const;
#if 0
void setMapPosition( const QgsPoint& pos ) override;
#endif

void setDesignerForm( const QString& uiFile );
QString designerForm() const { return mDesignerForm; }

virtual void writeXml( QDomElement& elem, QDomDocument & doc ) const override;
virtual void readXml( const QDomElement& itemElem, const QDomDocument& doc ) override;

void setAssociatedFeature( const QgsFeature& feature ) override;

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();

private:

QWidget* mDesignerWidget;
QScopedPointer<QWidget> mDesignerWidget;
QSize mMinimumSize;
//! True if the item is related to a vector feature
bool mHasAssociatedFeature;
//! Associated feature
QgsFeatureId mFeature;
//! Path to (and including) the .ui file
QString mDesignerForm;

Expand Down

0 comments on commit c22f5de

Please sign in to comment.