Skip to content
Permalink
Browse files

Port annotation feature handling to QgsAnnotation

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 c22f5de69010e746ec01d2c02c02dccb475bc1f5
@@ -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();
@@ -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;
@@ -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
@@ -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
@@ -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()
@@ -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() )
@@ -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 );
@@ -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() )
@@ -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
@@ -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.
@@ -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
@@ -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();
@@ -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 ) );
}
}
}
@@ -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 )
@@ -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() )
@@ -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 ) );
@@ -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;

0 comments on commit c22f5de

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