diff --git a/python/core/annotations/qgsannotation.sip b/python/core/annotations/qgsannotation.sip index 351916745de..45b1ea16d06 100644 --- a/python/core/annotations/qgsannotation.sip +++ b/python/core/annotations/qgsannotation.sip @@ -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(); diff --git a/python/core/annotations/qgshtmlannotation.sip b/python/core/annotations/qgshtmlannotation.sip index 3572de0a102..d9dd876d12f 100644 --- a/python/core/annotations/qgshtmlannotation.sip +++ b/python/core/annotations/qgshtmlannotation.sip @@ -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; diff --git a/src/core/annotations/qgsannotation.h b/src/core/annotations/qgsannotation.h index 94c74a51ffd..62a9516e28b 100644 --- a/src/core/annotations/qgsannotation.h +++ b/src/core/annotations/qgsannotation.h @@ -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 mMapLayer; + //! Associated feature, or invalid feature if no feature associated + QgsFeature mFeature; + }; #endif // QGSANNOTATION_H diff --git a/src/core/annotations/qgshtmlannotation.cpp b/src/core/annotations/qgshtmlannotation.cpp index 6fdd827a5ec..f08038959d0 100644 --- a/src/core/annotations/qgshtmlannotation.cpp +++ b/src/core/annotations/qgshtmlannotation.cpp @@ -38,9 +38,6 @@ QgsHtmlAnnotation::QgsHtmlAnnotation( QObject* parent ) : QgsAnnotation( parent ) - , mWebPage( nullptr ) - , mHasAssociatedFeature( false ) - , mFeatureId( -1 ) { mWebPage = new QgsWebPage(); mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff ); @@ -48,8 +45,6 @@ QgsHtmlAnnotation::QgsHtmlAnnotation( QObject* parent ) 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 diff --git a/src/core/annotations/qgshtmlannotation.h b/src/core/annotations/qgshtmlannotation.h index 8281b9d012e..a360374ed2d 100644 --- a/src/core/annotations/qgshtmlannotation.h +++ b/src/core/annotations/qgshtmlannotation.h @@ -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 diff --git a/src/gui/qgsformannotation.cpp b/src/gui/qgsformannotation.cpp index ac8f8e2937f..b9ed8ea5d33 100644 --- a/src/gui/qgsformannotation.cpp +++ b/src/gui/qgsformannotation.cpp @@ -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( fields.at( i ).name() ); + if ( attWidget ) { - QWidget* attWidget = widget->findChild( 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,8 +132,6 @@ 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 ); @@ -164,8 +139,6 @@ void QgsFormAnnotation::writeXml( QDomElement& elem, QDomDocument & doc ) const 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 ) ); diff --git a/src/gui/qgsformannotation.h b/src/gui/qgsformannotation.h index 08a857949ea..8fde1463382 100644 --- a/src/gui/qgsformannotation.h +++ b/src/gui/qgsformannotation.h @@ -20,10 +20,9 @@ #include "qgsannotation.h" #include "qgsfeature.h" +#include #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 @@ -31,36 +30,27 @@ 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 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; diff --git a/src/gui/qgsmapcanvasannotationitem.cpp b/src/gui/qgsmapcanvasannotationitem.cpp index 983066d5282..007faafdd3c 100644 --- a/src/gui/qgsmapcanvasannotationitem.cpp +++ b/src/gui/qgsmapcanvasannotationitem.cpp @@ -18,6 +18,10 @@ #include "qgsmapcanvasannotationitem.h" #include "qgsannotation.h" #include "qgsmapcanvas.h" +#include "qgsmaptool.h" +#include "qgsvectorlayer.h" +#include "qgsfeatureiterator.h" +#include "qgscsexception.h" #include @@ -28,11 +32,14 @@ QgsMapCanvasAnnotationItem::QgsMapCanvasAnnotationItem( QgsAnnotation* annotatio setFlag( QGraphicsItem::ItemIsSelectable, true ); connect( mAnnotation, &QgsAnnotation::appearanceChanged, this, [this] { update(); } ); connect( mAnnotation, &QgsAnnotation::moved, this, [this] { updatePosition(); } ); + connect( mAnnotation, &QgsAnnotation::moved, this, &QgsMapCanvasAnnotationItem::setFeatureForMapPosition ); + connect( mAnnotation, &QgsAnnotation::appearanceChanged, this, &QgsMapCanvasAnnotationItem::updateBoundingRect ); connect( mMapCanvas, &QgsMapCanvas::layersChanged, this, &QgsMapCanvasAnnotationItem::onCanvasLayersChanged ); connect( mAnnotation, &QgsAnnotation::mapLayerChanged, this, &QgsMapCanvasAnnotationItem::onCanvasLayersChanged ); updatePosition(); + setFeatureForMapPosition(); } QgsMapCanvasAnnotationItem::~QgsMapCanvasAnnotationItem() @@ -106,6 +113,40 @@ void QgsMapCanvasAnnotationItem::onCanvasLayersChanged() } } +void QgsMapCanvasAnnotationItem::setFeatureForMapPosition() +{ + if ( !mAnnotation || !mAnnotation->hasFixedMapPosition() ) + return; + + QgsVectorLayer* vectorLayer = qobject_cast< QgsVectorLayer* >( mAnnotation->mapLayer() ); + if ( !vectorLayer ) + return; + + double halfIdentifyWidth = QgsMapTool::searchRadiusMU( mMapCanvas ); + QgsPoint mapPosition = mAnnotation->mapPosition(); + + try + { + QgsCoordinateTransform ct( mAnnotation->mapPositionCrs(), mMapCanvas->mapSettings().destinationCrs() ); + if ( ct.isValid() ) + mapPosition = ct.transform( mapPosition ); + } + catch ( QgsCsException & ) + { + } + + QgsRectangle searchRect( mapPosition.x() - halfIdentifyWidth, mapPosition.y() - halfIdentifyWidth, + mapPosition.x() + halfIdentifyWidth, mapPosition.y() + halfIdentifyWidth ); + + searchRect = mMapCanvas->mapSettings().mapToLayerCoordinates( vectorLayer, searchRect ); + + QgsFeatureIterator fit = vectorLayer->getFeatures( QgsFeatureRequest().setFilterRect( searchRect ).setFlags( QgsFeatureRequest::ExactIntersect ).setLimit( 1 ) ); + + QgsFeature currentFeature; + ( void )fit.nextFeature( currentFeature ); + mAnnotation->setAssociatedFeature( currentFeature ); +} + void QgsMapCanvasAnnotationItem::drawSelectionBoxes( QPainter* p ) const { if ( !p ) diff --git a/src/gui/qgsmapcanvasannotationitem.h b/src/gui/qgsmapcanvasannotationitem.h index 6986a83f7f7..8a6e5e9c0ad 100644 --- a/src/gui/qgsmapcanvasannotationitem.h +++ b/src/gui/qgsmapcanvasannotationitem.h @@ -90,6 +90,9 @@ class GUI_EXPORT QgsMapCanvasAnnotationItem: public QObject, public QgsMapCanvas void onCanvasLayersChanged(); + //! Sets a feature for the current map position + void setFeatureForMapPosition(); + private: //! Draws selection handles around the item diff --git a/tests/src/python/test_qgsannotation.py b/tests/src/python/test_qgsannotation.py index 5d37653d00a..13163221f6a 100644 --- a/tests/src/python/test_qgsannotation.py +++ b/tests/src/python/test_qgsannotation.py @@ -22,7 +22,9 @@ from qgis.core import (QgsTextAnnotation, QgsCoordinateReferenceSystem, QgsRectangle, QgsRenderChecker, - QgsPoint) + QgsPoint, + QgsVectorLayer, + QgsFeature) from qgis.PyQt.QtCore import (QDir, QPointF, QSizeF) @@ -79,6 +81,26 @@ class TestQgsAnnotation(unittest.TestCase): im = self.renderAnnotation(a, QPointF(20, 30)) self.assertTrue(self.imageCheck('html_annotation', 'html_annotation', im)) + def testHtmlAnnotationWithFeature(self): + """ test rendering a html annotation with a feature""" + layer = QgsVectorLayer("Point?crs=EPSG:3111&field=station:string&field=suburb:string", + 'test', "memory") + + a = QgsHtmlAnnotation() + a.setFrameSize(QSizeF(400, 250)) + a.setFrameOffsetFromReferencePoint(QPointF(70, 90)) + a.setMapLayer(layer) + html = TEST_DATA_DIR + "/test_html_feature.html" + a.setSourceFile(html) + im = self.renderAnnotation(a, QPointF(20, 30)) + self.assertTrue(self.imageCheck('html_nofeature', 'html_nofeature', im)) + f = QgsFeature(layer.fields()) + f.setValid(True) + f.setAttributes(['hurstbridge', 'somewhere']) + a.setAssociatedFeature(f) + im = self.renderAnnotation(a, QPointF(20, 30)) + self.assertTrue(self.imageCheck('html_feature', 'html_feature', im)) + def testRelativePosition(self): """ test rendering an annotation without map point""" a = QgsHtmlAnnotation() diff --git a/tests/src/python/test_qgsmapcanvasannotationitem.py b/tests/src/python/test_qgsmapcanvasannotationitem.py index eb000f31c75..c1d07fae6cf 100644 --- a/tests/src/python/test_qgsmapcanvasannotationitem.py +++ b/tests/src/python/test_qgsmapcanvasannotationitem.py @@ -19,7 +19,9 @@ from qgis.core import (QgsTextAnnotation, QgsCoordinateReferenceSystem, QgsRectangle, QgsPoint, - QgsVectorLayer) + QgsVectorLayer, + QgsFeature, + QgsGeometry) from qgis.gui import (QgsMapCanvas, QgsMapCanvasAnnotationItem) @@ -121,6 +123,42 @@ class TestQgsMapCanvasAnnotationItem(unittest.TestCase): canvas.setLayers([layer]) self.assertTrue(i.isVisible()) + def testSettingFeature(self): + """ test that feature is set when item moves """ + a = QgsTextAnnotation() + a.setFrameSize(QSizeF(300, 200)) + a.setFrameOffsetFromReferencePoint(QPointF(40, 50)) + a.setHasFixedMapPosition(True) + a.setMapPosition(QgsPoint(12, 34)) + a.setMapPositionCrs(QgsCoordinateReferenceSystem(4326)) + + canvas = QgsMapCanvas() + canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) + canvas.setFrameStyle(0) + canvas.resize(600, 400) + + canvas.setExtent(QgsRectangle(10, 30, 20, 35)) + + i = QgsMapCanvasAnnotationItem(a, canvas) + + layer = QgsVectorLayer("Point?crs=EPSG:4326&field=station:string&field=suburb:string", + 'test', "memory") + canvas.setLayers([layer]) + f = QgsFeature(layer.fields()) + f.setGeometry(QgsGeometry.fromPoint(QgsPoint(14, 31))) + f.setValid(True) + f.setAttributes(['hurstbridge', 'somewhere']) + self.assertTrue(layer.dataProvider().addFeatures([f])) + a.setMapLayer(layer) + self.assertFalse(a.associatedFeature().isValid()) + + a.setMapPosition(QgsPoint(14, 31)) + self.assertTrue(a.associatedFeature().isValid()) + self.assertEqual(a.associatedFeature().attributes()[0], 'hurstbridge') + + a.setMapPosition(QgsPoint(17, 31)) + self.assertFalse(a.associatedFeature().isValid()) + if __name__ == '__main__': unittest.main() diff --git a/tests/testdata/control_images/annotations/expected_html_feature/expected_html_feature.png b/tests/testdata/control_images/annotations/expected_html_feature/expected_html_feature.png new file mode 100644 index 00000000000..f10be84e5d3 Binary files /dev/null and b/tests/testdata/control_images/annotations/expected_html_feature/expected_html_feature.png differ diff --git a/tests/testdata/control_images/annotations/expected_html_nofeature/expected_html_nofeature.png b/tests/testdata/control_images/annotations/expected_html_nofeature/expected_html_nofeature.png new file mode 100644 index 00000000000..2b6f5bd69d3 Binary files /dev/null and b/tests/testdata/control_images/annotations/expected_html_nofeature/expected_html_nofeature.png differ diff --git a/tests/testdata/test_html_feature.html b/tests/testdata/test_html_feature.html new file mode 100644 index 00000000000..f88d74edd47 --- /dev/null +++ b/tests/testdata/test_html_feature.html @@ -0,0 +1,41 @@ + + + + + + + + + +
[% "station" %][% "suburb" %]
+ +